diff --git a/apps/web/src/lazurio/LazurioEnvironmentBrowser.test.tsx b/apps/web/src/lazurio/LazurioEnvironmentBrowser.test.tsx index 25d6723df97e..e125b7e94d5a 100644 --- a/apps/web/src/lazurio/LazurioEnvironmentBrowser.test.tsx +++ b/apps/web/src/lazurio/LazurioEnvironmentBrowser.test.tsx @@ -13,7 +13,11 @@ import { type ThreadRightPanelState, useRightPanelStore, } from "../rightPanelStore"; -import { LazurioRightPanelSurfaces, useLazurioThreadTabs } from "./LazurioEnvironmentBrowser"; +import { + ENVIRONMENT_ELEMENT, + LazurioRightPanelSurfaces, + useLazurioThreadTabs, +} from "./LazurioEnvironmentBrowser"; import { resetThreadTabs, syncOf, @@ -294,22 +298,21 @@ describe("the panel's Environment browser frames", () => { }); describe("another Environment's browser in the panel", () => { - const signInNotice = "Sign in to the Environment browser of Acme · vm-02"; + const signInNotice = "Sign in to this Environment's browser"; - it("frames it marked as that Environment's, telling it only this page's origin", async () => { + it("frames it marked as that Environment's, named by the shell, telling it only this page's origin", async () => { withSnapshot(snapshot([{ tabId: id(1) }])); await showPanel(threadA, [tab(1), foreignTab], foreignTab.id); expect(frameOf(foreignView).getAttribute("referrerpolicy")).toBe("origin"); - expect(frameOf(foreignView).title).toBe("Acme · vm-02"); - expect(container.textContent).toContain("Acme · vm-02"); - const picture = container.querySelector( - 'img[src="https://github.com/acme.png?size=32"]', - ); - expect(picture?.getAttribute("referrerpolicy")).toBe("no-referrer"); - act(() => { - picture?.dispatchEvent(new Event("error")); - }); - expect(container.textContent).toContain("AAcme · vm-02"); + expect(frameOf(foreignView).title).toBe("Another Environment's browser"); + // The shell's element names it as that Environment's switcher does; until the shell knows it + // (or without the element), it says only that the browser is another Environment's. + const name = container.querySelector(ENVIRONMENT_ELEMENT); + expect(name?.getAttribute("environment")).toBe("vm-02.acme"); + expect(name?.getAttribute("lang")).toBe("en"); + expect(name?.textContent).toBe("Another Environment's browser"); + expect(container.textContent).not.toContain("Acme · vm-02"); + expect(container.querySelector("img")).toBeNull(); expect(frameOf(viewOf(1)).title).toBe("Environment browser"); }); diff --git a/apps/web/src/lazurio/LazurioEnvironmentBrowser.tsx b/apps/web/src/lazurio/LazurioEnvironmentBrowser.tsx index b91ae9054a8f..18bfa816764f 100644 --- a/apps/web/src/lazurio/LazurioEnvironmentBrowser.tsx +++ b/apps/web/src/lazurio/LazurioEnvironmentBrowser.tsx @@ -1,7 +1,7 @@ import { scopedThreadKey } from "@t3tools/client-runtime/environment"; import type { ScopedThreadRef } from "@t3tools/contracts"; import { ExternalLink, Globe2, LogIn, Plus, Users } from "lucide-react"; -import { type ReactNode, useEffect, useRef, useState } from "react"; +import { createElement, type ReactNode, useEffect, useRef, useState } from "react"; import { Button } from "~/components/ui/button"; import { @@ -21,10 +21,9 @@ import { usePrimaryEnvironmentId } from "../state/environments"; import type { LazurioBrowserView } from "./browserView"; import { foreignEnvironmentBrowserTab, - foreignEnvironmentIcon, - foreignEnvironmentLabel, isPlainPrimaryClick, readEnvironmentBrowserMessage, + viewEnvironmentId, } from "./environmentBrowser"; import { NewTabSurface, type SurfaceAction, useNewTab, useNewTabDefined } from "./LazurioNewTab"; import { @@ -219,21 +218,29 @@ export function useLazurioThreadTabs( const FOREIGN_VIEW_AFTER_LOAD_MS = 4_000; const FOREIGN_VIEW_MS = 15_000; -/** The GitHub picture of the Organization or person whose Environment it is, else its initial. */ -function EnvironmentOwnerPicture(props: { readonly environment: LazurioBrowserView }) { - const [failed, setFailed] = useState(false); - return failed ? ( - - {props.environment.owner.charAt(0).toUpperCase()} - - ) : ( - setFailed(true)} - /> +/** The shell's element that names an Environment as its switcher does (LazurioPlatform F39, + * addendum of 2026-10-12). */ +export const ENVIRONMENT_ELEMENT = "lazurio-environment"; + +/** Another Environment's view, said without naming it: what the head shows until the shell knows + * that Environment, and where the Environment's Platform predates the element. */ +const anotherEnvironment = (czech: boolean) => + czech ? "Prohlížeč jiného Environmentu" : "Another Environment's browser"; + +/** Another Environment's name in the panel's head: the shell's ``, which + * shows its children until the page's shell knows that Environment. */ +function ForeignEnvironmentName(props: { + readonly environment: LazurioBrowserView; + readonly czech: boolean; +}) { + return createElement( + ENVIRONMENT_ELEMENT, + { + environment: viewEnvironmentId(props.environment), + lang: props.czech ? "cs" : "en", + className: "min-w-0 flex-1 truncate font-medium text-warning-foreground text-xs", + }, + anotherEnvironment(props.czech), ); } @@ -466,7 +473,7 @@ function LazurioEnvironmentBrowser(props: { }, [isForeign, view, request, loaded, cameUp]); const signIn = isForeign && Boolean(view) && late === request && cameUp !== request; const czech = prefersCzech(); - const label = foreign === null ? null : foreignEnvironmentLabel(foreign.environment, czech); + const title = foreign === null ? "Environment browser" : anotherEnvironment(czech); return (
@@ -477,21 +484,13 @@ function LazurioEnvironmentBrowser(props: { )} > {foreign === null ? ( - + <> + + {title} + ) : ( - + )} - - {label ?? "Environment browser"} - setLoaded(request)} /> - {signIn && label !== null ? ( + {signIn && foreign !== null ? (
@@ -544,8 +543,8 @@ function LazurioEnvironmentBrowser(props: { {czech - ? `Přihlas se do prohlížeče Environmentu ${label}` - : `Sign in to the Environment browser of ${label}`} + ? "Přihlas se do prohlížeče tohoto Environmentu" + : "Sign in to this Environment's browser"} {czech diff --git a/apps/web/src/lazurio/environmentBrowser.test.ts b/apps/web/src/lazurio/environmentBrowser.test.ts index 283bf9d5ea88..ae75e4eb0624 100644 --- a/apps/web/src/lazurio/environmentBrowser.test.ts +++ b/apps/web/src/lazurio/environmentBrowser.test.ts @@ -4,10 +4,9 @@ import { environmentBrowserLink, environmentBrowserTab, foreignEnvironmentBrowserTab, - foreignEnvironmentIcon, - foreignEnvironmentLabel, isPlainPrimaryClick, readEnvironmentBrowserMessage, + viewEnvironmentId, } from "./environmentBrowser"; const origin = "https://t3code.vm-01.example.lazurio.io"; @@ -177,24 +176,16 @@ describe("another Lazurio Environment's tab", () => { } }); - it("is named by its Organization and Environment, or as the person's own", () => { - const named = (address: string, czech: boolean) => { + it("hands the shell the id of its Environment, the view's host between browser. and lazurio.io", () => { + const idOf = (address: string) => { const tab = tabOf(address); if (tab === null) throw new Error(`${address} is no other Environment's tab`); - return foreignEnvironmentLabel(tab.environment, czech); + return viewEnvironmentId(tab.environment); }; - expect(named(organization, true)).toBe("Acme · vm-02"); - expect(named(organization, false)).toBe("Acme · vm-02"); - expect(named(personal, true)).toBe("Osobní · jana"); - expect(named(personal, false)).toBe("Personal · jana"); - // More labels: the Organization is the one before lazurio.io, the Environment after browser. - expect(named(`https://browser.a.b.c.lazurio.io/t/${foreignId}`, false)).toBe("C · a"); - expect(foreignEnvironmentIcon(tabOf(organization)!.environment)).toBe( - "https://github.com/acme.png?size=32", - ); - expect(foreignEnvironmentIcon(tabOf(personal)!.environment)).toBe( - "https://github.com/jana.png?size=32", - ); + expect(idOf(organization)).toBe("vm-02.acme"); + expect(idOf(personal)).toBe("jana"); + // The shell decides what an id names; the fork reads no Organization or Machine out of it. + expect(idOf(`https://browser.a.b.c.lazurio.io/t/${foreignId}`)).toBe("a.b.c"); }); }); diff --git a/apps/web/src/lazurio/environmentBrowser.ts b/apps/web/src/lazurio/environmentBrowser.ts index 044bd6a45d8e..5fd6c23a0701 100644 --- a/apps/web/src/lazurio/environmentBrowser.ts +++ b/apps/web/src/lazurio/environmentBrowser.ts @@ -101,21 +101,13 @@ function parentDomainOf(origin: string): string | null { } /** - * How the panel names another Environment: ` · ` for an Organization's, - * `Osobní · ` or `Personal · ` for a person's own Environment. + * The id by which the Environment's shell names the Environment of a view: its view's host + * between `browser.` and `.lazurio.io` (`vm-02.acme`, or `jana` for a person's own). The panel + * hands it to the shell's ``, which names that Environment as its switcher + * does (LazurioPlatform F39, addendum of 2026-10-12); the fork has no naming rule of its own. */ -export function foreignEnvironmentLabel(environment: LazurioBrowserView, czech: boolean): string { - const owner = environment.personal - ? czech - ? "Osobní" - : "Personal" - : `${environment.owner.charAt(0).toUpperCase()}${environment.owner.slice(1)}`; - return `${owner} · ${environment.environment}`; -} - -/** The GitHub picture of the Organization, or of the person, whose Environment it is. */ -export const foreignEnvironmentIcon = (environment: LazurioBrowserView) => - `https://github.com/${environment.owner}.png?size=32`; +export const viewEnvironmentId = (environment: LazurioBrowserView): string => + new URL(environment.origin).hostname.slice("browser.".length, -".lazurio.io".length); /** A plain primary click follows a link in place; modifiers or other buttons open it elsewhere. */ export function isPlainPrimaryClick( diff --git a/docs/operations/lazurio-fork-release.md b/docs/operations/lazurio-fork-release.md index 45198a03a490..383acf193e63 100644 --- a/docs/operations/lazurio-fork-release.md +++ b/docs/operations/lazurio-fork-release.md @@ -433,18 +433,21 @@ T3 žádný prohlížeč nemá, a tak overlay dělá tohle: starší snímek ji nevrátí (bez Vrátit, záložka už není). Záložka panelu ukazuje místo „Browser“ titulek, který pohled hlásí (jinak host adresy stránky); titulky žijí jen v paměti stránky. -- **Záložka jiného Environmentu.** Je nezaměnitelně označená: barevný okraj a - hlavička s obrázkem Organizace z GitHubu - (`https://github.com/.png?size=32`, jinak její iniciála) a - jménem ` · `, tedy label před `lazurio.io` s velkým prvním - písmenem a label za `browser.` (například `Acme · vm-01`). Osobní Environment - je `Osobní · `, anglicky `Personal · `; jazyk volí overlay jako +- **Záložka jiného Environmentu.** Je nezaměnitelně označená barevným okrajem + a hlavičkou, která ten Environment jmenuje prvkem shellu + `` (LazurioPlatform F39, dodatek + z 2026-10-12): stejným jménem, znakem a řádkem „pro koho“ jako přepínač jeho + aplikací, ze stavu shellu této stránky. Id je host pohledu mezi `browser.` a + `.lazurio.io` (`viewEnvironmentId`, například `vm-01.acme` nebo `jana`). + Vlastní pravidlo pojmenování fork nemá. Dokud shell stránky ten Environment + nezná, a na Platformě bez prvku, ukáže hlavička jen „Prohlížeč jiného + Environmentu“; přesné jméno má pod ní lišta pohledu. Jazyk volí overlay jako pohled sám, podle jazyka prohlížeče (`cs`). Rám posílá `referrerPolicy="origin"`, podle kterého pohled jiného Environmentu dovolí zarámování T3 nebo Launchpadu (změna LazurioPlatform). Když pohled do 4 s od načtení rámu (15 s, když se nenačte) neohlásí svou záložku, typicky bez - sezení v bráně toho Environmentu, ukáže panel „Přihlas se do prohlížeče - Environmentu