Skip to content

feat(workbench): adopt streamlit-style codespace onboarding (single-tab Simple Browser)#158

Merged
fede-kamel merged 1 commit into
mainfrom
feat/workbench-best-practice-onboarding
May 12, 2026
Merged

feat(workbench): adopt streamlit-style codespace onboarding (single-tab Simple Browser)#158
fede-kamel merged 1 commit into
mainfrom
feat/workbench-best-practice-onboarding

Conversation

@fede-kamel
Copy link
Copy Markdown
Contributor

Summary

Aligns the workbench's Codespaces setup with the convergent best-practice recipe used by streamlit/streamlit-example and documented in GitHub's "Customizable Initial Experience" changelog (Oct 2022). Single-window UX; no popup blocker; no permission prompt; no second browser tab.

Five changes

  1. portsAttributes.5173.onAutoForward: openBrowserOnceopenPreview. Workbench renders inside VS Code's Simple Browser pane instead of opening a separate browser tab. Identical to streamlit-example.
  2. customizations.codespaces.openFiles: [".devcontainer/welcome.md"] added. Native Codespaces mechanism replaces the hand-rolled code .devcontainer/welcome.md call from the banner script.
  3. postAttachCommand: string form → named object form {"workbench": "..."}. Terminal panel gets the human label "workbench" instead of an opaque shell name.
  4. postCreateCommandupdateContentCommand. Deps refresh automatically when the user pulls latest main inside the codespace. Streamlit's pattern.
  5. Base image pinned: python:3.12-bookwormpython:1-3.12-bookworm. Pins the devcontainer-image major version (matches microsoft/vscode-remote-try-* convention).

Simplifications

  • workbench-banner.sh no longer calls code for files or URLs — both flows are now native devcontainer features. The script is narrowly observability-focused: print URL, tail tier logs.
  • postStart.sh no longer manually invokes Simple Browser via code --open-url — that's openPreview's job now. Also drops the broken http://127.0.0.1:5173/ URL (wrong host inside VS Code Web).

Net user experience

Click Launch workbenchone browser tab opens (VS Code Web). Inside:

  • welcome.md as an editor tab
  • Simple Browser editor tab with the workbench rendered inline
  • Terminal labeled "workbench" with the 🚀 URL banner + tier logs

Single window. No second tab to hunt for, no popup blocker, no permission prompt.

Verification plan

  • Delete an old codespace; create a fresh one on this branch
  • Expected: only one Firefox tab opens (VS Code Web)
  • Inside VS Code: welcome.md tab visible; Simple Browser tab with workbench loaded; terminal labeled "workbench" with banner output
  • Provider settings → paste Anthropic key → run tutorial_01 → completes cleanly

Related

References

…ab Simple Browser)

After researching how high-quality public repos (streamlit/streamlit-example,
microsoft/vscode-remote-try-*, n8n, vercel/next.js community templates)
handle Codespaces buttons that should land users on a running web app,
the dominant pattern is well-established and documented in GitHub's
"Customizable Initial Experience" changelog. Our existing #155/#157
setup did the right thing but reached for non-idiomatic primitives.
This PR aligns with the convergent best-practice recipe.

## What changes vs main

1. `portsAttributes."5173".onAutoForward`: `openBrowserOnce` →
   `openPreview`. The forwarded port now opens in VS Code's Simple
   Browser editor pane (single-tab UX, no separate browser tab, no
   popup blocker) instead of as a separate external tab. Identical
   to streamlit-example's port config.

2. `customizations.codespaces.openFiles: [".devcontainer/welcome.md"]`
   added. Codespaces natively opens these files as editor tabs on
   first launch — replaces the hand-rolled `code .devcontainer/welcome.md`
   call that the banner script was doing.

3. `postAttachCommand`: string form → named object form
   `{"workbench": "bash .devcontainer/workbench-banner.sh"}`. The
   terminal panel that hosts the command now gets the human label
   "workbench" instead of an opaque shell tab — matches streamlit's
   `{"server": "..."}` shape.

4. `postCreateCommand` → `updateContentCommand`. `postCreateCommand`
   fires once per codespace lifetime (on initial build).
   `updateContentCommand` fires whenever Codespaces detects content
   updates (e.g., the user pulls latest main), so deps refresh
   automatically after a git pull. Streamlit's recommended pattern.

5. `image`: `mcr.microsoft.com/devcontainers/python:3.12-bookworm` →
   `:1-3.12-bookworm`. Pins the major version of the devcontainer
   image so future devcontainer-image releases can't unexpectedly
   break the workbench. Standard practice across the
   microsoft/vscode-remote-try-* references.

## What's simplified

- `workbench-banner.sh` no longer calls `code .devcontainer/welcome.md`
  (handled by `openFiles`) or `code --open-external <url>` (handled
  by `openPreview`). The script is now narrowly focused: print the
  URL banner, tail the three tier logs.

- `postStart.sh` no longer calls
  `code --open-url "vscode://vscode.simpleBrowser.api.open/http://127.0.0.1:5173/"` — that was a workaround for the
  popup-blocker problem and it used the wrong (localhost) URL anyway.
  `openPreview` handles the Simple Browser open natively with the
  correct forwarded URL.

## Net user experience after this lands

Click Launch workbench → **one** Firefox tab opens (VS Code Web). Inside:

