Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add side-by-side chat sessions (Amicode: New Chat) - #238

Merged
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats
Aug 1, 2026
Merged

Add side-by-side chat sessions (Amicode: New Chat)#238
aarontrowbridge merged 4 commits into
mainfrom
feat/side-by-side-chats

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

What

New command Amicode: New Chat (Side by Side) (amicode.newChat): opens an additional chat editor tab at ViewColumn.Beside, pinned to the app's /new-session draft route. Each tab owns its own conversation; all tabs share the one opencode server. Tabs split across editor groups = true side-by-side sessions in one window.

How

  • chat_panel.ts: singleton → multi-instance registry. openOrReveal keeps primary semantics (auto-open, Open Chat untouched); new openNew always spawns a tab. Tab titles: Amicode Chat, Amicode Chat N (lowest free, reused on dispose; existing tabs never retitled).
  • extension.ts: command registration beside openChat with the same ready-server snapshot + credential gates.
  • package.json: command contribution ($(add) icon) + activation event.
  • No fork change/new-session exists in the pinned binary (v1.17.3-amicode.12).

Tests

5 new cases in test/chat_panel_sessions.test.ts: panel coexistence, openOrReveal reveal-isolation, title numbering/reuse, Beside column, auth-token carriage on the draft URL. vscode mock gains ViewColumn.Beside.

Gates

typecheck clean · fast suite 819/819 · esbuild bundle builds · boot smoke PASS.

Follow-ups (not in this PR)

  • Webview panel serializer so extra tabs survive window reload (sessions persist server-side regardless).
  • Fork-side postMessage bridge to mirror the session title into the tab label.
  • Test-isolation nits surfaced while running gates from inside a live Amicode shell: buildServerSpawnEnv reads process.env.AMICO_PYTHON; opencode_probe.mjs inherits OPENCODE_SERVER_PASSWORD (boot smoke 401s). Both unrelated to this change.

ChatPanel goes from singleton to multi-instance registry: openOrReveal keeps
primary semantics (auto-open, Open Chat), and a new openNew spawns an
additional editor tab at ViewColumn.Beside pinned to the app's /new-session
draft route — each tab owns its conversation over the one opencode server.
Tabs title Amicode Chat / Amicode Chat N (lowest free, reused on close).
No fork change needed: /new-session exists in the pinned binary
(v1.17.3-amicode.12). 5 new tests cover coexistence, reveal isolation,
title numbering, Beside column, and auth-token carriage.
- buildChildEnv: denylist OPENCODE / OPENCODE_PID / OPENCODE_SERVER_PASSWORD —
they ride the OPENCODE_ prefix allowance into the child, whose headless run
then resolves the PARENT's session ("Session not found") and dies before the
critic starts. Config vars (OPENCODE_CONFIG_CONTENT/DIR) stay.
- CRITIC_TIMEOUT_MS 120s -> 240s: a frontier review of a full spec measures
~3 min on a free-tier model (178s, 2026-07-31); the 120s ceiling read slow
legitimate answers as failures and silently degraded every review to
approved-mechanical.
- Planner gets its own ceiling (PLANNER_TIMEOUT_MS = 480s): a full gated plan
JSON is a bigger output than a critic pass and measured beyond 240s.
@aarontrowbridge
aarontrowbridge merged commit 68009de into mainAug 1, 2026
6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@aarontrowbridge