@deltarobotics/uhd-viewer 0.0.0-stage → 0.2.0

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 (100) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +22 -0
  4. package/README.md +168 -2
  5. package/bin/serve.d.mts +62 -0
  6. package/bin/serve.mjs +257 -0
  7. package/bin/uhd-viewer.mjs +75 -0
  8. package/dist/app/assets/KodeMono-Bold-DdIivhPN.woff2 +0 -0
  9. package/dist/app/assets/KodeMono-Medium-CVnTuv5A.ttf +0 -0
  10. package/dist/app/assets/KodeMono-Regular-Bdr_-rJX.woff2 +0 -0
  11. package/dist/app/assets/KodeMono-SemiBold-JAfn5Dpm.ttf +0 -0
  12. package/dist/app/assets/index-Boi2erhd.css +2 -0
  13. package/dist/app/assets/index-CkpIRYgM.js +3853 -0
  14. package/dist/app/index.html +16 -0
  15. package/dist/components/components.css +6108 -0
  16. package/dist/components/index.d.ts +1 -0
  17. package/dist/components/index.js +26048 -0
  18. package/dist/components/index.js.map +1 -0
  19. package/dist/components/types/uhd-viewer/src/api.d.ts +286 -0
  20. package/dist/components/types/uhd-viewer/src/cad/CadView.d.ts +52 -0
  21. package/dist/components/types/uhd-viewer/src/cad/ModelView.d.ts +28 -0
  22. package/dist/components/types/uhd-viewer/src/cad/explode.d.ts +110 -0
  23. package/dist/components/types/uhd-viewer/src/cad/explodeSample.d.ts +25 -0
  24. package/dist/components/types/uhd-viewer/src/cad/findings.d.ts +41 -0
  25. package/dist/components/types/uhd-viewer/src/cad/labels.d.ts +76 -0
  26. package/dist/components/types/uhd-viewer/src/cad/model.d.ts +132 -0
  27. package/dist/components/types/uhd-viewer/src/cad/row.d.ts +15 -0
  28. package/dist/components/types/uhd-viewer/src/cad/scene.d.ts +95 -0
  29. package/dist/components/types/uhd-viewer/src/cad/scope.d.ts +89 -0
  30. package/dist/components/types/uhd-viewer/src/canvas/Canvas.d.ts +86 -0
  31. package/dist/components/types/uhd-viewer/src/canvas/Icon.d.ts +5 -0
  32. package/dist/components/types/uhd-viewer/src/canvas/LinkEdge.d.ts +40 -0
  33. package/dist/components/types/uhd-viewer/src/canvas/Port.d.ts +18 -0
  34. package/dist/components/types/uhd-viewer/src/canvas/actions.d.ts +5 -0
  35. package/dist/components/types/uhd-viewer/src/canvas/geometry.d.ts +72 -0
  36. package/dist/components/types/uhd-viewer/src/canvas/interaction.d.ts +162 -0
  37. package/dist/components/types/uhd-viewer/src/canvas/nodes.d.ts +43 -0
  38. package/dist/components/types/uhd-viewer/src/canvas/reveal.d.ts +37 -0
  39. package/dist/components/types/uhd-viewer/src/canvas/rightDrag.d.ts +51 -0
  40. package/dist/components/types/uhd-viewer/src/canvas/routing.d.ts +28 -0
  41. package/dist/components/types/uhd-viewer/src/chrome/ArtifactCards.d.ts +30 -0
  42. package/dist/components/types/uhd-viewer/src/chrome/Artifacts.d.ts +44 -0
  43. package/dist/components/types/uhd-viewer/src/chrome/BoardInspector.d.ts +53 -0
  44. package/dist/components/types/uhd-viewer/src/chrome/Chrome.d.ts +116 -0
  45. package/dist/components/types/uhd-viewer/src/chrome/Evidence.d.ts +20 -0
  46. package/dist/components/types/uhd-viewer/src/chrome/InterfaceCards.d.ts +56 -0
  47. package/dist/components/types/uhd-viewer/src/chrome/PairView.d.ts +37 -0
  48. package/dist/components/types/uhd-viewer/src/chrome/Sections.d.ts +107 -0
  49. package/dist/components/types/uhd-viewer/src/components.d.ts +60 -0
  50. package/dist/components/types/uhd-viewer/src/host/channel.d.ts +49 -0
  51. package/dist/components/types/uhd-viewer/src/host/context.d.ts +29 -0
  52. package/dist/components/types/uhd-viewer/src/host/protocol.d.ts +22 -0
  53. package/dist/components/types/uhd-viewer/src/host/targets.d.ts +85 -0
  54. package/dist/components/types/uhd-viewer/src/layout/board-layout.d.ts +69 -0
  55. package/dist/components/types/uhd-viewer/src/layout/card-body.d.ts +64 -0
  56. package/dist/components/types/uhd-viewer/src/layout/layout.d.ts +94 -0
  57. package/dist/components/types/uhd-viewer/src/layout/module-layout.d.ts +32 -0
  58. package/dist/components/types/uhd-viewer/src/layout/router.d.ts +55 -0
  59. package/dist/components/types/uhd-viewer/src/layout/taps.d.ts +20 -0
  60. package/dist/components/types/uhd-viewer/src/model/artifacts.d.ts +129 -0
  61. package/dist/components/types/uhd-viewer/src/model/classification.d.ts +38 -0
  62. package/dist/components/types/uhd-viewer/src/model/diagnostics.d.ts +27 -0
  63. package/dist/components/types/uhd-viewer/src/model/evidence.d.ts +63 -0
  64. package/dist/components/types/uhd-viewer/src/model/from-uhd.d.ts +57 -0
  65. package/dist/components/types/uhd-viewer/src/model/hierarchy.d.ts +169 -0
  66. package/dist/components/types/uhd-viewer/src/model/isolate.d.ts +33 -0
  67. package/dist/components/types/uhd-viewer/src/model/nets.d.ts +53 -0
  68. package/dist/components/types/uhd-viewer/src/model/outputs.d.ts +79 -0
  69. package/dist/components/types/uhd-viewer/src/model/slots.d.ts +182 -0
  70. package/dist/components/types/uhd-viewer/src/model/types.d.ts +213 -0
  71. package/dist/components/types/uhd-viewer/src/options.d.ts +27 -0
  72. package/dist/components/types/uhd-viewer/src/source/source.d.ts +109 -0
  73. package/dist/components/types/uhd-viewer/src/source/tolerant.d.ts +45 -0
  74. package/dist/components/types/uhd-viewer/src/theme/ThemeProvider.d.ts +33 -0
  75. package/dist/components/types/uhd-viewer/src/theme/adapters.d.ts +28 -0
  76. package/dist/components/types/uhd-viewer/src/theme/resolve.d.ts +19 -0
  77. package/dist/components/types/uhd-viewer/src/theme/themes.d.ts +67 -0
  78. package/dist/components/types/uhd-viewer/src/theme/tokens.d.ts +175 -0
  79. package/dist/components/types/uhd-viewer/src/ui/Inspector.d.ts +45 -0
  80. package/dist/components/types/uhd-viewer/src/ui/Legend.d.ts +40 -0
  81. package/dist/components/types/uhd-viewer/src/ui/ScrollArea.d.ts +16 -0
  82. package/dist/components/types/uhd-viewer/src/ui/Toolbar.d.ts +49 -0
  83. package/dist/components/types/uhd-viewer/src/ui/Tooltips.d.ts +3 -0
  84. package/dist/components/types/uhd-viewer/src/ui/cx.d.ts +2 -0
  85. package/dist/components/types/uhd-viewer/src/ui/legendFit.d.ts +42 -0
  86. package/dist/components/types/uhd-viewer/src/viewer/controller.d.ts +218 -0
  87. package/dist/components/types/uhd-viewer/src/viewer/details.d.ts +81 -0
  88. package/dist/components/types/uhd-viewer/src/viewer/hierarchy.d.ts +60 -0
  89. package/dist/components/types/uhd-viewer/src/viewer/mount.d.ts +12 -0
  90. package/dist/components/types/uhd-viewer/src/viewer/views.d.ts +156 -0
  91. package/dist/headless/index.d.ts +71 -0
  92. package/dist/headless/index.js +7425 -0
  93. package/dist/headless/index.js.map +1 -0
  94. package/dist/lib/api.d.ts +252 -0
  95. package/dist/lib/uhd-viewer.css +3 -0
  96. package/dist/lib/uhd-viewer.d.ts +4 -0
  97. package/dist/lib/uhd-viewer.js +97469 -0
  98. package/package.json +110 -4
  99. package/src/styles/fonts/OFL.txt +93 -0
  100. package/src/styles/tailwind-theme.css +48 -0