- welcome.md as an editor tab (via `openFiles`)
- Simple Browser editor tab with the workbench rendered inline
  (via `openPreview`)
- Terminal panel labeled "workbench" showing the 🚀 banner +
  tier logs

Single window. No popup blocker. No permission prompt. Same recipe
streamlit + the official GitHub Codespaces docs recommend.

## Refs

- streamlit/streamlit-example .devcontainer/devcontainer.json — the
  canonical exemplar of this recipe.
- GitHub Codespaces "Customizable Initial Experience" changelog
  (2022-10-26) — documents `openFiles` + `postAttachCommand` +
  `onAutoForward: openPreview` as the intended three-key pattern.
- microsoft/vscode-remote-try-* — base image version pinning
  precedent (`1-3.12-bookworm` etc.).

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label May 12, 2026
@fede-kamel fede-kamel merged commit e1098dc into main May 12, 2026
10 checks passed
@fede-kamel fede-kamel deleted the feat/workbench-best-practice-onboarding branch May 12, 2026 07:16
fede-kamel added a commit that referenced this pull request May 12, 2026
…ew alone (#160)

PR #158 dropped the active `code --open-url` call from the banner
script, betting that `portsAttributes.5173.onAutoForward: "openPreview"`
in devcontainer.json would handle the workbench-tab open natively.
That bet only pays off **the first time** the port binds — `openPreview`
is a one-shot trigger that silently no-ops on every re-attach, every
manual `bash .devcontainer/workbench-banner.sh`, every postStart
restart. Users in those scenarios get a "Vite is ready, here's the
URL" banner with no actual tab opening.

Confirmed in a live codespace: `bash .devcontainer/workbench-banner.sh`
prints the banner but no Simple Browser tab appears.

## Fix

Add the active call back to the banner:

    code --open-url "vscode://vscode.simpleBrowser.api.open/${URL}"

This deterministically opens the workbench in VS Code's Simple Browser
editor pane every time the banner script runs — on postAttach, on
manual re-run, on tier-crash recovery, etc. Same single-tab
streamlit-pattern UX, but programmatically triggered instead of
relying on the one-shot port-forward event.

Layered defences (any one is sufficient; all four together cover
every failure mode):

1. `customizations.codespaces.openFiles` opens welcome.md as an
   editor tab (always).
2. `portsAttributes.5173.onAutoForward: openPreview` opens Simple
   Browser on first port-bind (usually).
3. `code --open-url` in this script opens Simple Browser
   deterministically on every attach (now, after this PR).
4. The printed banner URL is a ⌘-clickable manual fallback.

`code --open-url` is a no-op outside VS Code, so local devcontainer
and Docker runs are unaffected — they continue to see only the
printed URL.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
fede-kamel added a commit that referenced this pull request May 12, 2026
…pen fix) (#161)

After four iterations (#155, #157, #158, #160), the Codespaces Simple
Browser auto-open was still unreliable. The user's symptom: open a
fresh codespace, VS Code Web boots, tiers come up, banner prints —
but no Simple Browser tab pops with the workbench. Manual click on
the URL is still required.

## Root cause

`portsAttributes.5173.onAutoForward: "openPreview"` only fires for
the user when a VS Code client is **attached at the moment the port
binds**. In our setup, `postStartCommand` boots all three tiers
*before* the user's VS Code Web has finished attaching to the
container. Vite binds on :5173 → no client attached → openPreview
silently no-ops. By the time the user shows up, the port-forward
event is already past and Simple Browser never opens.

This is the timing trap that streamlit-example sidesteps by putting
the server itself in `postAttachCommand`, not in `postStartCommand`.
With the server in postAttach, the port binds **after** the user is
attached, so openPreview's event handler receives the bind and pops
Simple Browser.

The earlier PR #160 also tried to fix this with an active
`code --open-url "vscode://vscode.simpleBrowser.api.open/<url>"`
call, but that URI format isn't a valid VS Code Command URI —
should be `vscode://command/simpleBrowser.show?<JSON-encoded args>`.

## Fix

1. **Move tier startup from `postStartCommand` into `postAttachCommand`.**
   Server boots after the user attaches; ports bind with a client
   present; openPreview reliably fires.
2. **Make tier startup idempotent.** `postAttachCommand` fires on
   every attach (not just the first), so the script checks whether
   each port is already listening before launching its tier. Second
   attach is a no-op for the tiers.
3. **Use the correct Command URI** for the active Simple Browser open:
   `vscode://command/simpleBrowser.show?<URL-encoded JSON array>`.
   Belt-and-braces; works even if openPreview's port-bind event
   misfires for any reason.

## Implementation

- New `.devcontainer/workbench-attach.sh` — single entry point that
  does idempotent tier-boot + Vite-wait + Simple-Browser-open +
  banner + log-tail. Replaces both `postStart.sh` and
  `workbench-banner.sh` (deleted).
- `devcontainer.json` — `postStartCommand` removed; `postAttachCommand`
  now points to the new script.

## Why postStartCommand is gone entirely

There's nothing for it to do anymore. Container build runs `updateContentCommand` (deps install). User attaches → `postAttachCommand` runs. No work fits between those two events that wouldn't be either too-early (port-bind race) or never-needed.

## Reference

- streamlit/streamlit-example .devcontainer/devcontainer.json — the
  canonical pattern with server-in-postAttach.
- VS Code Command URI docs — correct `vscode://command/<id>?<args>` shape.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant