Skip to content

feat(web): Lazurio shell slot (rail, column head, Buddy) - #37

Merged
immakermatty merged 1 commit into
mainfrom
lazurio/shell-slot
Oct 4, 2026
Merged

immakermatty merged 1 commit into
mainfrom
lazurio/shell-slot

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 4, 2026 •

Copy link
Copy Markdown

Closes #33.

Why

Chat, Apps and Automate share one Lazurio shell: the rail of spaces on the left, and at the top of each app's own left column the Environment picker, the Settings gear and the switch Chat · Apps · Automate. Buddy's bubble comes later. The shell is one library in LazurioPlatform (decision F36), served by the Environment's Launchpad at /.lazurio/shell.js on every origin of the Environment. This fork only places the elements and fetches no data, so a new rail, picker or data source never needs a fork release. It mirrors Lazurio/OpenMausBot#20, which did the same for MausBot.

What changes (two upstream files)

  • apps/web/index.html:
    • <script type="module" src="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/.lazurio/shell.js" vite-ignore>. Vite leaves the tag in place (it drops only the vite-ignore attribute) and bundles nothing. Checked with a real vite build.
    • <lazurio-rail> before #root, <lazurio-buddy> after it.
    • #root { box-sizing: border-box; padding-left: var(--lazurio-rail-width, 0px); }. Padding, not margin.
    • T3-specific, beyond OpenMausBot#20: T3's desktop sidebar (ui/sidebar.tsx) and its toggle are position: fixed against the window, so padding alone left them under the rail. That showed up in the first visual check. When the rail is defined, the sidebar wrapper becomes their containing block: lazurio-rail:defined ~ #root [data-slot="sidebar-wrapper"] { contain: layout paint; }. Without the shell the selector never matches.
  • apps/web/src/components/AppSidebarLayout.tsx:
    • createElement("lazurio-column-head", { active: "chat", ref }) is the first child of the left sidebar, above the upstream top row (the toggle and the "T3 Code" brand). The brand and all controls stay (root decision 0179 point 6; placement clarified on Lazurio shell slot: load /.lazurio/shell.js and place the rail, the app switch and Buddy (upstream look stays) #33). Placing it here rather than in Sidebar.tsx covers the thread sidebar, the legacy sidebar and the Settings nav from one spot.
    • T3-specific: the sidebar toggle (SidebarControl) is also position: fixed at the top edge, so it would cover the column head. While the desktop sidebar is open, the toggle moves down by the column head's measured height (a ResizeObserver on the element). With the shell absent (or without ResizeObserver) the height is 0 and nothing moves. The mobile sheet has its own toggle and is unaffected.
  • allowed_upstream_changes gets both files as separate lines under one reason comment.
  • The release contract test checks the slot: the loader, the padding, the containment rule (and that ui/sidebar.tsx still has data-slot="sidebar-wrapper"), the rail, Buddy, the column head as the sidebar's first child (exactly once), its measurement and the toggle's top offset. A rebuild on a new upstream tag that loses any of these fails CI. I confirmed this by reverting each file locally: the test fails.
  • The runbook (docs/operations/lazurio-fork-release.md): the "Lazurio shell" section describes the slot as built, and the overlay table gets a row for this commit.

What does not change

  • Outside Lazurio there is no /.lazurio/shell.js. The T3 server's SPA fallback (and the desktop protocol handler) answers with index.html, the browser refuses it as a module and logs one console error. The elements stay undefined, the rail width is 0, the column head has height 0, the containment rule doesn't apply, and the layout is pixel-identical to upstream (measured: toggle at (13,12), brand at (52,12), same as before).
  • The desktop (Electron) app uses the same UI and falls into the case above. The official desktop and mobile apps are upstream builds and don't carry this patch at all.
  • No router, state, data or wire changes. No recolouring or renaming. lang is not set.
  • The picker dropdown must render in the top layer (Popover API or fixed position from the shadow root) so the sidebar's overflow cannot clip it. That belongs to the shell library, not this fork.

Verification

  • node --test scripts/lazurio-release-contract.test.mjs: 12/12 pass.
  • The allowlist guard from lazurio-fork-ci.yml, run locally against upstream v0.0.45 (6c8fed35): no unexpected upstream changes.
  • vp fmt --check: clean. tsc --noEmit in apps/web: ok. vp lint on AppSidebarLayout.tsx: one pre-existing upstream warning (set-state-in-effect at the fullscreen effect), nothing new.
  • vp test run src/themeBoot.test.ts src/bundledDev.test.ts src/components/Sidebar.logic.test.ts src/components/SidebarStageBackdrop.test.tsx (the index.html and sidebar tests): 4 files, 203 tests pass.
  • vp build of @t3tools/web: the built index.html keeps <script type="module" src="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/.lazurio/shell.js">, the rail, Buddy and the style. There is no .lazurio output in dist.
  • Visual check in Chrome (playwright-core, channel: "chrome"):
    • Setup: the real T3 server from this branch (node apps/server/src/bin.ts serve, isolated --base-dir, a throwaway git project) served the built web UI. /.lazurio/shell.js was answered by a route stub that defines a 64 px fixed rail (sets --lazurio-rail-width: 64px, z-index 40 like the Platform rail) and a 96 px block column head.
    • 1512×860: #root padding-left is 64px. The sidebar starts at x=64. The column head is at (64,0), 96 px tall, and is the first child of sidebar-inner. The upstream top row is at y=96, with the toggle at (77,108) and the brand at (116,108). Document, body and #root scroll widths all equal 1512, so there is no horizontal overflow.
    • Sidebar collapsed: the toggle returns to (77,12). The sidebar is clipped, with no sliver under the rail.
    • /settings: the column head is at the top of the Settings nav, the same way.
    • 390×844: no horizontal overflow (390/390). The mobile sheet (portaled to body) opens over the rail, with the column head at its top above the sheet's own top row.
    • Screenshots are kept locally, not attached.

Depends on

  • HumanAndMachine-ai/Machines#353 (released in v0.12.113): the gateway serves /.lazurio/* on the t3code. origin.
  • LazurioPlatform rc.19 or later: the shell elements (interface v1, src/shell/interface.ts).

Notes / risks

  • Low risk. A missing script costs one console error. Without the shell the slot changes nothing.
  • On a phone, the Platform rail (72 px, z-index 40) takes width from a 390 px screen. T3's own empty state already clips at that width without the rail. How the rail behaves on narrow screens is a shell question, not this fork's.
  • lazurio: unsent prompt draft by link from the shell #36 also appends to allowed_upstream_changes, the contract test and the runbook. Whichever merges second needs a trivial rebase.
  • This PR does not release anything. A -lazurio.N or preview release from main follows the runbook once this is merged.

🤖 Generated with Claude Code

Comment thread apps/web/index.html
Comment thread apps/web/src/components/AppSidebarLayout.tsx
Comment thread scripts/lazurio-release-contract.test.mjs

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the exact head 1879147 independently. The v1 shell interface matches the loader, elements, active="chat", and rail-width property. The :defined containment confines the fixed desktop sidebar only when the rail exists; the React 19 ref/ResizeObserver path measures the column head and moves the open desktop toggle, with a zero-height fallback when the shell/observer is absent. The upstream brand and routes remain untouched.

Local git diff --check and the release-contract suite passed (12/12). Lazurio Fork CI and archive checks completed successfully on this SHA. The three Greptile conversations are resolved after checking the two fixes and the withdrawn width finding. I did not independently repeat the browser screenshots or the 203 web tests; those are author evidence.

Integration note, not a code objection: current main includes #36, and the PR now conflicts in the workflow, release contract, and runbook. This approval is only for the pinned head. Resolve the conflicts without broadening scope, then rerun exact-head review/checks/conversation gates on the resulting new head before any merge. No merge performed here.

Place the Lazurio shell in the web client, as OpenMausBot #20 does for
MausBot. The shell itself (/.lazurio/shell.js, Web Components with Shadow
DOM) is served by the Environment's Launchpad; outside Lazurio the script
is absent, the elements stay undefined and the client behaves as upstream.

- index.html: the same-origin loader (vite-ignore), <lazurio-rail> before
  #root, <lazurio-buddy> after it, and #root padding-left by
  --lazurio-rail-width. T3's sidebar and its toggle are position: fixed, so
  with the rail defined the sidebar wrapper becomes their containing block
  and they sit beside the rail instead of under it.
- AppSidebarLayout.tsx: <lazurio-column-head active="chat"> as the first
  child of the sidebar, above the upstream top row (thread, legacy and
  settings sidebars alike); the fixed sidebar toggle moves down with that
  row while the desktop sidebar is open.
- Both files join allowed_upstream_changes; the release contract test keeps
  the slot across rebuilds on new upstream tags; the runbook describes it.

Closes #33

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Exact-head re-review of 0827ac7 after #35 landed on main. The slot source blobs (apps/web/index.html and AppSidebarLayout.tsx) are identical to the previously approved 1879147 head. The rebase retains both overlays in the CI allowlist, release-contract suite, and runbook (one entry each); the shell slot remains the sole PR delta against current main. Local git diff --check and the contract suite pass (13/13). All three review conversations are resolved; no reviewer is pending. Web dependencies were not installed in this isolated clone, so I did not repeat tsc, formatting, or browser verification. At this review, the server/web compatibility and darwin-arm64 archive CI checks are still running; approval is for the code on this exact commit, not a claim that CI/merge gates are green. No merge performed.

@immakermatty
immakermatty merged commit 1e4cf23 into main Oct 4, 2026
6 checks passed
Sign up for free to 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.

Lazurio shell slot: load /.lazurio/shell.js and place the rail, the app switch and Buddy (upstream look stays)

2 participants