diff --git a/BROWSER.md b/BROWSER.md index 46dbc23d..baf32d5f 100644 --- a/BROWSER.md +++ b/BROWSER.md @@ -164,54 +164,43 @@ The window has a subtle green shimmer line at the top edge and a floating "gstac ### Chrome extension (Side Panel) -A Chrome extension that shows a live activity feed of browse commands in a Side Panel, plus @ref overlays on the page. Works in any Chrome-based browser (Chrome, Comet, Edge). +A Chrome extension that shows a live activity feed of browse commands in a Side Panel, plus @ref overlays on the page. -#### Step-by-step install +#### Automatic install (recommended) -**1. Open the extensions page** +When you run `$B connect`, the extension **auto-loads** into the Playwright-controlled Chrome window. No manual steps needed — the Side Panel is immediately available. -Type `chrome://extensions` in Chrome's address bar and press Enter. - -**2. Enable Developer mode** - -In the top-right corner of the extensions page, toggle the **Developer mode** switch ON. You'll see new buttons appear: "Load unpacked", "Pack extension", and "Update". - -**3. Load the extension** - -Click **Load unpacked**. A file picker dialog opens. - -A file picker opens. You need to navigate to the `extension/` folder inside gstack, but macOS hides folders starting with `.` by default. The easiest way: press **Cmd+Shift+G** in the file picker to open "Go to folder", then paste one of these paths: - -- Global install: `~/.claude/skills/gstack/extension` -- Project install: `/.claude/skills/gstack/extension` -- Dev/source: `/extension` - -Press Enter, then click **Select** (select the `extension/` folder itself, not a file inside it). - -Alternatively, press **Cmd+Shift+.** (period) in the file picker to reveal hidden files, then navigate to `.claude/skills/gstack/extension` manually. - -**4. Pin the extension** - -Click the puzzle piece icon (Extensions) in Chrome's toolbar. Find "gstack browse" and click the pin icon so it's always visible. - -**5. Configure the port** - -Click the gstack icon in the toolbar. A popup appears with a port input field. - -Find your browse server port — run `$B status` or check `.gstack/browse.json` in your project root: ```bash -cat .gstack/browse.json | grep port -# or -$B status # shows the port in the output +$B connect # launches Chrome with extension pre-loaded +# Click the gstack icon in toolbar → Open Side Panel ``` -Enter the port number and press Enter. The popup saves it and starts polling. +The port is auto-configured. You're done. -**6. Open the Side Panel** +#### Manual install (for your regular Chrome) -Click the gstack icon again, then click **Open Side Panel**. The Side Panel slides open on the right side of Chrome showing a live activity feed. +If you want the extension in your everyday Chrome (not the Playwright-controlled one), run: -Alternatively, right-click the gstack icon and choose "Open side panel." +```bash +bin/gstack-extension # opens chrome://extensions, copies path to clipboard +``` + +Or do it manually: + +1. **Go to `chrome://extensions`** in Chrome's address bar +2. **Toggle "Developer mode" ON** (top-right corner) +3. **Click "Load unpacked"** — a file picker opens +4. **Navigate to the extension folder:** Press **Cmd+Shift+G** in the file picker to open "Go to folder", then paste one of these paths: + - Global install: `~/.claude/skills/gstack/extension` + - Dev/source: `/extension` + + Press Enter, then click **Select**. + + (Tip: macOS hides folders starting with `.` — press **Cmd+Shift+.** in the file picker to reveal them if you prefer to navigate manually.) + +5. **Pin it:** Click the puzzle piece icon (Extensions) in the toolbar → pin "gstack browse" +6. **Set the port:** Click the gstack icon → enter the port from `$B status` or `.gstack/browse.json` +7. **Open Side Panel:** Click the gstack icon → "Open Side Panel" #### What you get diff --git a/bin/gstack-extension b/bin/gstack-extension new file mode 100755 index 00000000..8d0a62af --- /dev/null +++ b/bin/gstack-extension @@ -0,0 +1,65 @@ +#!/bin/bash +# gstack-extension — helper to install the Chrome extension +# +# When using $B connect, the extension auto-loads. This script is for +# installing it in your regular Chrome (not the Playwright-controlled one). + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +# Find the extension directory +EXT_DIR="" +if [ -f "$REPO_ROOT/extension/manifest.json" ]; then + EXT_DIR="$REPO_ROOT/extension" +elif [ -f "$HOME/.claude/skills/gstack/extension/manifest.json" ]; then + EXT_DIR="$HOME/.claude/skills/gstack/extension" +fi + +if [ -z "$EXT_DIR" ]; then + echo "Error: extension/ directory not found." + echo "Expected at: $REPO_ROOT/extension/ or ~/.claude/skills/gstack/extension/" + exit 1 +fi + +# Copy path to clipboard +echo -n "$EXT_DIR" | pbcopy 2>/dev/null + +# Get browse server port +PORT="" +STATE_FILE="$REPO_ROOT/.gstack/browse.json" +if [ -f "$STATE_FILE" ]; then + PORT=$(grep -o '"port":[0-9]*' "$STATE_FILE" | grep -o '[0-9]*') +fi + +echo "gstack Chrome Extension Setup" +echo "==============================" +echo "" +echo "Extension path (copied to clipboard):" +echo " $EXT_DIR" +echo "" + +if [ -n "$PORT" ]; then + echo "Browse server port: $PORT" + echo "" +fi + +echo "Quick install (if using \$B connect):" +echo " The extension auto-loads when you run \$B connect." +echo " No manual installation needed!" +echo "" +echo "Manual install (for your regular Chrome):" +echo "" +echo " 1. Opening chrome://extensions now..." + +# Open chrome://extensions +osascript -e 'tell application "Google Chrome" to open location "chrome://extensions"' 2>/dev/null || \ + open "chrome://extensions" 2>/dev/null || \ + echo " Could not open Chrome. Navigate to chrome://extensions manually." + +echo " 2. Toggle 'Developer mode' ON (top-right)" +echo " 3. Click 'Load unpacked'" +echo " 4. In the file picker: Cmd+Shift+G → paste (path is in your clipboard) → Enter → Select" +echo " 5. Click the gstack puzzle icon in toolbar → enter port: ${PORT:-}" +echo " 6. Click 'Open Side Panel'" diff --git a/browse/src/browser-manager.ts b/browse/src/browser-manager.ts index 4a639091..5512888a 100644 --- a/browse/src/browser-manager.ts +++ b/browse/src/browser-manager.ts @@ -70,6 +70,39 @@ export class BrowserManager { getConnectionMode(): 'launched' | 'cdp' { return this.connectionMode; } + /** + * Find the gstack Chrome extension directory. + * Checks: repo root /extension, global install, dev install. + */ + private findExtensionPath(): string | null { + const fs = require('fs'); + const path = require('path'); + const candidates = [ + // Relative to this source file (dev mode: browse/src/ -> ../../extension) + path.resolve(__dirname, '..', '..', 'extension'), + // Global gstack install + path.join(process.env.HOME || '', '.claude', 'skills', 'gstack', 'extension'), + // Git repo root (detected via BROWSE_STATE_FILE location) + (() => { + const stateFile = process.env.BROWSE_STATE_FILE || ''; + if (stateFile) { + const repoRoot = path.resolve(path.dirname(stateFile), '..'); + return path.join(repoRoot, '.claude', 'skills', 'gstack', 'extension'); + } + return ''; + })(), + ].filter(Boolean); + + for (const candidate of candidates) { + try { + if (fs.existsSync(path.join(candidate, 'manifest.json'))) { + return candidate; + } + } catch {} + } + return null; + } + /** * Get the ref map for external consumers (e.g., /refs endpoint). */ @@ -125,12 +158,20 @@ export class BrowserManager { this.refMap.clear(); this.nextTabId = 1; + // Find the gstack extension directory for auto-loading + const extensionPath = this.findExtensionPath(); + const launchArgs = ['--restore-last-session']; + if (extensionPath) { + launchArgs.push(`--disable-extensions-except=${extensionPath}`); + launchArgs.push(`--load-extension=${extensionPath}`); + } + // Launch real Chrome via Playwright's channel protocol // This uses the system Chrome binary, headed, with real window this.browser = await chromium.launch({ channel: 'chrome', headless: false, - args: ['--restore-last-session'], + args: launchArgs, }); this.connectionMode = 'cdp'; this.intentionalDisconnect = false;