Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions clients/web/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,40 @@ routes to the implementation and its operating guide rather than duplicating eit

| Reading or interaction | Source entry | Interpretation |
| --- | --- | --- |
| Canvas Project catalogue, retained concerns, judgments, references, and continuation | [AtlasView.svelte](lib/components/AtlasView.svelte), [Atlas link hints](lib/atlas/navigation.ts) | [Atlas](../../../docs/divination/altar/atlas.md) |
| Project board retention/recovery, portable authorship and reviewed selection transformations | [CanvasWorkspace.svelte](lib/components/CanvasWorkspace.svelte), [CanvasView.svelte](lib/components/CanvasView.svelte), [Canvas engine](lib/canvas/engine.ts) | [Canvas](../../../docs/compositions/canvas/index.md) |
| Conversation, consent, and offering recovery | [BridgeView.svelte](lib/components/BridgeView.svelte), [shell-lifetime workspace](lib/bridge/workspace.svelte.ts) | [Bridge](../../../docs/divination/altar/bridge.md) |
| Conversation catalogue, retained inspection, and source-linked document-local working material | [SeancesWorkspace.svelte](lib/components/SeancesWorkspace.svelte), [material](lib/seances/material.ts) | [Séances](../../../docs/divination/altar/seances.md) |
| Finite dictation, transcript review and private correction/vocabulary controls | [BridgeVoiceInput.svelte](lib/components/BridgeVoiceInput.svelte), [capture](lib/bridge/voice/capture.ts), [draft coordinator](lib/bridge/voice/dictation.ts), [DictationFeedback.svelte](lib/components/DictationFeedback.svelte) | [Bridge dictation](../../../docs/divination/altar/bridge.md#dictate-and-review) |
| Exact instrument work destination and inert Canvas command proposals | [work-command registry](lib/bridge/work-commands.ts), [Canvas command planner](lib/canvas/commands.ts) | [Canvas commands](../../../docs/compositions/canvas/index.md#give-a-bounded-command-through-bridge) |
| Public-note intake, lifecycle review and metadata recovery within Bridge | [PublicNotes.svelte](lib/components/PublicNotes.svelte), [recovery controller](lib/archive/notes.ts) | [Public notes](../../../docs/divination/altar/bridge.md#manage-public-notes) |
| Spatial Run field, tab observation history, and one Run's retained records and gaps | [OrbField.svelte](lib/components/OrbField.svelte), [OrbTimeline.svelte](lib/components/OrbTimeline.svelte), [OrbView.svelte](lib/components/OrbView.svelte) | [Orb](../../../docs/divination/altar/orb.md) |
| Admitted runtime topology, actual call attempts, source coverage and inspection-only scores | [Runtime evidence](lib/components/RuntimeEvidence.svelte), [runtime reader](lib/orb/runtime.ts), [generic projection](lib/graph/runtime.ts) | [Connected components](../../../docs/sepulcher/extensions/oculus.md#connected-components-and-activity) |
| Exact-Run matching Archive selection and capture limits | [ArchiveRecallPanel.svelte](lib/components/ArchiveRecallPanel.svelte) | [Archive selection](../../../docs/divination/altar/orb.md#inspect-a-matching-archive-selection) |
| Capability ownership map/list, selected properties, preview, ticket, and physical request | [NexusView.svelte](lib/components/NexusView.svelte), [native map](lib/components/NexusMap.svelte), [projection](lib/nexus/map.ts), [properties](lib/components/NexusProperties.svelte) | [Nexus](../../../docs/divination/altar/nexus.md) |
| Exact registered score and computed graph | [LoomView.svelte](lib/components/LoomView.svelte), [shared canvas](lib/components/graph/GraphCanvas.svelte) | [Loom](../../../docs/divination/altar/loom.md) |
| Canvas Project catalogue, retained concerns, judgments, references, and continuation | [AtlasView.svelte](lib/pages/atlas/components/AtlasView.svelte), [Atlas link hints](lib/atlas/navigation.ts) | [Atlas](../../../docs/divination/altar/atlas.md) |
| Project board retention/recovery, portable authorship and reviewed selection transformations | [CanvasWorkspace.svelte](lib/pages/canvas/components/CanvasWorkspace.svelte), [CanvasView.svelte](lib/pages/canvas/components/CanvasView.svelte), [Canvas engine](lib/pages/canvas/state/engine.ts) | [Canvas](../../../docs/compositions/canvas/index.md) |
| Conversation, consent, and offering recovery | [BridgeView.svelte](lib/pages/bridge/components/BridgeView.svelte), [shell-lifetime workspace](lib/shell/state/bridge-workspace.svelte.ts) | [Bridge](../../../docs/divination/altar/bridge.md) |
| Conversation catalogue, retained inspection, and source-linked document-local working material | [SeancesWorkspace.svelte](lib/pages/seances/components/SeancesWorkspace.svelte), [material](lib/pages/seances/state/material.ts) | [Séances](../../../docs/divination/altar/seances.md) |
| Finite dictation, transcript review and private correction/vocabulary controls | [BridgeVoiceInput.svelte](lib/pages/bridge/components/BridgeVoiceInput.svelte), [capture](lib/pages/bridge/state/voice/capture.ts), [draft coordinator](lib/pages/bridge/state/voice/dictation.ts), [DictationFeedback.svelte](lib/pages/bridge/components/DictationFeedback.svelte) | [Bridge dictation](../../../docs/divination/altar/bridge.md#dictate-and-review) |
| Exact instrument work destination and inert Canvas command proposals | [work-command registry](lib/shell/state/work-commands.ts), [Canvas command planner](lib/pages/canvas/state/commands.ts) | [Canvas commands](../../../docs/compositions/canvas/index.md#give-a-bounded-command-through-bridge) |
| Public-note intake, lifecycle review and metadata recovery within Bridge | [PublicNotes.svelte](lib/pages/bridge/components/PublicNotes.svelte), [recovery controller](lib/pages/bridge/state/archive/notes.ts) | [Public notes](../../../docs/divination/altar/bridge.md#manage-public-notes) |
| Spatial Run field, tab observation history, and one Run's retained records and gaps | [OrbField.svelte](lib/pages/orb/components/OrbField.svelte), [OrbTimeline.svelte](lib/pages/orb/components/OrbTimeline.svelte), [OrbView.svelte](lib/pages/orb/components/OrbView.svelte) | [Orb](../../../docs/divination/altar/orb.md) |
| Admitted runtime topology, actual call attempts, source coverage and inspection-only scores | [Runtime evidence](lib/pages/orb/components/RuntimeEvidence.svelte), [runtime reader](lib/pages/orb/state/runtime.ts), [generic projection](lib/pages/orb/state/runtime-projection.ts) | [Connected components](../../../docs/sepulcher/extensions/oculus.md#connected-components-and-activity) |
| Exact-Run matching Archive selection and capture limits | [ArchiveRecallPanel.svelte](lib/pages/orb/components/ArchiveRecallPanel.svelte) | [Archive selection](../../../docs/divination/altar/orb.md#inspect-a-matching-archive-selection) |
| Capability ownership map/list, selected properties, preview, ticket, and physical request | [NexusView.svelte](lib/pages/nexus/components/NexusView.svelte), [native map](lib/pages/nexus/components/NexusMap.svelte), [projection](lib/pages/nexus/state/map.ts), [properties](lib/pages/nexus/components/NexusProperties.svelte) | [Nexus](../../../docs/divination/altar/nexus.md) |
| Exact registered score and computed graph | [LoomView.svelte](lib/pages/loom/components/LoomView.svelte), [shared canvas](lib/graph/components/GraphCanvas.svelte) | [Loom](../../../docs/divination/altar/loom.md) |

[The layout](routes/+layout.svelte) supplies browser-document context;
[AltarShell](lib/components/AltarShell.svelte) projects navigation, global attention and the
shell-lifetime [Bridge dock and legacy full-focus page](lib/components/BridgeDock.svelte). The Séances
[AltarShell](lib/shell/components/AltarShell.svelte) projects navigation, global attention and the
shell-lifetime [Bridge dock and legacy full-focus page](lib/pages/bridge/components/BridgeDock.svelte). The Séances
work route inspects conversations without selecting the live Bridge; explicit continuation reuses
that same conversation component. Its layout
preferences contain only bounded width, side and open state; conversation and recovery remain in
their existing owners.
[Browser-local pins](lib/bridge/pins.ts) retain bounded session identities only.
[Browser-local pins](lib/navigation/pins.ts) retain bounded session identities only.
[Instrument links](lib/navigation/instruments.ts) carry bounded selection hints. Destination
loaders still validate identity and relationships; a URL does not prove ownership or Run/Pattern
equivalence. Focused component tests live beside their components; navigation has its own
equivalence. Code used by one instrument lives in that instrument's `lib/pages/<instrument>/`
`components/` and `state/` folders; code used by two or more instruments stays outside `pages/`.
Focused component tests live beside their components; navigation has its own
[identity tests](lib/navigation/instruments.test.ts).

[API contracts](lib/api/models.ts) alias [generated transport types](lib/api/openapi.d.ts).
Their Python owners are [Altar controllers](../../../src/lychd/interface/web/) and
[web-domain contracts](../../../src/lychd/domain/web/). Regeneration, checks, and builds follow
[CONTRIBUTING](../../../CONTRIBUTING.md#quality-checks); do not hand-edit generated descriptions.

Loom and Orb share [graph projections](lib/graph/) and one [canvas adapter](lib/components/graph/GraphCanvas.svelte),
Loom and Orb share [graph projections](lib/graph/) and one [canvas adapter](lib/graph/components/GraphCanvas.svelte),
with semantic views over the same identities. [The renderer
gate](../../../docs/adr/15-frontend.md#decision-lock-and-reopening-gate) owns qualification;
[State](../../../docs/state-of-the-work.md#orb-instrument) records its remaining limits.
Expand Down
2 changes: 1 addition & 1 deletion clients/web/src/lib/api/canvas.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, expect, it, vi } from "vitest";
import { blankDocument } from "$lib/canvas/engine";
import { blankDocument } from "$lib/pages/canvas/state/engine";
afterEach(() => { vi.unstubAllGlobals(); document.cookie = "canvas-csrf=; Max-Age=0"; });

it("protects exact Canvas creation and bootstrap through generated paths", async () => {
Expand Down
2 changes: 1 addition & 1 deletion clients/web/src/lib/api/orb-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, expect, it, vi } from "vitest";
import { runtimeSnapshot } from "$lib/orb/__fixtures__/runtime";
import { runtimeSnapshot } from "$lib/pages/orb/state/__fixtures__/runtime";
afterEach(() => { vi.unstubAllGlobals(); vi.resetModules(); });
it("uses the independent read endpoint and preserves authorization refusal", async () => {
const NativeRequest = globalThis.Request;
Expand Down
2 changes: 1 addition & 1 deletion clients/web/src/lib/api/scavenger.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, expect, it, vi } from "vitest";
import { admissionId, request } from "$lib/scavenger/observation.fixtures";
import { admissionId, request } from "$lib/pages/observationsScavenger/state/observation.fixtures";

afterEach(() => { vi.unstubAllGlobals(); document.cookie = "scavenger-csrf=; Max-Age=0"; });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { ApiError, decideConsent } from "$lib/api/client";
import type { ConsentCard } from "$lib/api/models";
import type { BridgeWorkspace } from "$lib/bridge/workspace.svelte";
import type { BridgeWorkspace } from "$lib/shell/state/bridge-workspace.svelte";

let {
consent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ vi.mock("$lib/api/client", async (importOriginal) => {

import { decideConsent } from "$lib/api/client";
import ConsentCard from "./ConsentCard.svelte";
import { createBridgeWorkspace } from "$lib/bridge/workspace.svelte";
import { createBridgeWorkspace } from "$lib/shell/state/bridge-workspace.svelte";

const consent: ConsentCardModel = {
args: { target: "chat:local" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ import {
cancelBridgeRun, createBridgeSession, decideConsent, getAtlasReferences,
getBridgeSnapshot, getOrbRun, getRunSnapshot, listenToRun, sendBridgeMessage
} from "$lib/api/client";
import { bridgeWorkspaceKey, createBridgeWorkspace } from "$lib/bridge/workspace.svelte";
import BridgeView from "./BridgeView.svelte";
import OrbView from "./OrbView.svelte";
import { bridgeWorkspaceKey, createBridgeWorkspace } from "$lib/shell/state/bridge-workspace.svelte";
import BridgeView from "$lib/pages/bridge/components/BridgeView.svelte";
import OrbView from "$lib/pages/orb/components/OrbView.svelte";

const at = "2026-10-10T12:00:00Z";
const bridgeRead = vi.mocked(getBridgeSnapshot);
Expand Down
105 changes: 105 additions & 0 deletions clients/web/src/lib/components/shared/PageHeader.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!--
Shared page header for Altar instruments and workspaces.

Owns the layout and typography of the opening block (back link, kicker,
title, description, actions) so every page reads the same. Page-specific
extras render through the `children` snippet beneath the description, and
controls render through the `actions` snippet; both keep the caller's
scoped styles because snippets belong to the component that declares them.
-->
<script lang="ts">
import type { Snippet } from "svelte";

let {
title,
kicker,
description,
back,
titleId,
titleFocusable = false,
heading = $bindable(),
children,
actions
}: {
/** Visible level-one heading text. */
title: string;
/** Short uppercase context label above the title. */
kicker?: string;
/** One-sentence explanation below the title. */
description?: string;
/** Optional return link rendered above the kicker. */
back?: { href: string; label: string };
/** Id for the heading, for `aria-labelledby` and programmatic focus. */
titleId?: string;
/** Makes the heading a programmatic focus target (`tabindex="-1"`). */
titleFocusable?: boolean;
/** Bindable reference to the rendered heading element. */
heading?: HTMLHeadingElement;
/** Extra identity content rendered beneath the description. */
children?: Snippet;
/** Controls aligned to the end of the header. */
actions?: Snippet;
} = $props();

// A spread keeps the a11y lint from treating the dynamic value as a tab stop;
// the heading is only ever a programmatic (-1) focus target.
let focusTarget = $derived(titleFocusable ? { tabindex: -1 } : {});
</script>

<header class="page-header">
<div class="page-header__identity">
{#if back}<a class="page-header__back" href={back.href}>{back.label}</a>{/if}
{#if kicker}<span class="instrument-kicker">{kicker}</span>{/if}
<h1 class="page-header__title" id={titleId} {...focusTarget} bind:this={heading}>{title}</h1>
{#if description}<p class="page-header__description">{description}</p>{/if}
{@render children?.()}
</div>
{#if actions}<div class="page-header__actions">{@render actions()}</div>{/if}
</header>

<style>
.page-header {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 1rem 2rem;
min-width: 0;
padding-bottom: 1.25rem;
border-bottom: 1px solid var(--color-engraving);
}
.page-header__identity { flex: 1 1 24rem; min-width: 0; }
.page-header__back {
display: inline-block;
margin-bottom: 0.6rem;
padding-block: 0.2rem;
color: var(--color-ash);
font-size: var(--text-xs);
text-underline-offset: 0.2em;
}
.page-header__back:hover { color: var(--color-rune); }
.instrument-kicker { margin-bottom: 0.35rem; }
.page-header__title {
margin: 0;
color: var(--color-bone);
font: 600 clamp(1.5rem, 2.4vw, 2rem)/1.2 var(--font-body);
font-variant: normal;
letter-spacing: -0.03em;
overflow-wrap: anywhere;
}
.page-header__description {
max-width: 48rem;
margin: 0.6rem 0 0;
color: var(--color-parchment);
font-size: var(--text-sm);
line-height: 1.6;
}
.page-header__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
min-width: 0;
}
.page-header :is(a, h1):focus-visible { outline: 2px solid var(--color-rune); outline-offset: 3px; }
</style>
9 changes: 9 additions & 0 deletions clients/web/src/lib/graph/model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { LoomView, OrbRunSnapshot } from "$lib/api/models";

/** Immutable semantic input shared by the Loom and Orb renderer adapters.
* Coordinates and renderer drafts never become Pattern or Run authority.
*/
Expand Down Expand Up @@ -32,3 +34,10 @@ export interface GraphProjection {
readonly nodes: readonly GraphNode[];
readonly edges: readonly GraphEdge[];
}

/** Whether a Run snapshot exactly matches this Pattern id, revision and digest. */
export function matchesOrbPattern(snapshot: OrbRunSnapshot, pattern: LoomView): boolean {
return snapshot.pattern.exact && snapshot.pattern.digest !== null &&
snapshot.pattern.pattern_id === pattern.pattern_id && snapshot.pattern.revision === pattern.revision &&
snapshot.pattern.digest === pattern.digest;
}
Loading
Loading