@@ -0,0 +1,37 @@
1
+ import type { ViewLink, ViewModel } from "../model/types";
2
+ import { type Classification } from "../model/classification";
3
+ /**
4
+ * Pair view (design board 08): every stored link between two modules, left
5
+ * to right, with each link's child links as conductors. Crossovers (TX→RX)
6
+ * draw as crossing lines. Unlinked interfaces are listed below.
7
+ */
8
+ interface Endpoints {
9
+ a: {
10
+ node: string;
11
+ port: string;
12
+ };
13
+ b: {
14
+ node: string;
15
+ port: string;
16
+ };
17
+ }
18
+ /** True endpoints of a link, joining harness segments back together. */
19
+ export declare function pairEnds(vm: ViewModel, link: ViewLink): Endpoints;
20
+ export declare function PairView({ vm, link, mode, onClose }: {
21
+ vm: ViewModel;
22
+ link: ViewLink;
23
+ mode: Classification;
24
+ onClose: () => void;
25
+ }): import("react").JSX.Element | null;
26
+ /**
27
+ * The pair a link joins, small, for the inspector: the two modules and the
28
+ * link's conductors from one to the other (a crossover crosses). `onOpen`
29
+ * opens the full pair view.
30
+ */
31
+ export declare function PairPreview({ vm, link, mode, onOpen }: {
32
+ vm: ViewModel;
33
+ link: ViewLink;
34
+ mode: Classification;
35
+ onOpen: () => void;
36
+ }): import("react").JSX.Element | null;
37
+ export {};
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The inspector's sections (PB-1042, design A "Linear-tight", 2026-10-21):
3
+ * one column, no boxes. Each section is a 28 px header row (chevron, title,
4
+ * count, and a summary on the right) over its body, separated from the next
5
+ * by a 1 px divider. Secondary sections start collapsed and say what they
6
+ * hold in the header. The look is in one place (`SECTION_LOOK`), from the
7
+ * viewer's tokens only, so a host's theme decides the colours.
8
+ *
9
+ * Keyboard: a section header and an interface card are stops of one list
10
+ * (`data-nav`); Arrow Up and Down move between them, Home and End go to the
11
+ * first and the last, Enter and Space open and close (they are buttons).
12
+ */
13
+ import { type KeyboardEvent, type ReactNode } from "react";
14
+ /** The sections' look, in one place: swap these to restyle them. */
15
+ export declare const SECTION_LOOK: {
16
+ readonly root: "uhd-sections font-ui text-fg";
17
+ readonly section: "uhd-section border-b border-line-subtle";
18
+ readonly header: "flex h-7 min-w-0 items-center gap-1.5 pr-3 pl-2.5";
19
+ readonly toggle: "group/sec -my-px flex h-7 min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-[3px] text-left outline-none focus-visible:outline focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-focus";
20
+ readonly chevron: "shrink-0 text-fg-muted transition-transform duration-100";
21
+ readonly title: "shrink-0 text-[11px] leading-none font-bold text-fg";
22
+ readonly count: "shrink-0 text-[11px] leading-none text-fg-faint tabular-nums";
23
+ readonly summary: "ml-auto min-w-0 shrink truncate text-right text-[11px] leading-none text-fg-muted";
24
+ readonly body: "px-3 pt-0.5 pb-2.5";
25
+ /** A key and its value, as the Parameters rows. */
26
+ readonly kv: "flex min-h-[22px] items-baseline gap-2 py-[3px] text-[11px] leading-[1.35]";
27
+ readonly key: "w-[92px] shrink-0 truncate text-fg-faint";
28
+ readonly value: "min-w-0 flex-1 text-fg [overflow-wrap:anywhere]";
29
+ /** A small heading inside a section (an expanded card's groups). */
30
+ readonly label: "text-[10px] leading-none text-fg-faint";
31
+ /** A quiet text action at the right of a header ("Show all 21"). */
32
+ readonly aside: "ml-auto shrink-0 cursor-pointer text-[11px] leading-none text-fg-muted hover:text-fg focus-visible:outline focus-visible:outline-1 focus-visible:outline-focus";
33
+ };
34
+ type Listener = () => void;
35
+ /**
36
+ * Which sections are open and which interface cards are expanded, kept for
37
+ * as long as the page lives (a tab's webview, the viewer's page): sections by
38
+ * their id, cards per selection, so going back to a module shows it as it was.
39
+ */
40
+ declare class KeptState {
41
+ private sections;
42
+ private cards;
43
+ private listeners;
44
+ private version;
45
+ subscribe: (l: Listener) => () => undefined;
46
+ snapshot: () => number;
47
+ private changed;
48
+ section(id: string, fallback: boolean): boolean;
49
+ setSection(id: string, open: boolean): void;
50
+ expanded(selection: string): ReadonlySet<string>;
51
+ setExpanded(selection: string, ids: ReadonlySet<string>): void;
52
+ /** For tests and a host that resets its page. */
53
+ clear(): void;
54
+ }
55
+ /** The page's kept state (one per page). */
56
+ export declare const keptInspectorState: KeptState;
57
+ /** The selection the cards below belong to: their expanded set is kept under it. */
58
+ export declare const SelectionKey: import("react").Context<string>;
59
+ /** Whether a section is open (kept while the page lives), and its setter. */
60
+ export declare function useSectionOpen(id: string, defaultOpen: boolean): [boolean, (open: boolean) => void];
61
+ /** The interface cards expanded for the current selection, and a toggle. Several may be open at once. */
62
+ export declare function useExpandedCards(): [ReadonlySet<string>, (id: string) => void];
63
+ /** Where a key moves focus in a list of `count` stops from `index`; undefined: the key is not a move. */
64
+ export declare function navTarget(key: string, index: number, count: number): number | undefined;
65
+ /** Arrow keys on a stop (`data-nav`) move to the next one within the sections (`data-nav-root`). */
66
+ export declare function onNavKey(e: KeyboardEvent<HTMLElement>): void;
67
+ /**
68
+ * The column the sections are in: its stops are one keyboard list. Inside
69
+ * another (a host's inspector around the viewer's details) it is part of the
70
+ * outer one, so the arrows go through both.
71
+ */
72
+ export declare function Sections({ children, className, label }: {
73
+ children?: ReactNode;
74
+ className?: string;
75
+ label?: string;
76
+ }): import("react").JSX.Element;
77
+ export interface DetailsSectionProps {
78
+ /** Kept open or closed by this id while the page lives. */
79
+ id: string;
80
+ title: string;
81
+ count?: number;
82
+ /** What it holds, said in the header (shown open or closed): "6 linked · 1 open", "DolphinRC · $69.99". */
83
+ summary?: ReactNode;
84
+ /** A text action at the header's right instead of the summary ("Show all 21"). */
85
+ aside?: ReactNode;
86
+ /** Open at first (secondary sections start closed). */
87
+ defaultOpen?: boolean;
88
+ children?: ReactNode;
89
+ className?: string;
90
+ }
91
+ /** A section: its header row (a disclosure button) and its body. */
92
+ export declare function DetailsSection({ id, title, count, summary, aside, defaultOpen, children, className }: DetailsSectionProps): import("react").JSX.Element;
93
+ /** Rows of keys and values; past `limit` the rest wait behind "Show all N" (the header's aside, `ShowAll`). */
94
+ export declare function KeyValues({ rows, limit }: {
95
+ rows: {
96
+ key: string;
97
+ value: ReactNode;
98
+ title?: string;
99
+ }[];
100
+ limit?: number;
101
+ }): import("react").JSX.Element;
102
+ /** A section's rows shown in part, and the header action that shows them all. */
103
+ export declare function useShowAll(total: number, limit: number): {
104
+ limit: number | undefined;
105
+ aside: ReactNode;
106
+ };
107
+ export {};
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The component library (`@deltarobotics/uhd-viewer/components`): the
3
+ * viewer's views of a UHD bundle as React components, for hosts that build
4
+ * their own shell. No app chrome: the shell places the views, its own header,
5
+ * tabs and panels.
6
+ *
7
+ * <ThemeProvider adapter={vscodeThemeAdapter()}>
8
+ * <UhdViewer bundleUrl="/uhd-bundle.json" host={host}>
9
+ * <DiagramCanvas />
10
+ * <ViewerInspector />
11
+ * </UhdViewer>
12
+ * </ThemeProvider>
13
+ *
14
+ * React, React DOM, Three, React Flow and Radix are peer dependencies here
15
+ * (one copy each in the host's bundle). The stylesheet is
16
+ * `@deltarobotics/uhd-viewer/components.css`; a host's own Tailwind build
17
+ * gets the token utilities from `@deltarobotics/uhd-viewer/tailwind-theme.css`.
18
+ * Nothing here names a host product.
19
+ */
20
+ export type * from "./api";
21
+ export { renderViewer } from "./viewer/mount";
22
+ export { parseHostMessage, isViewerEvent, originAllowed, PREFIX as HOST_MESSAGE_PREFIX } from "./host/protocol";
23
+ export { UhdViewer, ViewerBoundary, ViewerProvider, useViewer, useViewerSource, useViewerController, type AppProps as UhdViewerOptions, type UhdViewerProps, type HostLink, type ViewerController } from "./viewer/controller";
24
+ export type { LoadedSource } from "./source/source";
25
+ export { tolerantSystem, isPlaceholder, type MissingDefinition, type LeftOut, type TolerantSystem } from "./source/tolerant";
26
+ export type { Classification } from "./model/classification";
27
+ export { CLASSIFICATIONS, colorFor, legendFor, classKey, type LegendEntry } from "./model/classification";
28
+ export { isolate, presentKeys, linkKeys, toggleIsolated, type Isolation } from "./model/isolate";
29
+ export type { ViewModel, ViewNode, ViewLink } from "./model/types";
30
+ export type { Selection } from "./canvas/Canvas";
31
+ export type { Panel, Goto } from "./chrome/Chrome";
32
+ export { initialInteraction, interactionReducer, interactionProps, canvasCommand, applySelectChanges, firstView, TOOL_OPTIONS, type CanvasTool, type InteractionState, type InteractionAction, type CanvasCommand, type Viewport } from "./canvas/interaction";
33
+ export { openAction } from "./viewer/controller";
34
+ export { cardWidth, labelIsCut, CARD_W, CARD_W_MAX } from "./layout/card-body";
35
+ export { DiagramCanvas, DiagramToolbar, ToolToggle, ViewButtons, DiagramLegend, DiagramLegendBody, DiagramLegendItems, FittedLegend, LegendToolbarButton, IsolationClear, MissingNotice, ScenarioBanner, Scene3D, ViewerInspector, ViewerNodeMenu, CLASSIFICATION_OPTIONS, DETAIL_OPTIONS, type DiagramCanvasProps, type Scene3DProps } from "./viewer/views";
36
+ export { ModelView, type ModelViewProps, type ModelInfo } from "./cad/ModelView";
37
+ export { ViewerHierarchy, HierarchyToggle, RAIL_WIDTH, HIERARCHY_LOOK, type ViewerHierarchyProps, type HierarchyCommand } from "./viewer/hierarchy";
38
+ export { buildHierarchy, filterHierarchy, visibleRows, treeKey, selectedPath, rowTarget, drillInto, revealPaths, ancestorsOf, hierarchyIndex, walkHierarchy, panelState, groupWiring, markLegend, WIRING_PATH, kindOf, KIND_ICON, KIND_LABEL as HIERARCHY_KIND_LABEL, HIERARCHY_WIDTH, type HierarchyNode, type HierarchyKind, type HierarchyRow, type HierarchyPanelState, type HierarchyOptions, type TreeKeyResult, } from "./model/hierarchy";
39
+ export { SelectionDetails, type SelectionDetailsProps, type DetailsTarget, type InterfaceRequest } from "./viewer/details";
40
+ export { Sections, DetailsSection, KeyValues, useShowAll, useSectionOpen, useExpandedCards, SelectionKey, keptInspectorState, navTarget, onNavKey, SECTION_LOOK, type DetailsSectionProps } from "./chrome/Sections";
41
+ export { InterfaceCards, InterfaceCard, SlotGlyph, CARD_LOOK, SLOT_COLOR, type InterfaceCardActions } from "./chrome/InterfaceCards";
42
+ export { interfaceSlots, slotSummary, groupSlots, checkFindings, levelNames, humanize, sentence, paramValue, type InterfaceSlot, type SlotKind, type SlotState, type SlotPin, type SlotLink, type SlotFinding, type SlotNote, type HostFinding, type SlotOptions } from "./model/slots";
43
+ export { ArtifactCard, ArtifactCards, ArtifactGrid, type ArtifactCardProps } from "./chrome/ArtifactCards";
44
+ export { EvidenceSection } from "./chrome/Evidence";
45
+ export { artifactCard, artifactCards, mergeFormats, isInternalArtifact, classifyArtifact, evidenceSourceCards, groupArtifactCards, ARTIFACT_GROUPS, type ArtifactCardModel, type ArtifactCardOptions, type ArtifactGroup, type ArtifactKind, type ArtifactPreview, type ArtifactSourceRow } from "./model/artifacts";
46
+ export type { EvidenceFiles, SourceRow, Verification } from "./model/evidence";
47
+ export { HostContext, type HostActions } from "./host/context";
48
+ export { ScrollArea, type ScrollAreaProps } from "./ui/ScrollArea";
49
+ export { Toolbar, ToolbarSeparator, IconButton, ToggleGroup, type ToolbarProps, type IconButtonProps, type ToggleGroupProps } from "./ui/Toolbar";
50
+ export { Legend, LegendItem, LegendSwatches, type LegendItemProps, type LegendProps } from "./ui/Legend";
51
+ export { legendStage, legendRoom, type LegendStage } from "./ui/legendFit";
52
+ export { InspectorPanel, Section, PropertyRow, Badge, StatusDot, EmptyState, type InspectorPanelProps, type Tone } from "./ui/Inspector";
53
+ export { Tooltips } from "./ui/Tooltips";
54
+ export { Icon } from "./canvas/Icon";
55
+ export { cx } from "./ui/cx";
56
+ export { TOKEN_NAMES, tokenVar, token, mix, defineTheme, applyTheme, readToken, themeCss, themeDeclarations, DERIVED, type Theme, type ThemeTokens, type ThemeToken, type ThemeScheme, } from "./theme/tokens";
57
+ export { defaultDark, defaultLight, defaultTheme, DRC_TOKENS, DARK_DATA_TOKENS, LIGHT_DATA_TOKENS } from "./theme/themes";
58
+ export { staticTheme, defaultThemeAdapter, vscodeThemeAdapter, vscodeTheme, vscodeScheme, pickTheme, type ThemeAdapter } from "./theme/adapters";
59
+ export { ThemeProvider, useTheme, tokenColor, type ThemeProviderProps, type ThemeState } from "./theme/ThemeProvider";
60
+ export { resolveTheme, completeTheme } from "./theme/resolve";
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The viewer's side of the host channel: commands from the handle or the
3
+ * parent window reach the mounted viewer (queued until it has a model on
4
+ * screen), and its events go to the host's callbacks and, in a frame, to the
5
+ * parent window.
6
+ */
7
+ import type { ViewerEvent, ViewerHostMessage, ViewerOptions } from "../api";
8
+ /** Host messages on their way to the viewer component (App.tsx subscribes once it shows a model). */
9
+ export interface CommandBus {
10
+ send(message: ViewerHostMessage): void;
11
+ /** Receive messages, first the ones that waited; returns the unsubscribe. */
12
+ subscribe(handler: (message: ViewerHostMessage) => void): () => void;
13
+ }
14
+ export declare function commandBus(): CommandBus;
15
+ /** Calls the option callback that matches an event. */
16
+ export declare function deliver(options: ViewerOptions, event: ViewerEvent): void;
17
+ /** The part of `window` the parent channel uses (a test passes its own). */
18
+ export interface ChannelWindow {
19
+ /** The frame's parent (the window itself when not in a frame). */
20
+ parent: {
21
+ postMessage(message: unknown, targetOrigin: string): void;
22
+ };
23
+ location: {
24
+ origin: string;
25
+ ancestorOrigins?: ArrayLike<string>;
26
+ };
27
+ document?: {
28
+ referrer?: string;
29
+ };
30
+ addEventListener(type: "message", listener: (e: MessageEvent) => void): void;
31
+ removeEventListener(type: "message", listener: (e: MessageEvent) => void): void;
32
+ }
33
+ export interface ParentChannel {
34
+ /** Post an event to the parent (held until the parent's origin is known). */
35
+ post(event: ViewerEvent): void;
36
+ close(): void;
37
+ }
38
+ /**
39
+ * The postMessage channel with the parent window. Accepts host messages from
40
+ * `win.parent` only, from an allowed origin (default: `win`'s own). Events
41
+ * go to the parent's origin: the frame's ancestor origin or referrer when it
42
+ * is allowed, the only allowed origin when exactly one is listed, else the
43
+ * origin of the parent's first accepted message (events wait until then).
44
+ * Returns undefined when `win` is not in a frame.
45
+ */
46
+ export declare function connectParent(win: ChannelWindow, options: {
47
+ origins?: string[];
48
+ onMessage: (message: ViewerHostMessage) => void;
49
+ }): ParentChannel | undefined;
@@ -0,0 +1,29 @@
1
+ import type { ArtifactDef, ModuleDef } from "@deltarobotics/uhd";
2
+ import type { ArtifactPreview } from "../model/artifacts";
3
+ import type { EvidenceFiles } from "../model/evidence";
4
+ export interface HostActions {
5
+ openSource?: (defId: string, instancePath?: string) => void;
6
+ openArtifact?: (defId: string, artifactId: string) => void;
7
+ /** Open a module on its own (the host's new tab); given only when the host takes it. */
8
+ openModule?: (path: string) => void;
9
+ /** Show a module in the host's own inspector (a node's INSPECT); given only when the host has one. */
10
+ inspect?: (path: string, defId?: string) => void;
11
+ /** Open a web address (an evidence source); not given: a link in a new tab. */
12
+ openLink?: (url: string) => void;
13
+ /**
14
+ * `"live"` (default): a module's body model turns in a small 3D preview
15
+ * above its cards. `"cards"`: no model is loaded for the inspector; the
16
+ * model is a card like the others, with the picture `artifactPreview` gives.
17
+ */
18
+ artifactModels?: "live" | "cards";
19
+ /** A picture for an artifact that is not an image itself (a model's exported preview, or the model to draw small). */
20
+ artifactPreview?: (def: ModuleDef, artifact: ArtifactDef) => ArtifactPreview | undefined;
21
+ /** Draws a model small, as a picture's URL, for a card whose preview is a model. Not given: such cards show their icon. */
22
+ drawModel?: (src: string) => Promise<string>;
23
+ /** Load pictures from web addresses (default true). */
24
+ remoteImages?: boolean;
25
+ /** A definition's evidence files (sources.json, verification.json), when the host has them. */
26
+ evidence?: (defId: string) => EvidenceFiles | undefined;
27
+ }
28
+ export declare const HostContext: import("react").Context<HostActions>;
29
+ export declare const useHost: () => HostActions;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The host message protocol (README, "Host messages"): checking what arrives
3
+ * over `postMessage`, and which origins a frame's parent may have. Nothing
4
+ * here names a host.
5
+ */
6
+ import type { ViewerEvent, ViewerHostMessage } from "../api";
7
+ /** Every protocol message's type starts with this. */
8
+ export declare const PREFIX = "uhd-viewer:";
9
+ /**
10
+ * A host → viewer message, checked and normalised (a path given as a list of
11
+ * child ids is joined); undefined for anything else.
12
+ */
13
+ export declare function parseHostMessage(data: unknown): ViewerHostMessage | undefined;
14
+ /** A viewer → host event (for a host or a relay telling them from other messages). */
15
+ export declare const isViewerEvent: (data: unknown) => data is ViewerEvent;
16
+ /**
17
+ * `origin` is one of `allowed`: an exact origin, `scheme://*` (any origin
18
+ * with that scheme), or `*` (any). An opaque origin ("null") matches only `*`.
19
+ */
20
+ export declare function originAllowed(origin: string, allowed: readonly string[]): boolean;
21
+ /** Origins from a comma-separated list (`a,b`), blanks dropped. */
22
+ export declare const originList: (value: string | null | undefined) => string[];
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Host messages against the model: what a focus or a select names on which
3
+ * drill level, and the viewer's selection as the host sees it (canonical
4
+ * paths from the root). Pure functions, so they test without a page.
5
+ */
6
+ import type { ModuleDef } from "@deltarobotics/uhd";
7
+ import type { ModuleLookup } from "@deltarobotics/uhd";
8
+ import type { ViewerSelection } from "../api";
9
+ import type { ViewModel } from "../model/types";
10
+ /** A selection on the level being viewed (ids relative to it), as the canvas and inspector hold it. */
11
+ export type LevelSelection = {
12
+ kind: "node" | "link";
13
+ id: string;
14
+ } | null;
15
+ /** The longest prefix of `path` that names instances, child by child, from `root`. */
16
+ export declare function instancePrefix(root: ModuleDef, lookup: ModuleLookup, path: string[]): string[];
17
+ /**
18
+ * Where to show a focused module: the current level when it is drawn there
19
+ * (a child, or a child of a module expanded in place), else the level of its
20
+ * parent. A path that stops naming instances is followed as far as it does;
21
+ * the root (or nothing valid) shows the top level with nothing selected.
22
+ */
23
+ export declare function focusTarget(root: ModuleDef, lookup: ModuleLookup, path: string, current: {
24
+ drill: string[];
25
+ expanded: ReadonlySet<string>;
26
+ }): {
27
+ drill: string[];
28
+ selection: LevelSelection;
29
+ };
30
+ /**
31
+ * The drill path for `path`: as far as it names instances, and only through
32
+ * modules with children of their own (a level shows a module's children).
33
+ */
34
+ export declare function drillTarget(root: ModuleDef, lookup: ModuleLookup, path: string): string[];
35
+ /** A selection in the 3D view (by instance path), as the host sees it. */
36
+ export declare function cadHostSelection(sel: {
37
+ body?: string;
38
+ component?: string;
39
+ harness?: string;
40
+ mate?: string;
41
+ }, defAt: (path: string[]) => ModuleDef | undefined): ViewerSelection | null;
42
+ /**
43
+ * What a host's select picks in the 3D view, by path from the root: the
44
+ * selection it made on the diagram's level (a module or a link; nets are not
45
+ * drawn in 3D), else the first id naming a module instance in full. `none`
46
+ * for no ids; undefined when nothing applies.
47
+ */
48
+ export declare function cadSelectTarget(root: ModuleDef, lookup: ModuleLookup, drill: string[], onLevel: LevelSelection | undefined, ids: string[]): {
49
+ kind: "node" | "link" | "none";
50
+ path: string;
51
+ } | undefined;
52
+ /** A stored or derived link id as the view selects it: the line that draws it, or the derived link itself. */
53
+ export declare function selectableLink(vm: ViewModel, id: string): LevelSelection;
54
+ /**
55
+ * The first of `ids` drawn on this level: a node (module instance path, net,
56
+ * boundary port) or a link. `node:` and `link:` prefixes pick the kind. Ids
57
+ * are canonical paths from the root (as `selectionChanged` reports them) or
58
+ * relative to the level. Undefined when none is there; null for no ids.
59
+ */
60
+ export declare function selectTarget(vm: ViewModel, drill: string[], ids: string[]): LevelSelection | undefined;
61
+ /** A level id as a canonical path from the root. */
62
+ export declare function fullId(drill: string[], id: string): string;
63
+ /**
64
+ * The selection as the host sees it: canonical ids, and the definition of
65
+ * the module selected (for a link, the module that declares it).
66
+ */
67
+ export declare function hostSelection(vm: ViewModel, drill: string[], selection: LevelSelection, levelDefId?: string): ViewerSelection | null;
68
+ /** The definition that declares a level link: the module expanded in place that holds it, or the level's. */
69
+ export declare function linkOwner(vm: ViewModel, linkId: string, levelDefId?: string): string | undefined;
70
+ /** Selections compare by content (the host hears each change once). */
71
+ export declare const selectionKey: (s: ViewerSelection | null) => string;
72
+ /**
73
+ * Whether the host hears the selection now: when it changed, and when a module or link was picked again while it
74
+ * was selected (a click on it, a row of the hierarchy, a host's focus). A pick is a new `pick` (the controller's
75
+ * state object, made anew by each pick, kept when nothing was picked); a rebuild or a view change makes none.
76
+ * The host then knows the user chose it (D6: the Inspector comes back every time, also for the module already
77
+ * selected), and two picks that arrive together (nothing, then the same module again) are still said.
78
+ */
79
+ export declare function saySelection(sent: {
80
+ key: string;
81
+ pick: unknown;
82
+ }, now: {
83
+ selection: ViewerSelection | null;
84
+ pick: unknown;
85
+ }): boolean;
@@ -0,0 +1,69 @@
1
+ export interface BoardNode {
2
+ id: string;
3
+ w: number;
4
+ h: number;
5
+ /** A net junction (placed after the components). */
6
+ net?: boolean;
7
+ /** Taxonomy category ("power.regulator", "component.passive.capacitor"). */
8
+ category?: string;
9
+ label?: string;
10
+ /** A junction that is one tap of a rail drawn in several places: the rail's id (taps.ts). */
11
+ tapOf?: string;
12
+ /** A card's height once one side holds this many ports (cards grow to fit their ports). */
13
+ heightFor?: (perSide: number) => number;
14
+ /** Ports on lines that do not go to a junction (a sheet port's, a direct link's): either side. */
15
+ otherPorts?: number;
16
+ }
17
+ export interface Rect {
18
+ x: number;
19
+ y: number;
20
+ w: number;
21
+ h: number;
22
+ }
23
+ export type FunctionGroup = "io" | "power" | "mcu" | "sensor" | "output" | "other";
24
+ /** A passive or discrete part: placed with the part it serves rather than in a group of its own. */
25
+ export declare function isSatellite(category?: string): boolean;
26
+ /** The function group of a component, from its category. */
27
+ export declare function functionGroup(category?: string): FunctionGroup;
28
+ /** Whether `boxes` overlap once each is grown by its own padding. */
29
+ export declare function hits(a: Rect, b: Rect, padA?: number, padB?: number): boolean;
30
+ /**
31
+ * The free top-left position nearest `ideal` (a centre) for a box of size w×h,
32
+ * clear of every obstacle by the paddings. Falls back to below everything.
33
+ */
34
+ export declare function freeSpot(ideal: [number, number], w: number, h: number, obstacles: Rect[], pad?: {
35
+ x: number;
36
+ y: number;
37
+ }): {
38
+ x: number;
39
+ y: number;
40
+ };
41
+ /**
42
+ * Isotonic placement: tops for boxes in a fixed order, each at least `gap`
43
+ * below the one before, as close to its ideal top as that allows (least
44
+ * squares, pool-adjacent-violators).
45
+ */
46
+ export declare function pack(ideal: number[], heights: number[], gap: number): number[];
47
+ /** What the layout and the rail taps share: the clusters of a board level. */
48
+ export interface BoardClusters {
49
+ /** Cluster (its anchor's id) of each component. */
50
+ clusterOf: Map<string, string>;
51
+ /** Members of each cluster: the anchor first, then its satellites in reading order. */
52
+ clusters: Map<string, string[]>;
53
+ /** Function group of each cluster. */
54
+ group: Map<string, FunctionGroup>;
55
+ /** Component members of each junction. */
56
+ members: Map<string, string[]>;
57
+ /** Component ↔ component lines (two-member nets of their own). */
58
+ direct: [string, string][];
59
+ }
60
+ /** Cluster a board level's components: each passive with the part it serves, a dedicated connector with its part. */
61
+ export declare function boardClusters(nodes: BoardNode[], edges: [string, string][]): BoardClusters;
62
+ /**
63
+ * Top-left positions by node id for a board level. `edges` are the lines
64
+ * drawn between nodes (component ↔ junction, or component ↔ component).
65
+ */
66
+ export declare function boardLayout(nodes: BoardNode[], edges: [string, string][]): Record<string, {
67
+ x: number;
68
+ y: number;
69
+ }>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * What a module card's body holds (its icon, its title and the chips that
3
+ * count its unlinked interfaces by protocol) and how much height that needs.
4
+ *
5
+ * The layout grows a card to fit its body between the labels of the ports
6
+ * on its top and bottom edges; the card then draws only the chips that fit
7
+ * and folds the rest into "+N". Sizes follow `.module-body`, `.module-title`
8
+ * and `.port-group` in styles/app.css.
9
+ */
10
+ import type { PortGroup } from "./layout";
11
+ import type { Side } from "../canvas/routing";
12
+ /** Chips shown before the rest fold into "+N". */
13
+ export declare const MAX_CHIPS = 4;
14
+ /** Chip rows a card grows for; more chips fold into "+N". */
15
+ export declare const MAX_CHIP_ROWS = 3;
16
+ export interface CardInsets {
17
+ top: number;
18
+ bottom: number;
19
+ left: number;
20
+ right: number;
21
+ }
22
+ /** A port's label as the card draws it: the name without its parenthesised or dashed qualifier. */
23
+ export declare function portLabelText(name: string): string;
24
+ /** How wide a label on a card's left or right edge may run before it is cut with an ellipsis. */
25
+ export declare function sideLabelWidth(cardWidth: number): number;
26
+ /** A card's width with nothing to fit, and the widest it grows to for its labels. */
27
+ export declare const CARD_W = 170;
28
+ export declare const CARD_W_MAX = 272;
29
+ /** The widest a label on a card's top or bottom edge runs (its share of the edge, when that is less). */
30
+ export declare const TOP_LABEL_MAX = 160;
31
+ /** A label's width as drawn (8 px type). */
32
+ export declare const labelWidth: (name: string) => number;
33
+ /**
34
+ * The width a card takes so the labels of its linked interfaces fit beside
35
+ * its body: as wide as the longest needs (two may face each other across the
36
+ * card), from the plain width up to `CARD_W_MAX`. A longer label is cut with
37
+ * an ellipsis and says its whole name on hover.
38
+ */
39
+ export declare function cardWidth(labels: readonly string[]): number;
40
+ /** A label is drawn short: it does not fit `width`, or the card leaves out the name's qualifier. */
41
+ export declare function labelIsCut(name: string, width: number): boolean;
42
+ /**
43
+ * Room the body keeps clear of the port labels: a label row on the top and
44
+ * bottom edges that have labelled ports, and on the left and right as wide
45
+ * as that edge's longest label.
46
+ */
47
+ export declare function cardInsets(labels: {
48
+ side: Side;
49
+ name: string;
50
+ }[], cardWidth: number): CardInsets;
51
+ export declare function chipText(g: PortGroup): string;
52
+ /** The height a card needs for its body, with up to MAX_CHIP_ROWS rows of chips. */
53
+ export declare function cardHeight(title: string, width: number, groups: PortGroup[], insets: CardInsets): number;
54
+ /**
55
+ * The chips a card of this size draws: as many groups as fit its free rows,
56
+ * the rest counted in "+N". A card with no room for a row draws none.
57
+ */
58
+ export declare function fitChips(title: string, box: {
59
+ w: number;
60
+ h: number;
61
+ }, groups: PortGroup[], insets: CardInsets): {
62
+ shown: PortGroup[];
63
+ rest: number;
64
+ };
@@ -0,0 +1,94 @@
1
+ import type { ViewLink, ViewModel, ViewPort } from "../model/types";
2
+ import { type Pt, type Side } from "../canvas/routing";
3
+ import type { LinkLayout } from "../api";
4
+ /** Distance kept between a corner and anything placed on a link (handles, toggles). */
5
+ export declare const BEND_CLEAR = 28;
6
+ export interface Box {
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ }
12
+ /** A link's hand layout: part of the host contract (src/api.ts). */
13
+ export type { LinkLayout };
14
+ export interface LevelLayout {
15
+ /** Top-level nodes: absolute. Nodes inside a container: relative to it. */
16
+ nodes: Record<string, Partial<Box>>;
17
+ /** Port side overrides keyed "node|port". */
18
+ ports?: Record<string, Side>;
19
+ links?: Record<string, LinkLayout>;
20
+ }
21
+ export interface PlacedPort {
22
+ node: string;
23
+ port: ViewPort;
24
+ side: Side;
25
+ /** Absolute position on the node outline. */
26
+ x: number;
27
+ y: number;
28
+ /** Colour key: the link that decides this port's classification colour. */
29
+ links: ViewLink[];
30
+ }
31
+ export interface PortGroup {
32
+ protocol: string;
33
+ domain: string;
34
+ count: number;
35
+ }
36
+ export interface ComputedLayout {
37
+ boxes: Record<string, Box>;
38
+ ports: Record<string, PlacedPort>;
39
+ /** Unlinked ports summarised per node (design D6: "UART ×4"). */
40
+ groups: Record<string, PortGroup[]>;
41
+ routes: Record<string, Pt[]>;
42
+ /** Label anchor per link (absent: route too short to label). */
43
+ labels: Record<string, Pt>;
44
+ /** Label + toggle position as an arc length along the route. */
45
+ labelAt: Record<string, number>;
46
+ /** Harness region span along the route (arc lengths), for links that run through one. */
47
+ runs: Record<string, [number, number]>;
48
+ /** Pinned waypoints per link, and the automatic ones (the router's own bends). */
49
+ pins: Record<string, Pt[]>;
50
+ bends: Record<string, Pt[]>;
51
+ /** Links whose label (and expansion point) the user or the display file placed. */
52
+ labelFixed: Record<string, boolean>;
53
+ /** The display file's and the user's label and tab placement per link, merged. */
54
+ edits: Record<string, LinkLayout>;
55
+ }
56
+ export declare const portKey: (node: string, port: string) => string;
57
+ /** How an automatic layout starts (PB-1007): cards that keep their places, and an earlier arrangement to keep. */
58
+ export interface ArrangeOptions {
59
+ /** Top-level cards that stay where they are (top-left). */
60
+ fixed?: Record<string, {
61
+ x: number;
62
+ y: number;
63
+ }>;
64
+ /** Each top-level card's centre in an earlier layout: the arrangement to start from. */
65
+ seed?: Record<string, {
66
+ x: number;
67
+ y: number;
68
+ }>;
69
+ /** The old default (one row), for comparing against. */
70
+ legacy?: boolean;
71
+ }
72
+ /**
73
+ * A drag in progress: only the links touching what moves are rerouted, around
74
+ * the routes the other links already have; everything is rerouted once the
75
+ * drag ends. Keeps dragging a module or a waypoint at frame rate.
76
+ */
77
+ export interface LiveLayout {
78
+ previous: ComputedLayout;
79
+ /** Nodes being dragged (their children move with them). */
80
+ nodes?: Set<string>;
81
+ /** Links whose waypoints are being dragged. */
82
+ links?: Set<string>;
83
+ }
84
+ /**
85
+ * The automatic layout of a level as a hand layout (PB-1007, for a host that
86
+ * writes it to the display file): top-level nodes absolute, nodes inside an
87
+ * expanded module relative to it. `fixed` cards keep their places; a `seed`
88
+ * keeps an earlier arrangement where it can.
89
+ */
90
+ export declare function arrangeLevel(vm: ViewModel, opts?: ArrangeOptions, keepSizes?: LevelLayout): LevelLayout;
91
+ export declare function computeLayout(vm: ViewModel, hand: LevelLayout | undefined, overrides: Record<string, {
92
+ x: number;
93
+ y: number;
94
+ }>, showGrounds: boolean, linkOverrides?: Record<string, LinkLayout>, live?: LiveLayout, arrange?: ArrangeOptions): ComputedLayout;
@@ -0,0 +1,32 @@
1
+ export interface GridNode {
2
+ id: string;
3
+ w: number;
4
+ h: number;
5
+ category?: string;
6
+ }
7
+ export interface GridOptions {
8
+ /** Cards that stay where they are: top-left positions. */
9
+ fixed?: Record<string, {
10
+ x: number;
11
+ y: number;
12
+ }>;
13
+ /** Where each card's centre was: the starting arrangement. */
14
+ seed?: Record<string, {
15
+ x: number;
16
+ y: number;
17
+ }>;
18
+ /** Width over height to aim for (default 1.6). */
19
+ aspect?: number;
20
+ }
21
+ /** Room between columns (lines and their labels run there) and between rows. */
22
+ export declare const GRID_GAP_X = 192;
23
+ export declare const GRID_GAP_Y = 128;
24
+ /**
25
+ * Top-left positions by id for a level of modules. `edges` are the lines
26
+ * between cards (one entry per line; several between two cards weigh more,
27
+ * though less than that many).
28
+ */
29
+ export declare function gridLayout(nodes: GridNode[], edges: [string, string][], opts?: GridOptions): Record<string, {
30
+ x: number;
31
+ y: number;
32
+ }>;