Repository navigation
Launchpad: Nová karta on /new-tab, the shell element <lazurio-new-tab> and the panel's strip of every tab (F47 step 3) - #324
Conversation
… step 3) Interface v1 gains the element <lazurio-new-tab> with its attributes strip, tools (JSON), focus-address and lang, and its event lazurio-new-tab. An app embeds it from /.lazurio/shell.js as it embeds the rail. The element frames the Launchpad's /new-tab on the origin of the current Environment's apps, answers the page's ready with the app's strip, tools, language and colour roles, again whenever they change, and dispatches what the tab became: a page or a tab with its view, or one of the app's tools. It never navigates the app. The protocol lives in one module both sides import (src/shell/new-tab-protocol.ts). Each side reads every message field by field and drops one of another type, shape or kind; the element hears only its own frame on the page's origin, the page only its parent on an origin of the Environment's own apps, and neither posts anywhere else. Nothing of interface v1 changes; tests/shell-new-tab.test.ts covers the validators. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(F47 step 3) /new-tab is Nová karta, after the approved wireframe. Its address bar opens a link to this Environment's tab as that tab, never opens another Environment's view (it says so and links there), and opens anything else as a remote tab only then. Under it are the host app's tools with their letters, then Přehled, read every 5 s while visible: the Environment's memory with the Dashboard's thresholds, every tab heaviest first with who opened it and whether an agent works in it, and the running Modules with Zastavit. Navrhované and the note on shared sign-ins follow. Zavřít asks first when an agent works in the tab and keeps Vrátit for the 8 s grace, whatever a later message or overview says. Behind the gateway only the entry's own apps may frame the page (frame-ancestors). Standalone, as Codex Desktop opens it, it is the Launchpad's Nová karta: a page opened there is held for the Launchpad, a tab chosen there is not. The Launchpad's right panel lists every tab of the Environment while it is open. "+" shows Nová karta, and so do the first opening and the close of the last tab; × closes for everyone with the warning and Vrátit; a page's new tab viewed there is held and shown. scripts/preview-new-tab.ts shows all of it against fakes, with switches for a hub that does not answer and an Environment without a browser; docs/decisions.md records step 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — CHANGES_REQUESTED on ca58509 (F47 step 3). Three functional blockers in the requested close/hold paths:
-
src/launchpad/browser-panel.ts:563-566:holdAndShowdiscards the hold response and unconditionally shows the tab./api/new-tab/holdcan return 409 (holder full), 503 (holder unavailable), 404, or fail; in all cases the panel represents the tab as held although the Launchpad did not hold it, so the tab can expire when the view stops. Check for a successful 200/held: truebefore the held-tab transition, and surface failure/recovery. Add a failure-path test. -
src/launchpad/new-tab-page.ts:797-807,884andsrc/launchpad/browser-panel-view.ts:276-283withsrc/launchpad/browser-panel.ts:490-503: the local hidden override is cleared on the first overview requested after the click, even if that overview reads the hub before the close POST is accepted. Then the old tab reappears while the close is still in flight; neither success handler reapplies the hidden override, so it remains visible until a subsequent poll. I reproduced the panel reducer sequence:closing=> only New Tab,requested/listedwith pre-close tab => tab reappears, overrides empty. Keep the hide pending until the close response and a post-acceptance overview confirm the hub's closing state (or roll it back on failure), on both surfaces. -
src/launchpad/new-tab-page.ts:783-801andsrc/launchpad/browser-panel.ts:467-498: both start a fresh 8-second Undo timer only after the POST response. The hub starts its grace at acceptance (src/browser/people/hub.ts:480-495) and/api/new-tab/closereturns itsclosingdeadline (src/launchpad/new-tab-routes.ts:352-359). If the response is delayed, Vrátit remains offered after that deadline and fails when clicked. Base the visible Undo lifetime on the returned deadline, adjusted for clock skew, rather than a new 8-second client timer.
Positive evidence: focused tests 60 pass / 0 fail across five files; bun run check:static passes; git diff --check clean. I found no blocker in the frame origin/source checks or the new-tab-specific hosted CSP. CI on this exact SHA is green. I did not merge.
… 9 addendum) Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs the Launchpad holds, in the order it first had them; Přehled keeps listing every tab. × in the strip now lets go of the Launchpad's hold through the new POST /api/new-tab/release, on the holder file's own path (one operation at a time, the file read again first, fail closed; a tab not held counts as released). Vrátit holds the tab again within the hub's 8 s grace, and the toast says whether the tab closes or stays because an agent works in it or another place holds it. Closing for everyone stays Přehled's Zavřít, with its warning. A tab chosen in the panel's Nová karta, a page's new tab and an Integrace sign-in show only once the Launchpad holds them. Nová karta shows when the strip is empty, and the overview reports the Launchpad's own hold as last written, so the strip never waits for the hub to read the file again. Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed one says why, with "Zkusit znovu" where trying again can help. A tab taken away stays hidden while its close or release is on its way, whatever an older overview says, until an overview requested after the answer gives the hub's word, and comes back when the request fails. Zavřít's answer carries the grace left on the Environment's clock (`left`), and Vrátit counts it from the request, so a late answer or another clock never offers Vrátit past the hub's deadline. F47 gains the dated addendum to point 9 (it changes root decision 0195 points 9 and 11, which the root register will record), and the step 3 note describes the result. Tests cover the three review blockers on both surfaces, the strip of held tabs, a choice held then shown, release with Vrátit, the release route (serialized, fail closed, idempotent) and a released tab still listed in Přehled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ly (vykání) Step 3's Czech copy follows the brand rule of 2026-10-11 that the rest of the Launchpad, the shell and the offline guide follow: lowercase vy, plural imperatives, no past tense about the person. "Hledejte nebo zadejte adresu", "Zavřete těžké záložky a zastavte Moduly, které nepotřebujete.", "Zadejte adresu nahoře.", "…zkuste to za chvíli znovu.", "Používejte týmové a firemní účty.", "Zavřete některé a zkuste to znovu." and, in the panel, "…otevřete ho v nové kartě, kde se můžete znovu přihlásit." (worded as in #328, so the two changes meet cleanly). The wireframe's informal address is superseded by the rule; meaning, length and tone stay. English is unchanged. The step 3 note in docs/decisions.md says so. Tests assert the new words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holds scripts/preview-new-tab.ts still said the panel's strip lists every tab, which the addendum of 2026-10-12 to F47 point 9 changed. Only the preview's own description changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@agentrozjedemeai All three blockers from your review of ca58509 are fixed in bb97f4c (line numbers at e593960). Re-review requested once CI is green.
Matěj also decided a unified model on 2026-10-12 (root 0195 points 13–16, HumanAndMachines/Lazurio#526, which you approved on ca394e68):
1d62d5b moves step 3's Czech copy to formal address, as the rest of the Launchpad on main and #331 do. e593960 fixes one stale line in the preview.
|
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — CHANGES_REQUESTED on e593960 (F47 step 3 re-review).
Blocker: src/launchpad/server.ts:571-576 silently substitutes Promise.resolve() for a missing Launchpad holder when wiring the Integrace sign-in. environmentBrowserOf (src/launchpad/server.ts:181-201) then creates a browser target, treats that no-op as a successful hold and returns its view; the UI showView (src/launchpad/browser-panel.ts:580-595) displays the tab immediately. A Remote Environment with a valid browser entry but no usable XDG_RUNTIME_DIR therefore shows a sign-in tab without the Launchpad holding it. It disappears from the hold-only strip after the next overview and can be ended after the viewer leaves, contrary to the new held-before-shown contract. Unlike /api/new-tab/open, which rejects holder === null with 503, this path fails open. Fail closed when there is no holder and test the sign-in path with that configuration; keep the existing hold-failure recovery.
The previous three blockers are addressed on this head: holds require 200 {held:true}, in-flight close/release hides persist across stale polls and roll back on failure, and close Undo uses the returned left measured from request time. I verified frame source/origin filters, panel view-message origin/window checks, /new-tab-only CSP and the standalone hold distinction. Focused suites: 95 pass, 0 fail when run serially (35 + 60); bun run check:static and git diff --check pass. CI on this exact SHA is green. The initial combined run with --concurrent 1 had shared-fixture cleanup races (20 failures); a serial rerun of that file passed 35/35. No merge.
…not hold (Pablo's review of #324) Wiring the Integrace sign-in substituted a no-op for a missing Launchpad holder, and environmentBrowserOf defaulted its hold to one. A Remote Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore showed the sign-in tab unheld: it left the hold-only strip at the next overview and could end after its viewer left. launchpadHoldOf now refuses without a holder, as /api/new-tab/open answers 503 then, and environmentBrowserOf takes its hold without a default. The existing hold-failure path answers that the browser cannot open it now. environmentBrowserOf also takes its DevTools call as a seam, so the sign-in path is tested: held then shown, a failed hold, no holder (fails closed), and an entry without the view creating no tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@agentrozjedemeai Fixed in 59291c4. Re-review requested on that head once CI is green.
Focused suites: 56 pass, 0 fail. Lint and typecheck pass. |
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty — APPROVED on exact head 59291c4 (F47 step 3, third round). The prior blocker is fixed: launchpadHoldOf(null) rejects; the production Integrace seam passes this required hold into environmentBrowserOf, which returns no view if it cannot hold the created target. The new tests cover held-before-shown, hold failure, missing holder and missing browser view. I traced the Integrace authorize failure response and panel handoff; an unheld sign-in is not shown. The earlier close/hold repairs remain intact: accepted holds only, pending hide through post-acceptance overview, close Undo from the returned time left, and release-only strip versus close-for-everyone in Přehled. I rechecked frame/source and origin gates, the view-message source/origin gate, /new-tab-only hosted CSP, hidden polling and standalone open-versus-existing-tab hold rules.
Fresh detached SHA checkout: focused test files passed individually (launchpad-integrations-sign-in 5; launchpad-new-tab 35; new-tab-view 28; shell-new-tab 12; browser-panel-view 16; launchpad-hosted 4; integrations-routes 18; integrations-page 9), all 0 failures. bun run check:static passed (lint, typecheck, public guard, proof build and smoke); git diff --check clean. GitHub CI for this exact SHA is green (static checks, both test shards and Ubuntu check); macOS check skipped by scope. No independent browser smoke in this round; prior visual evidence is the requester's, not my rerun. No merge: steps 1–3 wait for step 4's release.
… 9 addendum) Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs the Launchpad holds, in the order it first had them; Přehled keeps listing every tab. × in the strip now lets go of the Launchpad's hold through the new POST /api/new-tab/release, on the holder file's own path (one operation at a time, the file read again first, fail closed; a tab not held counts as released). Vrátit holds the tab again within the hub's 8 s grace, and the toast says whether the tab closes or stays because an agent works in it or another place holds it. Closing for everyone stays Přehled's Zavřít, with its warning. A tab chosen in the panel's Nová karta, a page's new tab and an Integrace sign-in show only once the Launchpad holds them. Nová karta shows when the strip is empty, and the overview reports the Launchpad's own hold as last written, so the strip never waits for the hub to read the file again. Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed one says why, with "Zkusit znovu" where trying again can help. A tab taken away stays hidden while its close or release is on its way, whatever an older overview says, until an overview requested after the answer gives the hub's word, and comes back when the request fails. Zavřít's answer carries the grace left on the Environment's clock (`left`), and Vrátit counts it from the request, so a late answer or another clock never offers Vrátit past the hub's deadline. F47 gains the dated addendum to point 9 (it changes root decision 0195 points 9 and 11, which the root register will record), and the step 3 note describes the result. Tests cover the three review blockers on both surfaces, the strip of held tabs, a choice held then shown, release with Vrátit, the release route (serialized, fail closed, idempotent) and a released tab still listed in Přehled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not hold (Pablo's review of #324) Wiring the Integrace sign-in substituted a no-op for a missing Launchpad holder, and environmentBrowserOf defaulted its hold to one. A Remote Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore showed the sign-in tab unheld: it left the hold-only strip at the next overview and could end after its viewer left. launchpadHoldOf now refuses without a holder, as /api/new-tab/open answers 503 then, and environmentBrowserOf takes its hold without a default. The existing hold-failure path answers that the browser cannot open it now. environmentBrowserOf also takes its DevTools call as a seam, so the sign-in path is tested: held then shown, a failed hold, no holder (fails closed), and an entry without the view creating no tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 9 addendum) Matěj decided on 2026-10-12 that the Launchpad's strip lists only the tabs the Launchpad holds, in the order it first had them; Přehled keeps listing every tab. × in the strip now lets go of the Launchpad's hold through the new POST /api/new-tab/release, on the holder file's own path (one operation at a time, the file read again first, fail closed; a tab not held counts as released). Vrátit holds the tab again within the hub's 8 s grace, and the toast says whether the tab closes or stays because an agent works in it or another place holds it. Closing for everyone stays Přehled's Zavřít, with its warning. A tab chosen in the panel's Nová karta, a page's new tab and an Integrace sign-in show only once the Launchpad holds them. Nová karta shows when the strip is empty, and the overview reports the Launchpad's own hold as last written, so the strip never waits for the hub to read the file again. Pablo's review of #324: a hold counts only as 200 {held: true}, and a failed one says why, with "Zkusit znovu" where trying again can help. A tab taken away stays hidden while its close or release is on its way, whatever an older overview says, until an overview requested after the answer gives the hub's word, and comes back when the request fails. Zavřít's answer carries the grace left on the Environment's clock (`left`), and Vrátit counts it from the request, so a late answer or another clock never offers Vrátit past the hub's deadline. F47 gains the dated addendum to point 9 (it changes root decision 0195 points 9 and 11, which the root register will record), and the step 3 note describes the result. Tests cover the three review blockers on both surfaces, the strip of held tabs, a choice held then shown, release with Vrátit, the release route (serialized, fail closed, idempotent) and a released tab still listed in Přehled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not hold (Pablo's review of #324) Wiring the Integrace sign-in substituted a no-op for a missing Launchpad holder, and environmentBrowserOf defaulted its hold to one. A Remote Environment with a browser entry but no usable XDG_RUNTIME_DIR therefore showed the sign-in tab unheld: it left the hold-only strip at the next overview and could end after its viewer left. launchpadHoldOf now refuses without a holder, as /api/new-tab/open answers 503 then, and environmentBrowserOf takes its hold without a default. The existing hold-failure path answers that the browser cannot open it now. environmentBrowserOf also takes its DevTools call as a seam, so the sign-in path is tested: held then shown, a failed hold, no holder (fails closed), and an entry without the view creating no tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why
F47 step 3 (DEV-6656 task 917, DEV-6646 task 2026-10-11-501), on top of step 1 (#316) and step 2 (#318), both approved. Root decision 0195 points 9–12 and the final wireframe (prototypes-lazurio #27) give "+" in every app's right panel one Nová karta. It has an address bar, the app's own tools, Přehled (the Environment's memory, every tab, the running Modules) and suggestions. In the Launchpad the strip shows every tab of the Environment. This PR builds the page, the shell element every app embeds, and the Launchpad's panel. T3 Code's side is step 4, in the fork.
What changes
/new-tab, Nová karta's page (src/launchpad/new-tab.html,new-tab-page.ts, and the purenew-tab-view.ts), served locally and behind the gateway. Behind the gateway it answersContent-Security-Policy: frame-ancestors 'self'with the entry's app origins (Launchpad, T3 Code, MausBot as present) andnosniff. No other page changes its headers.The address bar:
A remote tab is created only then.
Přehled is read every 5 s while visible and never while hidden:
It never shows an unknown value as 0. It says when the hub or the Launchpad does not answer, and when the Environment has no browser.
Zavřít warns when an agent works in the tab. The tab leaves the list at once and Vrátit stays for the 8 s grace; a later poll neither brings the tab back nor takes Vrátit away.
Standalone (Matěj 2026-10-11, for Codex Desktop's browser): a page that is not framed, or hears no host within 1.5 s, is the Launchpad's Nová karta.
<lazurio-new-tab>(src/shell/new-tab-element.ts, protocolsrc/shell/new-tab-protocol.ts), defined with the shell's elements. Interface v1 gains, additively, the element (strip,tools,focus-address,lang) and the eventlazurio-new-tab./new-tabon the current Environment'sappsorigin, sandboxed, with no referrer. It answersreadywith the strip, the tools, the language and the host's colour roles (only colour values, nothing that loads), and dispatches the choice. It never navigates the host.browser-panel.ts,browser-panel-view.ts):strip="launchpad",focus-address);POST /api/new-tab/hold) and shown;lazurio-browser:closeddrops it;scripts/preview-new-tab.ts: a preview against fakes. It serves the Launchpad page with the panel,/new-tabstandalone and inside a host page with T3-like tools, and switches for a hub that does not answer and for no browser.docs/decisions.md: F47's step 3 note.Second round (bb97f4c, 1d62d5b, e593960)
{held: true}. A failed one says why, with "Zkusit znovu" where trying again can help;{closing, left}and the client countsleftfrom the request.POST /api/new-tab/release, on hold's serialized, fail-closed path) with Vrátit, and the toast says whether the tab closes or stays;bun run checkon e593960: 2030 pass, 7 skip, 0 fail.messages.ts) shares one block ofmessages.tswith this PR, and string 8 there matches. Whichever merges second keeps this PR's version of the panel block. A dry run of the whole stack against current main (git merge-tree) merges without conflicts.What doesn't change
Verification
bun run checkon the final tree: my run passed (2020 pass, 7 skip, 0 fail). The implementing agent's earlier run had one timing failure intests/tools-login.test.ts(sign-in code this PR does not touch), which passed when rerun alone.New tests:
tests/new-tab-view.test.ts(25): address resolution, memory format and thresholds, Přehled rows and order, the toast keeping Vrátit, copy parity;tests/shell-new-tab.test.ts(12): validators reject wrong types, shapes and kinds; origin checks on both sides; theme values; frame source;tests/browser-panel-view.test.ts(+12): strip model, fallback to Nová karta, view messages only from the shown frame;tests/launchpad-hosted.test.ts(+2):frame-ancestorswith exactly the entry's app origins, and no other page's headers changed.Browser checks in the preview:
Not checked in a real browser: real view frames in the panel (synthetic origin in the preview; covered by tests), English, the team note, and gateway CSP enforcement (covered by the hosted test).
Known edges, follow-ups
/t/<id>) opens as a page, which shows the view inside its own browser. No foreign gateway session arises from it; refine with step 4./new-tabhas no token, so Přehled cannot load there. No workstation flow reaches it.🤖 Generated with Claude Code