@peasant-labs/fairtrade 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +202 -21
  2. package/dist/lib/InUseShell-VRWaysXY.js +2488 -0
  3. package/dist/lib/commons.js +801 -836
  4. package/dist/lib/components.css +1 -1
  5. package/dist/lib/graph.css +1 -1
  6. package/dist/lib/graph.js +684 -285
  7. package/dist/lib/types/CommitGraph.d.ts +12 -3
  8. package/dist/lib/types/MapCanvas.d.ts +45 -2
  9. package/dist/lib/types/ProviderIcon.d.ts +23 -20
  10. package/dist/lib/types/commons/providers.d.ts +7 -8
  11. package/dist/lib/types/commons/types.d.ts +3 -6
  12. package/dist/lib/types/graph/Changes.d.ts +8 -4
  13. package/dist/lib/types/graph/CodeMap.d.ts +17 -1
  14. package/dist/lib/types/graph/CodeMapComposition.d.ts +25 -1
  15. package/dist/lib/types/graph/CodeMapNavigator.d.ts +35 -0
  16. package/dist/lib/types/graph/changeGraph.d.ts +12 -0
  17. package/dist/lib/types/graph/codeMapState.d.ts +136 -0
  18. package/dist/lib/types/graph/index.d.ts +15 -1
  19. package/dist/lib/types/graph/timelineNavigation.d.ts +33 -0
  20. package/dist/lib/types/graph/types.d.ts +67 -440
  21. package/dist/lib/types/index.d.ts +2 -0
  22. package/dist/lib/types/provider-policy.d.ts +27 -0
  23. package/dist/lib/types/transcript/TranscriptViewer.d.ts +1 -1
  24. package/dist/lib/types/transcript/adapter.d.ts +5 -40
  25. package/dist/lib/types/transcript/graph/GraphLegend.d.ts +3 -3
  26. package/dist/lib/types/transcript/graph/GraphTurnNode.d.ts +2 -2
  27. package/dist/lib/types/transcript/index.d.ts +3 -1
  28. package/dist/lib/types/transcript/initial-position.d.ts +80 -0
  29. package/dist/lib/types/transcript/state-capabilities.d.ts +22 -1
  30. package/dist/lib/types/transcript/useTranscriptInitialPosition.d.ts +26 -0
  31. package/dist/lib/types/transcript/view-model.d.ts +16 -16
  32. package/dist/lib/types/transcript/wire-types.d.ts +46 -308
  33. package/dist/lib/{ui-TG4XLhWL.js → ui-CKqVB4x2.js} +3395 -3401
  34. package/dist/lib/ui.js +4 -4
  35. package/package.json +51 -26
  36. package/dist/lib/InUseShell-CjtqHauP.js +0 -1532
@@ -6,6 +6,7 @@
6
6
  * @property {string} message - the commit subject (USER CONTENT — case preserved)
7
7
  * @property {string} [branch] - branch name for the chip (USER CONTENT — case preserved)
8
8
  * @property {boolean} [session] - a recorded AI session sits behind this commit (-> filled + sparkle)
9
+ * @property {import('./graph/types.js').TimelineSessionPayload[]} [sessionRefs] - named sessions bound to this commit
9
10
  * @property {boolean} [merged] - this commit merged a branch back in (-> merged chip)
10
11
  * @property {boolean} [tip] - this commit is a branch tip (-> small tip affordance)
11
12
  * @property {string} [time] - relative time label (e.g. "8m ago") — already humanised by the caller
@@ -16,16 +17,20 @@
16
17
  * @param {object} props
17
18
  * @param {Commit[]} props.commits - the history, newest first; lane 0 is the main line.
18
19
  * @param {string} [props.selectedId] - id of the active/selected row (the scarce amber treatment).
19
- * @param {(commit: Commit) => void} [props.onSelect] - called with the commit when a row is chosen.
20
+ * @param {(action: import('./graph/timelineNavigation.js').TimelineNavigationAction) => void} [props.onNavigate] - canonical semantic action callback; when present, legacy callbacks do not fire.
21
+ * @param {(commit: Commit) => void} [props.onSelect] - deprecated legacy row callback; use onNavigate.
22
+ * @param {(sessionId: string) => void} [props.onOpenSession] - deprecated legacy session callback; use onNavigate.
20
23
  * @param {boolean} [props.hasMore=false] - more history exists below the window -> show the "show older" ghost.
21
- * @param {() => void} [props.onShowOlder] - called when "show older" is pressed.
24
+ * @param {() => void} [props.onShowOlder] - deprecated legacy pagination callback; use onNavigate.
22
25
  * @param {string} [props.label='commit history'] - accessible name for the list (lowercase chrome).
23
26
  * @param {string} [props.className] - extra classes appended to the root.
24
27
  */
