feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev
, '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

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId - #2

Merged
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route
Apr 9, 2026
Merged

feat(web): add embeddable thread route /embed/thread/:environmentId/:threadId#2
rororowyourboat merged 26 commits into
mainfrom
feat/embed-thread-route

Conversation

@rororowyourboat

Copy link
Copy Markdown
Owner

Summary

Adds a standalone route that renders the existing `ChatView` without the app sidebar chrome, for use inside iframes. The primary consumer is t3-canvas (a canvas-first agent workbench) which embeds T3 Code threads as tiles on an infinite canvas.

Closes#1

Changes

  • New route: `apps/web/src/routes/embed.thread.$environmentId.$threadId.tsx`
    • URL: `/embed/thread/:environmentId/:threadId?minimal=1`
    • Renders `` with the existing bootstrap checks (environment connected, thread exists)
    • Shows "Loading thread…" while the environment bootstraps
    • Shows a clear error state for invalid thread refs or missing threads
    • Wraps in a `position: fixed; inset: 0` container with `data-t3-embed` attributes for future targeted CSS
  • Root route update: `apps/web/src/routes/__root.tsx`
    • Any pathname starting with `/embed/` now renders `` directly instead of wrapping in ``
    • The environment connection, websocket surface, toasts, and event router still initialize — only the sidebar/diff/plan chrome is skipped
    • Auth gate still enforced (unauthenticated users see the outlet per existing behavior)
  • `routeTree.gen.ts` regenerated by `@tanstack/router-plugin` (via `bun run dev` tick)

Follow-ups not in this PR

The `minimal=1` query param is parsed and exposed via `data-t3-embed-minimal="true"` on the container, but the actual chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) inside `ChatView` is not yet implemented. These components render by default. Two reasonable paths, pick later:

  1. Add a `minimal?: boolean` prop to `ChatView` that conditionally skips those sections
  2. Scoped CSS on `[data-t3-embed-minimal="true"]` hiding `[data-branch-toolbar]` etc. (requires adding stable data attributes to those components)

For the MVP of t3-canvas agent tiles, showing the full ChatView inside an iframe is acceptable — the iframe is a large tile so the chrome is visible but not blocking.

Test plan

  • `bun fmt` ✅ passes
  • `bun lint` ✅ passes (0 warnings, 0 errors)
  • `bun typecheck` ✅ passes (all 8 packages, ~37s)
  • Manual: `bun run dev`, create a thread in the main UI, navigate to `http://localhost:/embed/thread//?minimal=1`, verify the thread renders without the sidebar and messages stream normally
  • Manual: embed the URL inside a plain `<iframe>` on another local-origin page, verify same-origin iframe interaction still works (clicks, form input, scrolling)
  • Manual: hit `/embed/thread/bogus/bogus` → verify graceful error state

Out of scope

  • Chrome hiding for minimal mode (see follow-ups above)
  • Cross-origin iframe hardening (same-origin only for MVP)
  • Auth handling customization (unauthenticated users get whatever the root route serves)
  • Multi-thread views or embed customization beyond `minimal`

juliusmarmingeand others added 26 commits April 5, 2026 16:53
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
…#1303)
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@macmini.local>
Adds /embed/thread/:environmentId/:threadId — a standalone route that
renders the existing ChatView without the app sidebar chrome. This is the
iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3).
- New file-based route embed.thread.\$environmentId.\$threadId.tsx
- __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the
environment connection + websocket surface + toasts still initialize
but the sidebar/diff/plan chrome does not render
- minimal=1 search param is parsed and wired to a data attribute on the
container for future targeted CSS; chrome hiding (BranchToolbar,
PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass
- routeTree.gen.ts regenerated by the @tanstack/router-plugin
@github-actionsgithub-actionsBot added size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 9, 2026
@rororowyourboat
rororowyourboat merged commit 3826ab2 into mainApr 9, 2026
6 of 7 checks passed
@rororowyourboat
rororowyourboat deleted the feat/embed-thread-route branch April 9, 2026 22:30

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:892e8de502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +59
if (!bootstrapComplete) {
return <EmbedMessage label="Loading thread…" />;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unknown embed environments instead of perpetual loading

This gate returns the loading state whenever bootstrapComplete is false, but unknown environmentId values never bootstrap (the store falls back to an initial environment state with bootstrapComplete: false). As a result, /embed/thread/<bogus>/<bogus> never reaches the not-found branch and spins forever, which breaks the route’s intended graceful error behavior for invalid thread references.

Useful? React with 👍 / 👎.

Comment threadapps/web/src/store.ts
Comment on lines +1700 to +1702
const nextEnvironmentState = updateThreadState(
getStoredEnvironmentState(state, state.activeEnvironmentId),
threadId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope thread error writes to the thread environment

setError updates only state.activeEnvironmentId, but ChatView calls it with just a threadId for the currently viewed route thread. In multi-environment usage, when the viewed thread belongs to a non-active environment, this write targets the wrong environment slice (or no-ops), so transport/send errors are not persisted on the actual thread the user is looking at.

Useful? React with 👍 / 👎.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXLvouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): add embeddable thread route /embed/thread/:id for canvas tile hosts

10 participants

@rororowyourboat@juliusmarminge@justsomelegs@heysonder@AdemBenAbdallah@Spitfire1900@jvzijp@Klemencina@oski646@noxire-dev