25
- export default function CommitGraph({ commits, selectedId, onSelect, hasMore, onShowOlder, label, className, ...rest }: {
28
+ export default function CommitGraph({ commits, selectedId, onNavigate, onSelect, onOpenSession, hasMore, onShowOlder, label, className, ...rest }: {
26
29
  commits: Commit[];
27
30
  selectedId?: string;
31
+ onNavigate?: (action: import("./graph/timelineNavigation.js").TimelineNavigationAction) => void;
28
32
  onSelect?: (commit: Commit) => void;
33
+ onOpenSession?: (sessionId: string) => void;
29
34
  hasMore?: boolean;
30
35
  onShowOlder?: () => void;
31
36
  label?: string;
@@ -56,6 +61,10 @@ export type Commit = {
56
61
  * - a recorded AI session sits behind this commit (-> filled + sparkle)
57
62
  */
58
63
  session?: boolean;
64
+ /**
65
+ * - named sessions bound to this commit
66
+ */
67
+ sessionRefs?: import("./graph/types.js").TimelineSessionPayload[];
59
68
  /**
60
69
  * - this commit merged a branch back in (-> merged chip)
61
70
  */
@@ -23,7 +23,8 @@
23
23
  */
24
24
  /**
25
25
  * MapCanvas — an interactive code-structure map. Pan (drag), zoom (square controls /
26
- * +/- keys, never wheel), semantic zoom (overview / folders / files) with edge
26
+ * +/- keys / ctrl-or-cmd+wheel — a plain wheel always just scrolls the page),
27
+ * semantic zoom (overview / folders / files) with edge
27
28
  * ancestor-lifting, a minimap, and a node search combobox. Square metric-encoded
28
29
  * nodes (width ∝ LOC, monochrome coverage fill, folder/file icon, amber selection +
29
30
  * marker, clay violation badge) and square orthogonal edges (solid structure /
@@ -34,6 +35,8 @@
34
35
  * @param {{nodes: MapNode[], edges?: MapEdge[]}} [props.data] the graph
35
36
  * @param {'overview'|'folders'|'files'} [props.grain='folders'] initial semantic zoom
36
37
  * @param {Iterable<string>} [props.expandedIds] expanded node ids
38
+ * @param {Iterable<string>} [props.visibleIds] canonical derived visible node ids
39
+ * @param {import('./graph/codeMapState.js').CodeMapHierarchy} [props.hierarchy] canonical validated hierarchy
37
40
  * @param {string|null} [props.selectedId] selected node id
38
41
  * @param {Iterable<string>} [props.highlightedIds] host hover/relay ids
39
42
  * @param {Record<string,'new'|'removed'>} [props.nodeDeltas] per-node review deltas
@@ -45,14 +48,42 @@
45
48
  * @param {number|string} [props.height=520] canvas height
46
49
  * @param {string} [props.ariaLabel='code map'] application label
47
50
  * @param {string} [props.className] extra container class
51
+ * @param {{scale:number, panX:number, panY:number}|null} [props.viewport] controlled/restorable spatial viewport.
52
+ * In `canonicalControlled` mode this is the SOLE source of truth once a
53
+ * gesture ends: MapCanvas renders it directly and does not keep its own
54
+ * copy. CONTRACT: when a pan/zoom gesture ends, MapCanvas calls
55
+ * `onViewportChange`/`onStateAction` with the proposed viewport, and the
56
+ * owner is expected to feed that value straight back through this prop
57
+ * PROMPTLY (a same-tick/next-render `useState` update, as peasant's real
58
+ * integration does, is the reference case — not a debounce, network
59
+ * round-trip, or anything else that can take an unbounded amount of time).
60
+ * During the gesture itself MapCanvas tracks the pointer locally so the
61
+ * drag/zoom stays smooth without waiting on this round-trip; it holds that
62
+ * local preview for up to ~500ms after the gesture ends waiting for
63
+ * `viewport` to reflect (or nearly reflect — the owner may legitimately
64
+ * clamp scale/pan bounds) the proposal, THEN falls back to whatever this
65
+ * prop says. An owner that never updates `viewport` in response (rejects
66
+ * the change, or simply never wires the callback) will see the gesture
67
+ * held for that ~500ms window and then discarded — a visible snap-back to
68
+ * the pre-gesture position, not a smooth revert. An owner that reconciles
69
+ * slower than ~500ms (e.g. a debounce or a network-backed proposal) will
70
+ * see the SAME snap-back-then-jump-to-late-value symptom the pointer-drag
71
+ * fix in this file exists to prevent — reconcile synchronously.
72
+ * @param {(viewport:{scale:number, panX:number, panY:number})=>void} [props.onViewportChange]
73
+ * @param {(action:import('./graph/codeMapState.js').CodeMapAction)=>void} [props.onStateAction] one semantic canvas gesture
74
+ * @param {boolean} [props.canonicalControlled=false] canonical values render directly; gestures emit proposals only.
75
+ * See the `viewport` prop doc above for the synchronous-reconciliation
76
+ * contract this mode requires of its owner.
48
77
  */
49
- export default function MapCanvas({ data, grain: grainProp, expandedIds, selectedId: selectedIdProp, highlightedIds, nodeDeltas, edgeDeltas, onSelect, onGrainChange, onExpandedIdsChange, onExpand, height, ariaLabel, className, }: {
78
+ export default function MapCanvas({ data, grain: grainProp, expandedIds, visibleIds: canonicalVisibleIds, hierarchy: canonicalHierarchy, selectedId: selectedIdProp, highlightedIds, nodeDeltas, edgeDeltas, onSelect, onGrainChange, onExpandedIdsChange, onExpand, height, ariaLabel, className, viewport, onViewportChange, onStateAction, canonicalControlled, }: {
50
79
  data?: {
51
80
  nodes: MapNode[];
52
81
  edges?: MapEdge[];
53
82
  };
54
83
  grain?: "overview" | "folders" | "files";
55
84
  expandedIds?: Iterable<string>;
85
+ visibleIds?: Iterable<string>;
86
+ hierarchy?: import("./graph/codeMapState.js").CodeMapHierarchy;
56
87
  selectedId?: string | null;
57
88
  highlightedIds?: Iterable<string>;
58
89
  nodeDeltas?: Record<string, "new" | "removed">;
@@ -64,6 +95,18 @@ export default function MapCanvas({ data, grain: grainProp, expandedIds, selecte
64
95
  height?: number | string;
65
96
  ariaLabel?: string;
66
97
  className?: string;
98
+ viewport?: {
99
+ scale: number;
100
+ panX: number;
101
+ panY: number;
102
+ } | null;
103
+ onViewportChange?: (viewport: {
104
+ scale: number;
105
+ panX: number;
106
+ panY: number;
107
+ }) => void;
108
+ onStateAction?: (action: import("./graph/codeMapState.js").CodeMapAction) => void;
109
+ canonicalControlled?: boolean;
67
110
  }): any;
68
111
  export type MapNode = {
69
112
  /**
@@ -7,20 +7,20 @@
7
7
  * the surrounding text color — but remember the accent is never the sole signal.
8
8
  *
9
9
  * @param {object} props
10
- * @param {'claude-code'|'gemini-cli'|'codex'|'opencode'|'cursor'} props.harness
10
+ * @param {Harness} props.harness
11
11
  * @param {number} [props.size] explicit px; omit to inherit the contextual icon size.
12
12
  * @param {boolean} [props.accent] tint with PROVIDER_ACCENT[harness] (default: currentColor).
13
13
  * @param {boolean|string} [props.label] true → the provider name as aria-label; a string overrides.
14
14
  * @param {string} [props.className] extra classes appended after `pv-icon`.
15
- * @returns {JSX.Element|null} null when `harness` is unknown.
15
+ * @returns {JSX.Element}
16
16
  */
17
17
  export function ProviderIcon({ harness, size, accent, label, className, ...rest }: {
18
- harness: "claude-code" | "gemini-cli" | "codex" | "opencode" | "cursor";
18
+ harness: Harness;
19
19
  size?: number;
20
20
  accent?: boolean;
21
21
  label?: boolean | string;
22
22
  className?: string;
23
- }): JSX.Element | null;
23
+ }): JSX.Element;
24
24
  /**
25
25
  * ProviderTag — a square hairline chip: the real mark + the lowercase harness slug.
26
26
  *
@@ -29,16 +29,16 @@ export function ProviderIcon({ harness, size, accent, label, className, ...rest
29
29
  * monochrome it names the provider in words. pass `accent` to tint the mark.
30
30
  *
31
31
  * @param {object} props
32
- * @param {'claude-code'|'gemini-cli'|'codex'|'opencode'|'cursor'} props.harness
32
+ * @param {Harness} props.harness
33
33
  * @param {boolean} [props.accent] tint the mark with the provider accent (default: ink).
34
34
  * @param {string} [props.className]
35
- * @returns {JSX.Element|null}
35
+ * @returns {JSX.Element}
36
36
  */
37
37
  export function ProviderTag({ harness, accent, className, ...rest }: {
38
- harness: "claude-code" | "gemini-cli" | "codex" | "opencode" | "cursor";
38
+ harness: Harness;
39
39
  accent?: boolean;
40
40
  className?: string;
41
- }): JSX.Element | null;
41
+ }): JSX.Element;
42
42
  /**
43
43
  * ProviderName — mark + the harness slug as a plain inline label (no chip border).
44
44
  *
@@ -47,16 +47,16 @@ export function ProviderTag({ harness, accent, className, ...rest }: {
47
47
  * non-color cue regardless — it is real text, so it is readable mono-color too).
48
48
  *
49
49
  * @param {object} props
50
- * @param {'claude-code'|'gemini-cli'|'codex'|'opencode'|'cursor'} props.harness
50
+ * @param {Harness} props.harness
51
51
  * @param {boolean} [props.accent] tint mark + label with the provider accent.
52
52
  * @param {string} [props.className]
53
- * @returns {JSX.Element|null}
53
+ * @returns {JSX.Element}
54
54
  */
55
55
  export function ProviderName({ harness, accent, className, ...rest }: {
56
- harness: "claude-code" | "gemini-cli" | "codex" | "opencode" | "cursor";
56
+ harness: Harness;
57
57
  accent?: boolean;
58
58
  className?: string;
59
- }): JSX.Element | null;
59
+ }): JSX.Element;
60
60
  /**
61
61
  * AccentLegend — the per-provider accent map, documented as a UI.
62
62
  *
@@ -73,12 +73,15 @@ export function ProviderName({ harness, accent, className, ...rest }: {
73
73
  export function AccentLegend({ className, ...rest }: {
74
74
  className?: string;
75
75
  }): JSX.Element;
76
- export const HARNESSES: string[];
77
- export const PROVIDER_ACCENT: {
78
- 'claude-code': string;
79
- 'gemini-cli': string;
80
- codex: string;
81
- opencode: string;
82
- cursor: string;
83
- };
76
+ /** ProviderIcon — the real brand mark for a harness, single-color via currentColor. */
77
+ /** @typedef {import('@peasant-labs/schema').Harness} Harness */
78
+ /** @type {readonly Harness[]} */
79
+ export const HARNESSES: readonly Harness[];
80
+ /**
81
+ * @deprecated Use providerAccent(harness), which validates present values at
82
+ * the canonical schema boundary before returning an accent.
83
+ * @type {Readonly<Record<Harness, 'amber'|'teal'|'olive'|'mauve'|'clay'>>}
84
+ */
85
+ export const PROVIDER_ACCENT: Readonly<Record<Harness, "amber" | "teal" | "olive" | "mauve" | "clay">>;
84
86
  export default ProviderIcon;
87
+ export type Harness = import("@peasant-labs/schema").Harness;
@@ -1,8 +1,7 @@
1
- export function formatProvider(provider: any): string;
2
- export function providerLabel(provider: any): any;
3
- export const PROVIDER_LABEL: {
4
- 'claude-code': string;
5
- 'gemini-cli': string;
6
- opencode: string;
7
- codex: string;
8
- };
1
+ /** Format explicitly non-Harness prose. Never use this as a Harness fallback.
2
+ * @param {unknown} provider
3
+ */
4
+ export function formatProvider(provider: unknown): string;
5
+ /** @param {import('@peasant-labs/schema').Harness} provider */
6
+ export function providerLabel(provider: import("@peasant-labs/schema").Harness): string;
7
+ export const PROVIDER_LABEL: Readonly<Record<"cursor" | "claude-code" | "gemini-cli" | "codex" | "opencode" | "antigravity", string>>;
@@ -129,10 +129,7 @@ export type ExploreTranscriptPayload = {
129
129
  id: string;
130
130
  title: string | null;
131
131
  visibility: TranscriptVisibility;
132
- /**
133
- * e.g. "claude-code", "opencode"
134
- */
135
- modelProvider: string;
132
+ modelProvider: import("@peasant-labs/schema").Harness;
136
133
  modelName: string | null;
137
134
  harnessVersion: string | null;
138
135
  /**
@@ -241,7 +238,7 @@ export type CollectiveTranscriptPayload = {
241
238
  id: string;
242
239
  title: string | null;
243
240
  visibility: TranscriptVisibility;
244
- modelProvider: string;
241
+ modelProvider: import("@peasant-labs/schema").Harness;
245
242
  modelName: string | null;
246
243
  /**
247
244
  * ISO-8601 datetime or null
@@ -268,7 +265,7 @@ export type CollectiveStatsPayload = {
268
265
  * Mirrors GroupModelBreakdown from frontend/src/lib/types.ts.
269
266
  */
270
267
  export type CollectiveModelBreakdownPayload = {
271
- modelProvider: string;
268
+ modelProvider: import("@peasant-labs/schema").Harness;
272
269
  transcriptCount: number;
273
270
  };
274
271
  /**
@@ -3,19 +3,23 @@
3
3
  * @param {import('./types.js').ChangesPayload} props.payload the cooked changes payload (adapter output)
4
4
  * @param {string} [props.projectLabel] the head label (e.g. "peasant-labs/peasant"); host-supplied (not a payload field)
5
5
  * @param {string} [props.selectedId] id of the active commit/tip row (the scarce amber treatment)
6
- * @param {(commit: import('./changeGraph.js').CommitGraphRow) => void} [props.onSelectChange] a row was chosen (open its change detail)
7
- * @param {() => void} [props.onOpenMap] the "open the map" affordance was pressed
6
+ * @param {(action: import('./timelineNavigation.js').TimelineNavigationAction) => void} [props.onNavigate] canonical semantic action callback; when present, legacy callbacks do not fire
7
+ * @param {(commit: import('./changeGraph.js').CommitGraphRow) => void} [props.onSelectChange] deprecated legacy row callback; use onNavigate.
8
+ * @param {() => void} [props.onOpenMap] deprecated legacy map callback; use onNavigate.
8
9
  * @param {boolean} [props.hasMore] more history exists below the window (→ "show older")
9
- * @param {() => void} [props.onShowOlder] "show older" was pressed
10
+ * @param {() => void} [props.onShowOlder] deprecated legacy pagination callback; use onNavigate.
11
+ * @param {(sessionId: string) => void} [props.onOpenSession] deprecated legacy session callback; use onNavigate.
10
12
  * @param {number} [props.nowMs] pins relative-time humanisation (default Date.now()); the harness fixes it for byte-stable captures
11
13
  */
12
- export default function Changes({ payload, projectLabel, selectedId, onSelectChange, onOpenMap, hasMore, onShowOlder, nowMs, }: {
14
+ export default function Changes({ payload, projectLabel, selectedId, onNavigate, onSelectChange, onOpenMap, hasMore, onShowOlder, onOpenSession, nowMs, }: {
13
15
  payload: import("./types.js").ChangesPayload;
14
16
  projectLabel?: string;
15
17
  selectedId?: string;
18
+ onNavigate?: (action: import("./timelineNavigation.js").TimelineNavigationAction) => void;
16
19
  onSelectChange?: (commit: import("./changeGraph.js").CommitGraphRow) => void;
17
20
  onOpenMap?: () => void;
18
21
  hasMore?: boolean;
19
22
  onShowOlder?: () => void;
23
+ onOpenSession?: (sessionId: string) => void;
20
24
  nowMs?: number;
21
25
  }): any;
@@ -18,8 +18,12 @@
18
18
  * @param {number|string} [props.height]
19
19
  * @param {string} [props.ariaLabel]
20
20
  * @param {string} [props.className]
21
+ * @param {{scale:number, panX:number, panY:number}|null} [props.viewport]
22
+ * @param {(viewport:{scale:number, panX:number, panY:number})=>void} [props.onViewportChange]
23
+ * @param {import('./codeMapState.js').CodeMapState} [props.state]
24
+ * @param {(state:import('./codeMapState.js').CodeMapState)=>void} [props.onStateChange]
21
25
  */
22
- export default function CodeMap({ payload, zoom, onZoomChange, selectedId, onSelect, onExpand, highlightedIds, nodeDeltas, structureEdgeDeltas, height, ariaLabel, className, }: {
26
+ export default function CodeMap({ payload, zoom, onZoomChange, selectedId, onSelect, onExpand, highlightedIds, nodeDeltas, structureEdgeDeltas, height, ariaLabel, className, viewport, onViewportChange, state, onStateChange, }: {
23
27
  payload: import("./types.js").CodeMapPayload;
24
28
  zoom?: {
25
29
  level: "project" | "package" | "file";
@@ -38,6 +42,18 @@ export default function CodeMap({ payload, zoom, onZoomChange, selectedId, onSel
38
42
  height?: number | string;
39
43
  ariaLabel?: string;
40
44
  className?: string;
45
+ viewport?: {
46
+ scale: number;
47
+ panX: number;
48
+ panY: number;
49
+ } | null;
50
+ onViewportChange?: (viewport: {
51
+ scale: number;
52
+ panX: number;
53
+ panY: number;
54
+ }) => void;
55
+ state?: import("./codeMapState.js").CodeMapState;
56
+ onStateChange?: (state: import("./codeMapState.js").CodeMapState) => void;
41
57
  }): any;
42
58
  /**
43
59
  * @param {import('./types.js').CodeMapPayload | undefined} payload
@@ -45,8 +45,16 @@
45
45
  * @param {React.CSSProperties} [props.canvasWrapperStyle] inline style on the canvas region wrapper
46
46
  * (e.g. a host's own responsive height clamp)
47
47
  * @param {string} [props.className] extra class on the root
48
+ * @param {'canvas'|'navigator'} [props.presentation='canvas'] additive presentation switch; omitted preserves the historical canvas DOM
49
+ * @param {React.ReactNode} [props.navigatorSlot] host-controlled navigator content
50
+ * @param {(presentation:'canvas'|'navigator')=>void} [props.onPresentationChange] when present, renders the canonical browse/map switch
51
+ * @param {{scale:number, panX:number, panY:number}} [props.viewport]
52
+ * @param {(viewport:{scale:number, panX:number, panY:number})=>void} [props.onViewportChange]
53
+ * @param {import('./codeMapState.js').CodeMapState} [props.state] canonical controlled state
54
+ * @param {Partial<import('./codeMapState.js').CodeMapState>} [props.defaultState] initial canonical uncontrolled state
55
+ * @param {(state:import('./codeMapState.js').CodeMapState)=>void} [props.onStateChange]
48
56
  */
49
- export default function CodeMapComposition({ payload, zoom, onZoomChange, selectedId, onSelect, onExpand, highlightedIds, nodeDeltas, structureEdgeDeltas, height, ariaLabel, canvasSlot, rail, toolbar, sheetTitle, sheetMeta, railSide, legend, canvasWrapperClassName, canvasWrapperStyle, className, }: {
57
+ export default function CodeMapComposition({ payload, zoom, onZoomChange, selectedId, onSelect, onExpand, highlightedIds, nodeDeltas, structureEdgeDeltas, height, ariaLabel, canvasSlot, rail, toolbar, sheetTitle, sheetMeta, railSide, legend, canvasWrapperClassName, canvasWrapperStyle, className, presentation, navigatorSlot, onPresentationChange, viewport, onViewportChange, state, defaultState, onStateChange, }: {
50
58
  payload?: import("./types.js").CodeMapPayload;
51
59
  zoom?: {
52
60
  level: "project" | "package" | "file";
@@ -74,6 +82,22 @@ export default function CodeMapComposition({ payload, zoom, onZoomChange, select
74
82
  canvasWrapperClassName?: string;
75
83
  canvasWrapperStyle?: React.CSSProperties;
76
84
  className?: string;
85
+ presentation?: "canvas" | "navigator";
86
+ navigatorSlot?: React.ReactNode;
87
+ onPresentationChange?: (presentation: "canvas" | "navigator") => void;
88
+ viewport?: {
89
+ scale: number;
90
+ panX: number;
91
+ panY: number;
92
+ };
93
+ onViewportChange?: (viewport: {
94
+ scale: number;
95
+ panX: number;
96
+ panY: number;
97
+ }) => void;
98
+ state?: import("./codeMapState.js").CodeMapState;
99
+ defaultState?: Partial<import("./codeMapState.js").CodeMapState>;
100
+ onStateChange?: (state: import("./codeMapState.js").CodeMapState) => void;
77
101
  }): any;
78
102
  /**
79
103
  * The persistent legend explaining the canvas's four orthogonal visual channels
@@ -0,0 +1,35 @@
1
+ /**
2
+ * CodeMapNavigator presents the code tree as a readable, keyboard-operable
3
+ * starting point. Hosts keep selection, expansion, focus, and filter state so
4
+ * a transition to the spatial canvas and back is lossless.
5
+ *
6
+ * @param {object} props
7
+ * @param {import('./types.js').CodeMapPayload} props.payload
8
+ * @param {'project'|'package'|'file'} [props.grain='package']
9
+ * @param {Iterable<string>} [props.expandedIds]
10
+ * @param {(ids:string[])=>void} [props.onExpandedIdsChange]
11
+ * @param {string|null} [props.selectedId]
12
+ * @param {(id:string|null, node:import('./types.js').MapNodePayload|null)=>void} [props.onSelect]
13
+ * @param {string|null} [props.focusedId]
14
+ * @param {(id:string|null)=>void} [props.onFocusChange]
15
+ * @param {string} [props.filter='']
16
+ * @param {(filter:string)=>void} [props.onFilterChange]
17
+ * @param {(id:string)=>void} [props.onOpenMap]
18
+ * @param {(action:import('./codeMapState.js').CodeMapAction)=>void} [props.onStateAction]
19
+ * @param {string} [props.ariaLabel='code map navigator']
20
+ */
21
+ export default function CodeMapNavigator({ payload, grain, expandedIds, onExpandedIdsChange, selectedId, onSelect, focusedId, onFocusChange, filter, onFilterChange, onOpenMap, onStateAction, ariaLabel, }: {
22
+ payload: import("./types.js").CodeMapPayload;
23
+ grain?: "project" | "package" | "file";
24
+ expandedIds?: Iterable<string>;
25
+ onExpandedIdsChange?: (ids: string[]) => void;
26
+ selectedId?: string | null;
27
+ onSelect?: (id: string | null, node: import("./types.js").MapNodePayload | null) => void;
28
+ focusedId?: string | null;
29
+ onFocusChange?: (id: string | null) => void;
30
+ filter?: string;
31
+ onFilterChange?: (filter: string) => void;
32
+ onOpenMap?: (id: string) => void;
33
+ onStateAction?: (action: import("./codeMapState.js").CodeMapAction) => void;
34
+ ariaLabel?: string;
35
+ }): any;
@@ -58,6 +58,10 @@ export type CommitGraphRow = {
58
58
  * a recorded session sits behind this commit (→ filled dot + sparkle)
59
59
  */
60
60
  session?: boolean;
61
+ /**
62
+ * named sessions bound to this commit
63
+ */
64
+ sessionRefs?: import("./types.js").TimelineSessionPayload[];
61
65
  /**
62
66
  * this commit merged a branch back in (→ merged chip)
63
67
  */
@@ -103,4 +107,12 @@ export type ChangesGraph = {
103
107
  * number of merged changes
104
108
  */
105
109
  mergedCount: number;
110
+ /**
111
+ * sessions not linked to a commit in this timeline window
112
+ */
113
+ unlinkedSessions: import("./types.js").TimelineSessionPayload[];
114
+ /**
115
+ * sessions linked to a commit outside the displayed default-branch window
116
+ */
117
+ outsideWindowSessions: import("./types.js").TimelineSessionPayload[];
106
118
  };
@@ -0,0 +1,136 @@
1
+ /** @param {unknown} value @returns {value is number} */
2
+ export function isCodeMapViewportScale(value: unknown): value is number;
3
+ /**
4
+ * Creates a canonical, JSON-safe code-map interaction state.
5
+ * @param {Partial<CodeMapState>|null} [seed]
6
+ * @returns {CodeMapState}
7
+ */
8
+ export function createCodeMapState(seed?: Partial<CodeMapState> | null): CodeMapState;
9
+ /** @param {CodeMapState} left @param {CodeMapState} right */
10
+ export function codeMapStatesEqual(left: CodeMapState, right: CodeMapState): boolean;
11
+ /**
12
+ * Applies one semantic interaction and returns a fresh canonical state.
13
+ * @param {CodeMapState} state
14
+ * @param {CodeMapAction} action
15
+ * @returns {CodeMapState}
16
+ */
17
+ export function reduceCodeMapState(state: CodeMapState, action: CodeMapAction): CodeMapState;
18
+ /**
19
+ * Derives the complete navigator and canvas inputs from payload plus canonical state.
20
+ * Returned nodes are plain public payload data; no private indexes or depths are attached.
21
+ * @param {import('./types.js').CodeMapPayload|undefined|null} payload
22
+ * @param {CodeMapState} state
23
+ * @returns {CodeMapView}
24
+ */
25
+ export function deriveCodeMapView(payload: import("./types.js").CodeMapPayload | undefined | null, state: CodeMapState): CodeMapView;
26
+ /** @typedef {import('./types.js').MapNodePayload} MapNodePayload */
27
+ /** @typedef {{type:'replace'|'hydrate', state:Partial<CodeMapState>} | {type:'set-presentation', presentation:CodeMapPresentation} | {type:'select'|'toggle-expanded'|'open-in-map', id:string} | {type:'focus', id:string|null} | {type:'clear-selection'} | {type:'set-filter', filter:string} | {type:'set-expanded', ids:Iterable<string>} | {type:'set-grain', grain:CodeMapGrain} | {type:'set-viewport', viewport:CodeMapViewport|null} | {type:'reveal', id:string, grain:CodeMapGrain, expandedIds:Iterable<string>}} CodeMapAction */
28
+ /**
29
+ * @typedef {object} CodeMapState
30
+ * @property {1} version
31
+ * @property {CodeMapPresentation} presentation
32
+ * @property {string|null} selectedId
33
+ * @property {CodeMapGrain} grain
34
+ * @property {string[]} expandedIds
35
+ * @property {string} navigatorFilter
36
+ * @property {string|null} navigatorFocusedId
37
+ * @property {CodeMapViewport|null} viewport
38
+ */
39
+ /** @typedef {{node:import('./types.js').MapNodePayload, depth:number, parentId:string|null, childIds:string[], hasChildren:boolean, canExpand:boolean, forcedOpen:boolean, expanded:boolean}} CodeMapNavigatorRow */
40
+ /** @typedef {{orderedIds:string[], rootIds:string[], childIdsByParent:Record<string,string[]>, depthById:Record<string,number>}} CodeMapHierarchy */
41
+ /** @typedef {{allNodes:import('./types.js').MapNodePayload[], nodes:import('./types.js').MapNodePayload[], visibleIds:string[], selectedId:string|null, grain:CodeMapGrain, expandedIds:string[], viewport:CodeMapViewport|null, hierarchy:CodeMapHierarchy}} CodeMapCanvasView */
42
+ /** @typedef {{state:CodeMapState, rows:CodeMapNavigatorRow[], focusedId:string|null, selected:import('./types.js').MapNodePayload|null, staleSelectedId:string|null, canvas:CodeMapCanvasView}} CodeMapView */
43
+ export const CODE_MAP_STATE_VERSION: 1;
44
+ export const CODE_MAP_VIEWPORT_SCALE: Readonly<{
45
+ min: 0.35;
46
+ max: 2.4;
47
+ }>;
48
+ export const CODE_MAP_GRAIN_DEPTH: Readonly<{
49
+ project: 0;
50
+ package: 1;
51
+ file: 1;
52
+ }>;
53
+ export type CodeMapPresentation = "navigator" | "canvas";
54
+ export type CodeMapGrain = "project" | "package" | "file";
55
+ export type CodeMapViewport = {
56
+ scale: number;
57
+ panX: number;
58
+ panY: number;
59
+ };
60
+ export type MapNodePayload = import("./types.js").MapNodePayload;
61
+ export type CodeMapAction = {
62
+ type: "replace" | "hydrate";
63
+ state: Partial<CodeMapState>;
64
+ } | {
65
+ type: "set-presentation";
66
+ presentation: CodeMapPresentation;
67
+ } | {
68
+ type: "select" | "toggle-expanded" | "open-in-map";
69
+ id: string;
70
+ } | {
71
+ type: "focus";
72
+ id: string | null;
73
+ } | {
74
+ type: "clear-selection";
75
+ } | {
76
+ type: "set-filter";
77
+ filter: string;
78
+ } | {
79
+ type: "set-expanded";
80
+ ids: Iterable<string>;
81
+ } | {
82
+ type: "set-grain";
83
+ grain: CodeMapGrain;
84
+ } | {
85
+ type: "set-viewport";
86
+ viewport: CodeMapViewport | null;
87
+ } | {
88
+ type: "reveal";
89
+ id: string;
90
+ grain: CodeMapGrain;
91
+ expandedIds: Iterable<string>;
92
+ };
93
+ export type CodeMapState = {
94
+ version: 1;
95
+ presentation: CodeMapPresentation;
96
+ selectedId: string | null;
97
+ grain: CodeMapGrain;
98
+ expandedIds: string[];
99
+ navigatorFilter: string;
100
+ navigatorFocusedId: string | null;
101
+ viewport: CodeMapViewport | null;
102
+ };
103
+ export type CodeMapNavigatorRow = {
104
+ node: import("./types.js").MapNodePayload;
105
+ depth: number;
106
+ parentId: string | null;
107
+ childIds: string[];
108
+ hasChildren: boolean;
109
+ canExpand: boolean;
110
+ forcedOpen: boolean;
111
+ expanded: boolean;
112
+ };
113
+ export type CodeMapHierarchy = {
114
+ orderedIds: string[];
115
+ rootIds: string[];
116
+ childIdsByParent: Record<string, string[]>;
117
+ depthById: Record<string, number>;
118
+ };
119
+ export type CodeMapCanvasView = {
120
+ allNodes: import("./types.js").MapNodePayload[];
121
+ nodes: import("./types.js").MapNodePayload[];
122
+ visibleIds: string[];
123
+ selectedId: string | null;
124
+ grain: CodeMapGrain;
125
+ expandedIds: string[];
126
+ viewport: CodeMapViewport | null;
127
+ hierarchy: CodeMapHierarchy;
128
+ };
129
+ export type CodeMapView = {
130
+ state: CodeMapState;
131
+ rows: CodeMapNavigatorRow[];
132
+ focusedId: string | null;
133
+ selected: import("./types.js").MapNodePayload | null;
134
+ staleSelectedId: string | null;
135
+ canvas: CodeMapCanvasView;
136
+ };
@@ -1,11 +1,14 @@
1
1
  export { default as Changes } from "./Changes.jsx";
2
2
  export { default as ChangeDetail } from "./ChangeDetail.jsx";
3
+ export { default as CodeMapNavigator } from "./CodeMapNavigator.jsx";
4
+ export { assertTimelineNavigationAction } from "./timelineNavigation.js";
3
5
  export type MapNodeKind = import("./types.js").MapNodeKind;
4
6
  export type ChangeBinding = import("./types.js").ChangeBinding;
5
7
  export type EdgeViolationKind = import("./types.js").EdgeViolationKind;
6
8
  export type FileChangeStatus = import("./types.js").FileChangeStatus;
7
9
  export type DiffLineKind = import("./types.js").DiffLineKind;
8
10
  export type CommitRefPayload = import("./types.js").CommitRefPayload;
11
+ export type TimelineSessionPayload = import("./types.js").TimelineSessionPayload;
9
12
  export type MapEdgePayload = import("./types.js").MapEdgePayload;
10
13
  export type ActivityEdgePayload = import("./types.js").ActivityEdgePayload;
11
14
  export type EdgeViolationPayload = import("./types.js").EdgeViolationPayload;
@@ -23,8 +26,19 @@ export type DiffLinePayload = import("./types.js").DiffLinePayload;
23
26
  export type DiffHunkPayload = import("./types.js").DiffHunkPayload;
24
27
  export type ChangeDiffPayload = import("./types.js").ChangeDiffPayload;
25
28
  export type CodeMapPayload = import("./types.js").CodeMapPayload;
29
+ export type CodeMapPresentation = import("./codeMapState.js").CodeMapPresentation;
30
+ export type CodeMapGrain = import("./codeMapState.js").CodeMapGrain;
31
+ export type CodeMapViewport = import("./codeMapState.js").CodeMapViewport;
32
+ export type CodeMapState = import("./codeMapState.js").CodeMapState;
33
+ export type CodeMapAction = import("./codeMapState.js").CodeMapAction;
34
+ export type CodeMapNavigatorRow = import("./codeMapState.js").CodeMapNavigatorRow;
35
+ export type CodeMapCanvasView = import("./codeMapState.js").CodeMapCanvasView;
36
+ export type CodeMapView = import("./codeMapState.js").CodeMapView;
37
+ export type TimelineNavigationAction = import("./timelineNavigation.js").TimelineNavigationAction;
26
38
  export { default as CodeMap, codeMapPayloadToMapData } from "./CodeMap.jsx";
27
39
  export { default as CodeMapComposition, DefaultMapLegend } from "./CodeMapComposition.jsx";
40
+ export { CODE_MAP_STATE_VERSION, CODE_MAP_VIEWPORT_SCALE, isCodeMapViewportScale, codeMapStatesEqual, createCodeMapState, reduceCodeMapState, deriveCodeMapView } from "./codeMapState.js";
28
41
  export { GraphAppShell, GraphSectionNav, GRAPH_APP_SECTIONS } from "../inuse/InUseShell.jsx";
29
42
  export { buildChangesGraph, humanizeAge } from "./changeGraph.js";
30
- export { MAP_NODE_KINDS, CHANGE_BINDINGS, EDGE_VIOLATION_KINDS, FILE_CHANGE_STATUSES, DIFF_LINE_KINDS } from "./types.js";
43
+ export { assertChangeDetailPayloadEnums, assertChangeDiffPayloadEnums, assertCodeMapPayloadEnums, MAP_NODE_KINDS, CHANGE_BINDINGS, EDGE_VIOLATION_KINDS, FILE_CHANGE_STATUSES, DIFF_LINE_KINDS } from "./types.js";
44
+ export { MapNodeKind, AllMapNodeKinds, isMapNodeKind, ChangeBinding, AllChangeBindings, isChangeBinding, EdgeViolationKind, AllEdgeViolationKinds, isEdgeViolationKind, FileChangeStatus, AllFileChangeStatuses, isFileChangeStatus, DiffLineKind, AllDiffLineKinds, isDiffLineKind } from "@peasant-labs/schema";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Fail closed at untyped JavaScript boundaries before a host interprets a timeline gesture.
3
+ * The accepted object shapes are exact so additions require an explicit contract change.
4
+ *
5
+ * @param {unknown} value
6
+ * @returns {asserts value is TimelineNavigationAction}
7
+ */
8
+ export function assertTimelineNavigationAction(value: unknown): asserts value is TimelineNavigationAction;
9
+ export type TimelineNavigationAction = {
10
+ type: "open-change";
11
+ change: {
12
+ id: string;
13
+ branch: string | null;
14
+ };
15
+ } | {
16
+ type: "open-session";
17
+ sessionId: string;
18
+ source: {
19
+ kind: "commit";
20
+ commit: {
21
+ id: string;
22
+ branch: string | null;
23
+ };
24
+ } | {
25
+ kind: "unlinked";
26
+ } | {
27
+ kind: "outside-window";
28
+ };
29
+ } | {
30
+ type: "open-map";
31
+ } | {
32
+ type: "show-older";
33
+ };