structured-context-map-views 0.0.0-stage → 0.1.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 (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -2
  3. package/dist/definition-KuPoA1mO.js +582 -0
  4. package/dist/definition-KuPoA1mO.js.map +1 -0
  5. package/dist/index.js +889 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/node.js +106 -0
  8. package/dist/node.js.map +1 -0
  9. package/dist/styles.css +497 -0
  10. package/dist/types/core/definition.d.ts +112 -0
  11. package/dist/types/core/graph.d.ts +16 -0
  12. package/dist/types/core/index.d.ts +6 -0
  13. package/dist/types/core/landscape.d.ts +44 -0
  14. package/dist/types/core/occurrence.d.ts +11 -0
  15. package/dist/types/core/schemas/definition.d.ts +163 -0
  16. package/dist/types/core/schemas/index.d.ts +418 -0
  17. package/dist/types/core/schemas/landscape-layout.d.ts +102 -0
  18. package/dist/types/core/schemas/param-ref.d.ts +10 -0
  19. package/dist/types/core/schemas/schema-url.d.ts +7 -0
  20. package/dist/types/core/schemas/settings.d.ts +27 -0
  21. package/dist/types/core/schemas/tree-layout.d.ts +40 -0
  22. package/dist/types/core/schemas/xy-layout.d.ts +94 -0
  23. package/dist/types/core/tree.d.ts +45 -0
  24. package/dist/types/core/xy.d.ts +43 -0
  25. package/dist/types/index.d.ts +7 -0
  26. package/dist/types/layouts/EdgeOverlay.d.ts +36 -0
  27. package/dist/types/layouts/LandscapeLayout.d.ts +8 -0
  28. package/dist/types/layouts/MapView.d.ts +12 -0
  29. package/dist/types/layouts/NodeCard.d.ts +16 -0
  30. package/dist/types/layouts/ParameterControls.d.ts +13 -0
  31. package/dist/types/layouts/TreeLayout.d.ts +16 -0
  32. package/dist/types/layouts/XYLayout.d.ts +9 -0
  33. package/dist/types/node/bundled-definitions.d.ts +1 -0
  34. package/dist/types/node/index.d.ts +17 -0
  35. package/package.json +69 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["_Fragment"],"sources":["../src/core/graph.ts","../src/core/occurrence.ts","../src/core/landscape.ts","../src/core/tree.ts","../src/core/xy.ts","../src/layouts/NodeCard.tsx","../src/layouts/LandscapeLayout.tsx","../src/layouts/EdgeOverlay.tsx","../src/layouts/TreeLayout.tsx","../src/layouts/XYLayout.tsx","../src/layouts/MapView.tsx","../src/layouts/ParameterControls.tsx"],"sourcesContent":["import type { Hierarchy, SpaceGraph, SpaceNode } from 'structured-context/api';\n\ntype NodeId = SpaceNode['id'];\n\n/** A directed parent → child edge of a space graph, carrying the metadata of the reference it came from. */\nexport type Edge = SpaceGraph['parentEdges'] extends ReadonlyMap<string, readonly (infer E)[]> ? E : never;\n\nconst NONE: readonly Edge[] = [];\n\n/**\n * Edges to a node's parents, in the order the node declares them. A graph narrowed by a filter keeps\n * edges to parents outside it; those are left out.\n */\nexport function parentEdges(graph: SpaceGraph, id: NodeId): readonly Edge[] {\n return (graph.parentEdges.get(id) ?? NONE).filter((edge) => graph.nodes.has(edge.parent));\n}\n\n/** Edges to a node's children, in graph order of the children. */\nexport function childEdges(graph: SpaceGraph, id: NodeId): readonly Edge[] {\n return graph.childEdges.get(id) ?? NONE;\n}\n\n/** Every edge whose parent and child are both in the graph. */\nexport function edgesWithin(graph: SpaceGraph): Edge[] {\n return [...graph.nodes.keys()].flatMap((id) => parentEdges(graph, id));\n}\n\n/** Whether an edge belongs to the given hierarchy (as opposed to a relationship or another hierarchy). */\nexport function isHierarchyEdge(edge: Edge, hierarchy: Hierarchy | undefined): boolean {\n return hierarchy !== undefined && edge.kind === 'hierarchy' && edge.hierarchy === hierarchy.name;\n}\n","/**\n * One step of an occurrence path: a node title, a `[field, value]` group, or `null` for the\n * ungrouped area. Encoded distinctly, so a field group can't collide with a node of the same name.\n */\nexport type PathSegment = string | readonly [field: string, value: string | null] | null;\n\n/**\n * Path-based key for one occurrence of a node: region, then the steps from the outermost\n * container down to the node. Unique within a map view even when a node appears several times.\n * Hosts can render several map views at once, so layouts scope keys to the map view instance.\n */\nexport function occurrenceKey(regionId: string, path: readonly PathSegment[]): string {\n return JSON.stringify([regionId, ...path]);\n}\n","import type { SpaceGraph, SpaceNode } from 'structured-context/api';\nimport type { GroupLevel, LandscapeLayoutConfig } from './definition';\nimport { childEdges, parentEdges } from './graph';\nimport { occurrenceKey, type PathSegment } from './occurrence';\n\n/** One rendered container or node in a landscape. */\nexport type LandscapeOccurrence = {\n /** Path-based key, unique within the map view (see {@link occurrenceKey}). */\n key: string;\n /** Index of the grouping level this occurrence belongs to. */\n level: number;\n /** A node, or a field value shared by the nodes inside (`null`: the field is empty). */\n item: { kind: 'node'; node: SpaceNode } | { kind: 'value'; field: string; value: string | null };\n /** Nodes on the `via` chain that joined this node to the node level above, one entry per skipped type. */\n via: ViaStep[];\n /** Key of the node's first occurrence when this is a repeat. Repeats are rendered in full. */\n repeatOf?: string;\n children: LandscapeOccurrence[];\n};\n\nexport type ViaStep = { type: string; nodes: SpaceNode[] };\n\nexport type Landscape = {\n groups: LandscapeOccurrence[];\n /**\n * Nodes of a grouped level that no container above reaches (e.g. an Activity with no\n * Outcome when grouping by Outcomes), each with its own contents.\n */\n ungrouped: LandscapeOccurrence[];\n};\n\ntype Candidate = { node: SpaceNode; via: ViaStep[] };\ntype FieldFilter = { field: string; value: string | null };\n\n/**\n * Lay a space out as nested containers following the layout's grouping levels.\n *\n * Adjacent node levels are joined by any edge between their types, in either direction, or only\n * edges from `edge.field`. A `via` chain joins them through the skipped types. A field level\n * splits the next node level by that field's values. A node reached from several containers\n * appears in each, in full: the levels are finite, so repeating contents can't recurse.\n */\nexport function buildLandscape(graph: SpaceGraph, regionId: string, layout: LandscapeLayoutConfig): Landscape {\n const levels = layout.group;\n const order = new Map([...graph.nodes.keys()].map((title, i) => [title, i]));\n const first = new Map<string, string>();\n\n const occurrenceFor = (candidate: Candidate, levelIdx: number, path: readonly PathSegment[]): LandscapeOccurrence => {\n const { node } = candidate;\n const nodePath = [...path, node.id];\n const key = occurrenceKey(regionId, nodePath);\n const repeatOf = first.get(node.id);\n if (repeatOf === undefined) first.set(node.id, key);\n return {\n key,\n level: levelIdx,\n item: { kind: 'node', node },\n via: candidate.via,\n ...(repeatOf !== undefined && { repeatOf }),\n children: place(levelIdx + 1, node, [], nodePath),\n };\n };\n\n /** Occurrences for level `levelIdx` inside `anchor`, the nearest enclosing node. */\n const place = (\n levelIdx: number,\n anchor: SpaceNode | undefined,\n filters: readonly FieldFilter[],\n path: readonly PathSegment[],\n ): LandscapeOccurrence[] => {\n const level = levels[levelIdx];\n if (level === undefined) return [];\n if (isTypeLevel(level)) {\n return candidates(level, anchor, filters).map((c) => occurrenceFor(c, levelIdx, path));\n }\n\n const nodeLevel = levels.slice(levelIdx).find(isTypeLevel)!;\n const values = distinctValues(\n candidates(nodeLevel, anchor, filters).map((c) => c.node),\n level.field,\n );\n return values.map((value) => {\n const valuePath = [...path, [level.field, value] as const];\n return {\n key: occurrenceKey(regionId, valuePath),\n level: levelIdx,\n item: { kind: 'value', field: level.field, value },\n via: [],\n children: place(levelIdx + 1, anchor, [...filters, { field: level.field, value }], valuePath),\n };\n });\n };\n\n /** Nodes of a type level joined to `anchor` (all of the type at the top) and matching the filters. */\n const candidates = (\n level: TypeLevel,\n anchor: SpaceNode | undefined,\n filters: readonly FieldFilter[],\n ): Candidate[] => {\n const reached: Candidate[] =\n anchor === undefined\n ? ofType(level.type).map((node) => ({ node, via: [] }))\n : walkChain(anchor, level.via ?? [], level.type, level.edge?.field);\n return reached.filter(({ node }) => filters.every((f) => hasValue(node, f)));\n };\n\n const ofType = (type: string) => [...graph.nodes.values()].filter((node) => node.resolvedType === type);\n\n /** Neighbours of a node with the given type, across edges in either direction, in space order. */\n const neighbours = (title: SpaceNode['id'], type: string, field: string | undefined): SpaceNode[] => {\n const titles = new Set([\n ...childEdges(graph, title)\n .filter((e) => field === undefined || e.field === field)\n .map((e) => e.child),\n ...parentEdges(graph, title)\n .filter((e) => field === undefined || e.field === field)\n .map((e) => e.parent),\n ]);\n return [...titles]\n .map((t) => graph.nodes.get(t)!)\n .filter((node) => node.resolvedType === type)\n .sort((a, b) => order.get(a.id)! - order.get(b.id)!);\n };\n\n /**\n * Follow edges from `anchor` through each `via` type to `target`, keeping the skipped nodes on\n * the way to each target node. Several chains to one target merge into one candidate.\n */\n const walkChain = (anchor: SpaceNode, via: readonly string[], target: string, field: string | undefined) => {\n let frontier = [{ node: anchor, trail: [] as SpaceNode[] }];\n for (const type of via) {\n frontier = frontier.flatMap(({ node, trail }) =>\n neighbours(node.id, type, undefined).map((next) => ({ node: next, trail: [...trail, next] })),\n );\n }\n const byTitle = new Map<string, Candidate>();\n for (const { node, trail } of frontier) {\n for (const next of neighbours(node.id, target, field)) {\n const candidate = byTitle.get(next.id) ?? { node: next, via: via.map((type) => ({ type, nodes: [] })) };\n trail.forEach((skipped, i) => {\n const step = candidate.via[i]!;\n if (!step.nodes.includes(skipped)) step.nodes.push(skipped);\n });\n byTitle.set(next.id, candidate);\n }\n }\n return [...byTitle.values()].sort((a, b) => order.get(a.node.id)! - order.get(b.node.id)!);\n };\n\n const groups = place(0, undefined, [], []);\n\n // Nodes of an inner level that no container reached get their own contents, like tree orphans.\n const ungrouped: LandscapeOccurrence[] = [];\n levels.forEach((level, levelIdx) => {\n if (levelIdx === 0 || !isTypeLevel(level)) return;\n for (const node of ofType(level.type)) {\n if (!first.has(node.id)) ungrouped.push(occurrenceFor({ node, via: [] }, levelIdx, [null]));\n }\n });\n\n return { groups, ungrouped };\n}\n\ntype TypeLevel = Extract<GroupLevel, { type: string }>;\n\nfunction isTypeLevel(level: GroupLevel): level is TypeLevel {\n return 'type' in level;\n}\n\n/** A field's values as group labels: strings, an entry per array item, `null` when empty. */\nfunction valuesOf(node: SpaceNode, field: string): (string | null)[] {\n const raw = node.schemaData[field];\n const list = (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '');\n return list.length === 0 ? [null] : list.map((v) => (typeof v === 'object' ? JSON.stringify(v) : String(v)));\n}\n\n/** Distinct values in first-seen order, with the empty group last. */\nfunction distinctValues(nodes: SpaceNode[], field: string): (string | null)[] {\n const values = new Set(nodes.flatMap((node) => valuesOf(node, field)));\n const present = [...values].filter((v): v is string => v !== null);\n return values.has(null) ? [...present, null] : present;\n}\n\nfunction hasValue(node: SpaceNode, { field, value }: FieldFilter): boolean {\n return valuesOf(node, field).includes(value);\n}\n","import type { SpaceGraph, SpaceNode } from 'structured-context/api';\nimport type { DagMode } from './definition';\nimport { childEdges, isHierarchyEdge, parentEdges } from './graph';\nimport { occurrenceKey } from './occurrence';\n\n/** One rendered appearance of a node in a tree. */\nexport type TreeOccurrence = {\n /** Path-based key, unique within the map view (see {@link occurrenceKey}). */\n key: string;\n node: SpaceNode;\n children: TreeOccurrence[];\n /**\n * Key of the node's primary occurrence when this is a repeat. A node with several parents\n * appears under each of them, but its subtree is only rendered at its first occurrence.\n */\n repeatOf?: string;\n};\n\nexport type Tree = {\n roots: TreeOccurrence[];\n /**\n * Hierarchy-typed nodes not reachable from a root, rendered as extra roots: nodes with no\n * hierarchy parent in the space, then entry points into otherwise unreachable cycles.\n */\n orphans: TreeOccurrence[];\n /** Key of each node's primary (first, fully rendered) occurrence. */\n primary: ReadonlyMap<string, string>;\n /**\n * In cross-edge mode, links from a node's other parents to its primary occurrence, which\n * replace repeat occurrences. Empty in repeat mode.\n */\n crossLinks: TreeLink[];\n};\n\nexport type TreeLink = { from: string; to: string; kind: 'tree' | 'cross' };\n\n/**\n * Lay a space out as a tree over its hierarchy.\n *\n * Roots are root-type nodes with no hierarchy parent. Traversal is depth-first in space order,\n * so a node's primary occurrence is under the parent reached first. Under its other parents a\n * node is either repeated without its subtree (`repeat`), or left out and linked by a cross-edge\n * (`cross-edge`). Either way traversal stops there, which also keeps cycles from recursing.\n */\nexport function buildTree(graph: SpaceGraph, regionId: string, dag: DagMode = 'repeat'): Tree {\n const { hierarchy } = graph;\n const levelTypes = new Set(hierarchy?.levels.map((level) => level.type));\n const rootType = hierarchy?.levels[0]?.type;\n const primary = new Map<string, string>();\n // Parent occurrence → child title, resolved to the child's primary occurrence afterwards.\n const pendingCross: { from: string; child: string }[] = [];\n\n const hierarchyParents = (title: SpaceNode['id']) =>\n parentEdges(graph, title).filter((edge) => isHierarchyEdge(edge, hierarchy));\n\n const visit = (node: SpaceNode, path: readonly string[]): TreeOccurrence => {\n const nodePath = [...path, node.id];\n const key = occurrenceKey(regionId, nodePath);\n primary.set(node.id, key);\n // A child can reference the same parent through more than one field; it occurs once.\n const childTitles = new Set(\n childEdges(graph, node.id)\n .filter((edge) => isHierarchyEdge(edge, hierarchy))\n .map((edge) => edge.child),\n );\n const children: TreeOccurrence[] = [];\n for (const title of childTitles) {\n const child = graph.nodes.get(title)!;\n const repeatOf = primary.get(title);\n if (repeatOf === undefined) children.push(visit(child, nodePath));\n else if (dag === 'repeat')\n children.push({ key: occurrenceKey(regionId, [...nodePath, title]), node: child, children: [], repeatOf });\n else pendingCross.push({ from: key, child: title });\n }\n return { key, node, children };\n };\n\n const levelNodes = [...graph.nodes.values()].filter((node) => levelTypes.has(node.resolvedType));\n const unparented = levelNodes.filter((node) => hierarchyParents(node.id).length === 0);\n const roots = unparented.filter((node) => node.resolvedType === rootType).map((node) => visit(node, []));\n const orphans = unparented.filter((node) => node.resolvedType !== rootType).map((node) => visit(node, []));\n // Nodes only reachable through a cycle have a parent but no path from a root. Rather than\n // drop them, enter each such cycle at its first node in space order.\n for (const node of levelNodes) {\n if (!primary.has(node.id)) orphans.push(visit(node, []));\n }\n\n const crossLinks = pendingCross.map(({ from, child }) => ({ from, to: primary.get(child)!, kind: 'cross' as const }));\n return { roots, orphans, primary, crossLinks };\n}\n\n/** Parent → child pairs of occurrence keys: one per rendered parent-child link, then cross-links. */\nexport function treeLinks(tree: Tree): TreeLink[] {\n const links: TreeLink[] = [];\n const walk = (occurrence: TreeOccurrence) => {\n for (const child of occurrence.children) {\n links.push({ from: occurrence.key, to: child.key, kind: 'tree' });\n walk(child);\n }\n };\n for (const root of [...tree.roots, ...tree.orphans]) walk(root);\n return [...links, ...tree.crossLinks];\n}\n","import type { SpaceGraph, SpaceNode } from 'structured-context/api';\nimport type { AxisConfig, XYLayoutConfig } from './definition';\nimport { edgesWithin, isHierarchyEdge } from './graph';\nimport { occurrenceKey } from './occurrence';\n\n/** A plotted node. `x` and `y` run from 0 to 1 across the plot, left to right and bottom to top. */\nexport type XYPoint = { key: string; node: SpaceNode; x: number; y: number };\n\n/** Why a node of a plotted type couldn't be placed, per axis. */\nexport type Unplaced = { key: string; node: SpaceNode; reasons: string[] };\n\n/**\n * An edge between plotted nodes. `lane` separates edges joining the same two nodes: 0 when\n * there's one, otherwise centred around 0 (e.g. -0.5 and 0.5), for layouts to draw side by side.\n */\nexport type XYLink = { from: string; to: string; field: string; hierarchy: boolean; lane: number };\n\nexport type XYPlot = {\n points: XYPoint[];\n unplaced: Unplaced[];\n /** Edges between plotted nodes. */\n links: XYLink[];\n /** Each axis's [min, max]; `direction` says which end is drawn where. */\n domains: { x: readonly [number, number]; y: readonly [number, number] };\n};\n\n/**\n * Position nodes by two numeric fields. Every node of a plotted type appears once: on the plot\n * when both its values are numbers inside the axes' domains, otherwise in `unplaced` with the\n * reason. Positions are normalised; layouts decide the plot's size.\n */\nexport function buildXYPlot(graph: SpaceGraph, regionId: string, layout: XYLayoutConfig): XYPlot {\n const { x, y } = layout;\n const nodes = [...graph.nodes.values()];\n const types = new Set(\n layout.types.length > 0\n ? layout.types\n : nodes\n .filter((n) => isNumber(n.schemaData[x.field]) || isNumber(n.schemaData[y.field]))\n .map((n) => n.resolvedType),\n );\n const plotted = nodes.filter((n) => types.has(n.resolvedType));\n\n const domains = {\n x: domainOf(x, plotted),\n y: domainOf(y, plotted),\n };\n\n const points: XYPoint[] = [];\n const unplaced: Unplaced[] = [];\n for (const node of plotted) {\n const key = occurrenceKey(regionId, [node.id]);\n const px = position(node, x, domains.x);\n const py = position(node, y, domains.y);\n if (typeof px === 'number' && typeof py === 'number') points.push({ key, node, x: px, y: py });\n else unplaced.push({ key, node, reasons: [px, py].filter((r): r is string => typeof r === 'string') });\n }\n\n const keys = new Map<string, string>(points.map((p) => [p.node.id, p.key]));\n const fields = new Set(layout.edgeFields);\n const links = edgesWithin(graph)\n .filter((e) => keys.has(e.parent) && keys.has(e.child) && (fields.size === 0 || fields.has(e.field)))\n .map((e) => ({\n from: keys.get(e.parent)!,\n to: keys.get(e.child)!,\n field: e.field,\n hierarchy: isHierarchyEdge(e, graph.hierarchy),\n lane: 0,\n }));\n assignLanes(links);\n\n return { points, unplaced, links, domains };\n}\n\nfunction isNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\n/** The axis's fixed domain, or the range of plotted values (widened when they're all equal). */\nfunction domainOf(axis: AxisConfig, nodes: readonly SpaceNode[]): readonly [number, number] {\n if (Array.isArray(axis.domain)) return [axis.domain[0]!, axis.domain[1]!];\n const values = nodes.map((n) => n.schemaData[axis.field]).filter(isNumber);\n if (values.length === 0) return [0, 1];\n const min = Math.min(...values);\n const max = Math.max(...values);\n return min === max ? [min - 1, max + 1] : [min, max];\n}\n\n/** The node's normalised position on the axis, or why it has none. */\nfunction position(node: SpaceNode, axis: AxisConfig, [min, max]: readonly [number, number]): number | string {\n const value = node.schemaData[axis.field];\n if (value === undefined || value === null) return `no ${axis.field}`;\n if (!isNumber(value)) return `${axis.field} is not a number`;\n if (value < min || value > max) return `${axis.field} ${value} is outside ${min}–${max}`;\n const t = (value - min) / (max - min);\n return axis.direction === 'descending' ? 1 - t : t;\n}\n\n/** Spread links joining the same pair of nodes (in either direction) into lanes around 0. */\nfunction assignLanes(links: XYLink[]) {\n const byPair = new Map<string, XYLink[]>();\n for (const link of links) {\n const pair = [link.from, link.to].sort().join('\\n');\n byPair.set(pair, [...(byPair.get(pair) ?? []), link]);\n }\n for (const group of byPair.values()) {\n group.forEach((link, i) => {\n link.lane = i - (group.length - 1) / 2;\n });\n }\n}\n","import type { SpaceNode } from 'structured-context/api';\nimport type { NodesConfig } from '../core';\n\ntype Props = {\n node: SpaceNode;\n /** The node's subtree is rendered at another occurrence. */\n repeat: boolean;\n nodes: NodesConfig;\n onSelect?: (node: SpaceNode) => void;\n};\n\n/**\n * A clickable node, drawn as a card (title plus selected fields) or a compact shape with a\n * tooltip. Deliberately a `div` with button semantics rather than a `<button>`: hosts style\n * native form controls globally (Obsidian fixes button height and disables wrapping).\n */\nexport function NodeCard({ node, repeat, nodes, onSelect }: Props) {\n const select = () => onSelect?.(node);\n const label = `${node.resolvedType}: ${node.title}${repeat ? ' (shown in full elsewhere)' : ''}`;\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n select();\n }\n };\n const border = repeat ? 'mv:border-dashed mv:border-muted/60 mv:opacity-75' : 'mv:border-edge';\n\n if (nodes.style === 'shape') {\n // 24px: the minimum target size for pointer input (WCAG 2.2 AA). The type's initial is the\n // visible cue; the full type and title are in the tooltip and accessible name.\n return (\n // biome-ignore lint/a11y/useSemanticElements: a native button inherits host button styles.\n <div\n role=\"button\"\n tabIndex={0}\n title={label}\n onClick={select}\n onKeyDown={onKeyDown}\n aria-label={label}\n className={`mv:flex mv:size-6 mv:cursor-pointer mv:items-center mv:justify-center mv:rounded-full mv:border-2 mv:bg-card mv:text-[0.65rem] mv:font-semibold mv:uppercase mv:text-muted mv:hover:border-accent mv:focus-visible:outline-2 mv:focus-visible:outline-accent ${border}`}\n >\n <span aria-hidden=\"true\">{node.resolvedType.charAt(0)}</span>\n </div>\n );\n }\n\n const fields = nodes.fields\n .map((field) => [field, formatValue(node.schemaData[field])] as const)\n .filter(([, value]) => value !== undefined);\n\n return (\n // biome-ignore lint/a11y/useSemanticElements: a native button inherits host button styles.\n <div\n role=\"button\"\n tabIndex={0}\n title={label}\n onClick={select}\n onKeyDown={onKeyDown}\n className={`mv:w-44 mv:cursor-pointer mv:rounded-md mv:border mv:bg-card mv:px-2.5 mv:py-1.5 mv:text-left mv:shadow-sm mv:hover:border-accent mv:focus-visible:outline-2 mv:focus-visible:outline-accent ${border}`}\n >\n <span className=\"mv:block mv:text-[0.65rem] mv:uppercase mv:tracking-wide mv:text-muted\">\n {node.resolvedType}\n {repeat && ' · repeat'}\n </span>\n <span className=\"mv:block mv:text-sm mv:leading-snug\">{node.title}</span>\n {fields.length > 0 && (\n <dl className=\"mv:m-0 mv:mt-1 mv:text-xs mv:text-muted\">\n {fields.map(([field, value]) => (\n <div key={field} className=\"mv:flex mv:gap-1\">\n <dt className=\"mv:shrink-0\">{field}:</dt>\n <dd className=\"mv:m-0 mv:truncate mv:text-ink\">{value}</dd>\n </div>\n ))}\n </dl>\n )}\n </div>\n );\n}\n\nfunction formatValue(value: unknown): string | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (Array.isArray(value)) return value.map(String).join(', ');\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n","import { useMemo } from 'react';\nimport type { SpaceNode } from 'structured-context/api';\nimport { buildLandscape, type LandscapeLayoutConfig, type LandscapeOccurrence, type Orientation } from '../core';\nimport { NodeCard } from './NodeCard';\nimport type { LayoutProps } from './TreeLayout';\n\n/**\n * Nested containers following the layout's grouping levels; the last level is drawn as nodes.\n * Flexbox does the nesting and wrapping. Containment shows the grouping edges, so no connectors\n * are drawn. PROTOTYPE: `edges.show` has no effect here; other edges aren't drawn yet.\n */\nexport function LandscapeLayout({ graph, region, instanceId, onNodeSelect }: LayoutProps<LandscapeLayoutConfig>) {\n const { layout } = region;\n const landscape = useMemo(() => buildLandscape(graph, region.id, layout), [graph, region.id, layout]);\n const last = layout.group.length - 1;\n\n const breadcrumb = (occurrence: LandscapeOccurrence) => {\n const level = layout.group[occurrence.level];\n if (!level || !('type' in level) || level.intermediate !== 'breadcrumb') return null;\n const steps = occurrence.via.filter((step) => step.nodes.length > 0);\n if (steps.length === 0) return null;\n return (\n <span className=\"mv:block mv:max-w-44 mv:text-[0.65rem] mv:leading-tight mv:text-muted\">\n via {steps.map((step) => step.nodes.map((n) => n.title).join(', ')).join(' › ')}\n </span>\n );\n };\n\n const render = (occurrence: LandscapeOccurrence, direction: Orientation): React.ReactNode => {\n const repeat = occurrence.repeatOf !== undefined;\n const marker = { 'data-occurrence': `${instanceId}${occurrence.key}` };\n\n if (occurrence.item.kind === 'node' && occurrence.level === last) {\n return (\n <li key={occurrence.key} {...marker} className=\"mv:flex mv:flex-col mv:gap-0.5\">\n <NodeCard node={occurrence.item.node} repeat={repeat} nodes={region.nodes} onSelect={onNodeSelect} />\n {breadcrumb(occurrence)}\n </li>\n );\n }\n\n // Containers alternate direction with depth; a container of nodes wraps them.\n const inner: Orientation = direction === 'horizontal' ? 'vertical' : 'horizontal';\n const holdsNodes = occurrence.level + 1 === last;\n return (\n <li\n key={occurrence.key}\n {...marker}\n className={`mv:flex mv:flex-col mv:gap-2 mv:rounded-lg mv:border mv:bg-surface mv:p-2 ${repeat ? 'mv:border-dashed mv:border-muted/60' : 'mv:border-edge'}`}\n >\n {occurrence.item.kind === 'node' ? (\n <ContainerHeading node={occurrence.item.node} repeat={repeat} onSelect={onNodeSelect}>\n {breadcrumb(occurrence)}\n </ContainerHeading>\n ) : (\n <span className=\"mv:text-xs mv:font-semibold mv:text-muted\">\n {occurrence.item.field}: {occurrence.item.value === null ? '(none)' : displayValue(occurrence.item.value)}\n </span>\n )}\n <Items direction={holdsNodes ? 'wrap' : inner}>\n {occurrence.children.map((child) => render(child, inner))}\n </Items>\n </li>\n );\n };\n\n const top = layout.orientation;\n return (\n <div className=\"mv:w-max mv:min-w-full mv:p-4\">\n <Items direction={last === 0 ? 'wrap' : top}>\n {landscape.groups.map((group) => render(group, top))}\n {landscape.ungrouped.length > 0 && (\n <li className=\"mv:flex mv:flex-col mv:gap-2 mv:rounded-lg mv:border mv:border-dashed mv:border-edge mv:p-2\">\n <span className=\"mv:text-xs mv:font-semibold mv:text-muted\">Ungrouped</span>\n <Items direction=\"wrap\">{landscape.ungrouped.map((o) => render(o, top))}</Items>\n </li>\n )}\n </Items>\n </div>\n );\n}\n\nfunction Items({ direction, children }: { direction: Orientation | 'wrap'; children: React.ReactNode }) {\n const layout = {\n horizontal: 'mv:flex-row mv:items-start mv:gap-3',\n vertical: 'mv:flex-col mv:items-stretch mv:gap-2',\n wrap: 'mv:max-w-[36rem] mv:flex-row mv:flex-wrap mv:items-start mv:gap-2',\n }[direction];\n return <ul className={`mv:m-0 mv:flex mv:list-none mv:p-0 ${layout}`}>{children}</ul>;\n}\n\n/** A container's node: type and title, selectable like a card. */\nfunction ContainerHeading({\n node,\n repeat,\n onSelect,\n children,\n}: {\n node: SpaceNode;\n repeat: boolean;\n onSelect?: (node: SpaceNode) => void;\n children?: React.ReactNode;\n}) {\n const select = () => onSelect?.(node);\n return (\n // biome-ignore lint/a11y/useSemanticElements: a native button inherits host button styles.\n <div\n role=\"button\"\n tabIndex={0}\n title={`${node.resolvedType}: ${node.title}${repeat ? ' (also shown elsewhere)' : ''}`}\n onClick={select}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n select();\n }\n }}\n className=\"mv:cursor-pointer mv:rounded mv:px-1 mv:hover:text-accent mv:focus-visible:outline-2 mv:focus-visible:outline-accent\"\n >\n <span className=\"mv:block mv:text-[0.65rem] mv:uppercase mv:tracking-wide mv:text-muted\">\n {node.resolvedType}\n {repeat && ' · repeat'}\n </span>\n <span className=\"mv:block mv:text-sm mv:font-semibold mv:leading-snug\">{node.title}</span>\n {children}\n </div>\n );\n}\n\n/** Field values that are wikilinks show as the linked title. */\nfunction displayValue(value: string): string {\n return value.replace(/^\\[\\[([^\\]|]+)(\\|[^\\]]*)?\\]\\]$/, '$1');\n}\n","import { useLayoutEffect, useState } from 'react';\nimport type { Orientation } from '../core';\n\n/**\n * A connector between two occurrences. `cross` links (e.g. extra parents) are drawn dashed.\n * `id` tells apart several links between the same occurrences; straight links between the same\n * pair are drawn side by side, `lane` apart (0: on the line between centres).\n */\nexport type Link = { from: string; to: string; kind?: 'tree' | 'cross'; id?: string; lane?: number };\n\ntype Props = {\n links: readonly Link[];\n /** Rendered element of each occurrence, by occurrence key. */\n elements: ReadonlyMap<string, HTMLElement>;\n /**\n * Positioned element the overlay covers; coordinates are relative to it. Passed as an element,\n * not a ref: the overlay sits inside it, and React runs a child's layout effects before\n * attaching the parent's ref.\n */\n container: HTMLElement | null;\n /** Curves leaving along the tree's orientation, or straight lines between centres (drawn under the nodes). */\n routing: Orientation | 'straight';\n};\n\ntype Drawn = { key: string; d: string; cross: boolean };\n\n/**\n * SVG layer drawing connectors between rendered occurrences.\n *\n * Measures occurrence elements after layout and redraws when the container or any occurrence\n * resizes, so it follows embedded panes that resize independently of the window, and reflows\n * such as late font loads. The SVG fills the container instead of being sized from it, so it\n * never holds the container open after the layout shrinks.\n */\nexport function EdgeOverlay({ links, elements, container, routing }: Props) {\n const [paths, setPaths] = useState<Drawn[]>([]);\n\n useLayoutEffect(() => {\n if (!container) return;\n\n const measure = () => {\n const origin = container.getBoundingClientRect();\n const paths: Drawn[] = [];\n for (const { from, to, kind, id, lane = 0 } of links) {\n const a = elements.get(from)?.getBoundingClientRect();\n const b = elements.get(to)?.getBoundingClientRect();\n if (!a || !b) continue;\n paths.push({\n key: `${from}→${to}${id ?? ''}`,\n d: routing === 'straight' ? line(a, b, origin, lane) : connector(a, b, origin, routing),\n cross: kind === 'cross',\n });\n }\n setPaths(paths);\n };\n\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(container);\n for (const element of elements.values()) observer.observe(element);\n return () => observer.disconnect();\n }, [links, elements, container, routing]);\n\n return (\n <svg\n aria-hidden=\"true\"\n className=\"mv:pointer-events-none mv:absolute mv:inset-0 mv:h-full mv:w-full mv:overflow-visible\"\n >\n {paths.map(({ key, d, cross }) => (\n <path\n key={key}\n d={d}\n className={`mv:fill-none ${cross ? 'mv:stroke-accent/60' : 'mv:stroke-muted/50'}`}\n strokeWidth={1.5}\n strokeDasharray={cross ? '4 3' : undefined}\n />\n ))}\n </svg>\n );\n}\n\n/** Cubic curve from the parent's trailing edge to the child's leading edge. */\nfunction connector(a: DOMRect, b: DOMRect, origin: DOMRect, orientation: Orientation): string {\n if (orientation === 'vertical') {\n const x1 = a.left + a.width / 2 - origin.left;\n const y1 = a.bottom - origin.top;\n const x2 = b.left + b.width / 2 - origin.left;\n const y2 = b.top - origin.top;\n const my = (y1 + y2) / 2;\n return `M${x1},${y1} C${x1},${my} ${x2},${my} ${x2},${y2}`;\n }\n const x1 = a.right - origin.left;\n const y1 = a.top + a.height / 2 - origin.top;\n const x2 = b.left - origin.left;\n const y2 = b.top + b.height / 2 - origin.top;\n const mx = (x1 + x2) / 2;\n return `M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}`;\n}\n\n/** Pixels between parallel straight links. */\nconst LANE_GAP = 6;\n\n/**\n * Straight line between centres, shifted sideways by its lane; the layout draws nodes over the\n * overlay to hide the ends. The shift is relative to a fixed orientation of the pair, so links in\n * opposite directions between the same nodes still separate.\n */\nfunction line(a: DOMRect, b: DOMRect, origin: DOMRect, lane: number): string {\n const x1 = a.left + a.width / 2 - origin.left;\n const y1 = a.top + a.height / 2 - origin.top;\n const x2 = b.left + b.width / 2 - origin.left;\n const y2 = b.top + b.height / 2 - origin.top;\n const flip = x1 < x2 || (x1 === x2 && y1 < y2) ? 1 : -1;\n const length = Math.hypot(x2 - x1, y2 - y1) || 1;\n const dx = (-(y2 - y1) / length) * lane * LANE_GAP * flip;\n const dy = ((x2 - x1) / length) * lane * LANE_GAP * flip;\n return `M${x1 + dx},${y1 + dy} L${x2 + dx},${y2 + dy}`;\n}\n","import { useCallback, useMemo, useRef, useState } from 'react';\nimport type { SpaceGraph, SpaceNode } from 'structured-context/api';\nimport {\n buildTree,\n type LayoutConfig,\n type Orientation,\n type Region,\n type TreeLayoutConfig,\n type TreeOccurrence,\n treeLinks,\n} from '../core';\nimport { EdgeOverlay } from './EdgeOverlay';\nimport { NodeCard } from './NodeCard';\n\nexport type LayoutProps<L extends LayoutConfig = LayoutConfig> = {\n graph: SpaceGraph;\n /** The region with parameter values applied, narrowed to the layout's own settings. */\n region: Region & { layout: L };\n /** Scopes occurrence ids when several map views render in one document. */\n instanceId: string;\n onNodeSelect?: (node: SpaceNode) => void;\n};\n\n/**\n * Tree over the schema's hierarchy. Flexbox does the branching; the SVG overlay draws connectors.\n */\nexport function TreeLayout({ graph, region, instanceId, onNodeSelect }: LayoutProps<TreeLayoutConfig>) {\n const { orientation, dag } = region.layout;\n const tree = useMemo(() => buildTree(graph, region.id, dag), [graph, region.id, dag]);\n const links = useMemo(() => (region.edges.show ? treeLinks(tree) : []), [tree, region.edges.show]);\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n // Filled by ref callbacks during commit, read by the overlay's layout effect afterwards.\n const elements = useRef(new Map<string, HTMLElement>()).current;\n const register = useCallback(\n (key: string) => (el: HTMLElement | null) => {\n if (el) elements.set(key, el);\n else elements.delete(key);\n },\n [elements],\n );\n\n const branch = (occurrence: TreeOccurrence) => (\n <li\n key={occurrence.key}\n className={`mv:flex mv:items-center ${orientation === 'vertical' ? 'mv:flex-col' : 'mv:flex-row'}`}\n >\n <div ref={register(occurrence.key)} data-occurrence={`${instanceId}${occurrence.key}`}>\n <NodeCard\n node={occurrence.node}\n repeat={occurrence.repeatOf !== undefined}\n nodes={region.nodes}\n onSelect={onNodeSelect}\n />\n </div>\n {occurrence.children.length > 0 && (\n <Branches orientation={orientation} nested>\n {occurrence.children.map(branch)}\n </Branches>\n )}\n </li>\n );\n\n return (\n <div ref={setContainer} className=\"mv:relative mv:w-max mv:min-w-full mv:p-4\">\n <Branches orientation={orientation}>{[...tree.roots, ...tree.orphans].map(branch)}</Branches>\n <EdgeOverlay links={links} elements={elements} container={container} routing={orientation} />\n </div>\n );\n}\n\nfunction Branches({\n orientation,\n nested = false,\n children,\n}: {\n orientation: Orientation;\n nested?: boolean;\n children: React.ReactNode;\n}) {\n // Siblings run across the main axis; nested branches leave a gap for connectors.\n const layout =\n orientation === 'vertical'\n ? `mv:flex-row mv:items-start mv:justify-center mv:gap-3 ${nested ? 'mv:pt-10' : ''}`\n : `mv:flex-col mv:items-start mv:justify-center mv:gap-2 ${nested ? 'mv:pl-14' : ''}`;\n return <ul className={`mv:m-0 mv:flex mv:list-none mv:p-0 ${layout}`}>{children}</ul>;\n}\n","import { useCallback, useMemo, useRef, useState } from 'react';\nimport { type AxisConfig, buildXYPlot, type XYLayoutConfig } from '../core';\nimport { EdgeOverlay } from './EdgeOverlay';\nimport { NodeCard } from './NodeCard';\nimport type { LayoutProps } from './TreeLayout';\n\n/**\n * Nodes positioned by two numeric fields, with straight connectors between them. Positions are\n * percentages of the plot area, which fills the container (scrolling below a minimum size). Nodes that can't be\n * placed are listed under the plot with the reason.\n * PROTOTYPE: labels may overlap where nodes sit close together; no collision handling.\n */\nexport function XYLayout({ graph, region, instanceId, onNodeSelect }: LayoutProps<XYLayoutConfig>) {\n const { layout } = region;\n const plot = useMemo(() => buildXYPlot(graph, region.id, layout), [graph, region.id, layout]);\n // Relationship edges (flows, pipelines) are dashed, to tell them from the value chain.\n const links = useMemo(\n () =>\n region.edges.show\n ? plot.links.map(({ from, to, field, hierarchy, lane }) => ({\n from,\n to,\n id: field,\n lane,\n kind: hierarchy ? ('tree' as const) : ('cross' as const),\n }))\n : [],\n [plot, region.edges.show],\n );\n const shape = region.nodes.style === 'shape';\n\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const elements = useRef(new Map<string, HTMLElement>()).current;\n const register = useCallback(\n (key: string) => (el: HTMLElement | null) => {\n if (el) elements.set(key, el);\n else elements.delete(key);\n },\n [elements],\n );\n\n return (\n <div className=\"mv:box-border mv:flex mv:h-full mv:min-h-[28rem] mv:min-w-[36rem] mv:flex-col mv:gap-4 mv:p-4\">\n <div className=\"mv:grid mv:min-h-0 mv:flex-1 mv:grid-cols-[auto_1fr] mv:grid-rows-[1fr_auto] mv:gap-1\">\n <AxisTitle axis={layout.y} domain={plot.domains.y} vertical />\n <div ref={setContainer} className=\"mv:relative mv:border-b mv:border-l mv:border-edge\">\n {/* Before the nodes, so the opaque nodes cover the ends of the lines. */}\n <EdgeOverlay links={links} elements={elements} container={container} routing=\"straight\" />\n {/* Inset, so nodes at the ends of a domain stay inside the plot. */}\n <div className=\"mv:absolute mv:inset-8\">\n {plot.points.map((point) => (\n <div\n key={point.key}\n data-occurrence={`${instanceId}${point.key}`}\n className={`mv:absolute mv:flex mv:items-center mv:gap-1 ${shape ? 'mv:-translate-x-3 mv:translate-y-3' : 'mv:-translate-x-1/2 mv:translate-y-1/2'}`}\n style={{ left: `${point.x * 100}%`, bottom: `${point.y * 100}%` }}\n >\n <div ref={register(point.key)}>\n <NodeCard node={point.node} repeat={false} nodes={region.nodes} onSelect={onNodeSelect} />\n </div>\n {shape && (\n <span className=\"mv:whitespace-nowrap mv:rounded mv:bg-surface/80 mv:px-0.5 mv:text-xs\">\n {point.node.title}\n </span>\n )}\n </div>\n ))}\n </div>\n </div>\n <div />\n <AxisTitle axis={layout.x} domain={plot.domains.x} />\n </div>\n\n {plot.unplaced.length > 0 && (\n <section className=\"mv:flex mv:flex-col mv:gap-2\">\n <span className=\"mv:text-xs mv:font-semibold mv:text-muted\">Not plotted</span>\n <ul className=\"mv:m-0 mv:flex mv:list-none mv:flex-wrap mv:gap-3 mv:p-0\">\n {plot.unplaced.map(({ key, node, reasons }) => (\n <li key={key} data-occurrence={`${instanceId}${key}`} className=\"mv:flex mv:items-center mv:gap-1\">\n <NodeCard node={node} repeat={false} nodes={region.nodes} onSelect={onNodeSelect} />\n <span className=\"mv:text-xs\">\n {shape && `${node.title} `}\n <span className=\"mv:text-muted\">({reasons.join('; ')})</span>\n </span>\n </li>\n ))}\n </ul>\n </section>\n )}\n </div>\n );\n}\n\n/** Axis title with the values at each end, in drawing order. */\nfunction AxisTitle({\n axis,\n domain: [min, max],\n vertical = false,\n}: {\n axis: AxisConfig;\n domain: readonly [number, number];\n vertical?: boolean;\n}) {\n const [start, end] = axis.direction === 'descending' ? [max, min] : [min, max];\n return (\n <div\n className={`mv:flex mv:items-center mv:justify-between mv:gap-2 mv:text-xs mv:text-muted ${vertical ? 'mv:flex-col-reverse mv:py-8' : 'mv:px-8'}`}\n >\n <span>{format(start)}</span>\n <span className={`mv:font-semibold ${vertical ? 'mv:[writing-mode:vertical-rl] mv:rotate-180' : ''}`}>\n {axis.label ?? axis.field}\n </span>\n <span>{format(end)}</span>\n </div>\n );\n}\n\nfunction format(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toPrecision(3).replace(/\\.?0+$/, '');\n}\n","import { useId, useMemo } from 'react';\nimport type { SpaceGraph, SpaceNode } from 'structured-context/api';\nimport { type MapViewDefinition, type ParameterValues, type Region, resolveRegions } from '../core';\nimport { LandscapeLayout } from './LandscapeLayout';\nimport { type LayoutProps, TreeLayout } from './TreeLayout';\nimport { XYLayout } from './XYLayout';\n\ntype Props = {\n definition: MapViewDefinition;\n graph: SpaceGraph;\n /** Current parameter values (host-owned session state); missing values use defaults. */\n values?: ParameterValues;\n onNodeSelect?: (node: SpaceNode) => void;\n};\n\n/** Renders a map view definition against a space. Sizes to its container and scrolls within it. */\nexport function MapView({ definition, graph, values, onNodeSelect }: Props) {\n const instanceId = useId();\n const regions = useMemo(() => resolveRegions(definition, values), [definition, values]);\n // PROTOTYPE: only the first region is rendered. Arranging several regions is undesigned (#8).\n const region = regions[0]!;\n const props = { graph, instanceId, onNodeSelect };\n\n return (\n <div className=\"mv:h-full mv:w-full mv:overflow-auto mv:bg-surface mv:text-ink\">{layoutFor(region, props)}</div>\n );\n}\n\nfunction layoutFor(region: Region, props: Omit<LayoutProps, 'region'>) {\n const { layout } = region;\n switch (layout.type) {\n case 'tree':\n return <TreeLayout {...props} region={{ ...region, layout }} />;\n case 'landscape':\n return <LandscapeLayout {...props} region={{ ...region, layout }} />;\n case 'xy':\n return <XYLayout {...props} region={{ ...region, layout }} />;\n }\n}\n","import { useId, useMemo } from 'react';\nimport { type MapViewDefinition, type ParameterValues, resolveParameterValues } from '../core';\n\ntype Props = {\n definition: MapViewDefinition;\n /** Host-owned values; missing or stale ones show as the default, matching what renders. */\n values: ParameterValues;\n onChange: (name: string, value: unknown) => void;\n};\n\n/**\n * Controls generated from a map view's declared parameters. Hosts own the values (session\n * state) and decide where the controls go.\n */\nexport function ParameterControls({ definition, values, onChange }: Props) {\n const id = useId();\n const resolved = useMemo(() => resolveParameterValues(definition, values), [definition, values]);\n const { parameters } = definition;\n if (parameters.length === 0) return null;\n return (\n <div className=\"mv:flex mv:flex-wrap mv:items-center mv:gap-x-4 mv:gap-y-2 mv:text-sm\">\n {parameters.map((param) => {\n const value = resolved[param.name];\n const controlId = `${id}-${param.name}`;\n const { control } = param;\n return (\n <div key={param.name} className=\"mv:flex mv:items-center mv:gap-1.5\" title={param.description}>\n {control.kind === 'toggle' ? (\n <>\n <input\n id={controlId}\n type=\"checkbox\"\n checked={value === true}\n onChange={(e) => onChange(param.name, e.target.checked)}\n />\n <label htmlFor={controlId}>{param.title}</label>\n </>\n ) : control.kind === 'select' ? (\n <>\n <label htmlFor={controlId} className=\"mv:text-muted\">\n {param.title}\n </label>\n <select\n id={controlId}\n value={JSON.stringify(value)}\n onChange={(e) => onChange(param.name, JSON.parse(e.target.value))}\n >\n {control.options.map((option) => (\n <option key={JSON.stringify(option)} value={JSON.stringify(option)}>\n {String(option)}\n </option>\n ))}\n </select>\n </>\n ) : (\n <fieldset className=\"mv:m-0 mv:flex mv:flex-wrap mv:items-center mv:gap-2 mv:border-0 mv:p-0\">\n <legend className=\"mv:float-left mv:mr-1.5 mv:p-0 mv:text-muted\">{param.title}</legend>\n {control.options.map((option) => {\n const selected = Array.isArray(value) ? value : [];\n const checked = selected.some((v) => JSON.stringify(v) === JSON.stringify(option));\n return (\n <label key={JSON.stringify(option)} className=\"mv:flex mv:items-center mv:gap-1\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() =>\n onChange(\n param.name,\n // Keep the options' order rather than the order of clicks.\n control.options.filter((o) =>\n o === option ? !checked : selected.some((v) => JSON.stringify(v) === JSON.stringify(o)),\n ),\n )\n }\n />\n {String(option)}\n </label>\n );\n })}\n </fieldset>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
package/dist/node.js ADDED
@@ -0,0 +1,106 @@
1
+ import { r as parseMapViewDefinition } from "./definition-KuPoA1mO.js";
2
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { PLUGIN_PREFIX, resolveConfigPath, resolvePluginDir } from "structured-context/api";
5
+ //#region src/node/bundled-definitions.ts
6
+ var BUNDLED_DEFINITIONS = { "strategy-tree.json5": "// Strategy stack as a tree: vision → mission → goals → opportunities → solutions → experiments.\n{\n $schema: 'https://raw.githubusercontent.com/mindsocket/structured-context-map-views/main/schemas/generated/latest/definition.json',\n $id: 'sctx-map-view://core/strategy-tree',\n title: 'Strategy tree',\n schema: 'sctx://core/strategy_general',\n parameters: {\n orientation: { $ref: 'settings.json#/$defs/orientation', default: 'vertical' },\n nodeStyle: { $ref: 'settings.json#/$defs/nodeStyle', default: 'card' },\n cardFields: {\n title: 'Card fields',\n type: 'array',\n items: { enum: ['status', 'priority', 'summary'] },\n default: ['status'],\n },\n },\n regions: [\n {\n id: 'main',\n layout: { type: 'tree', orientation: { $param: 'orientation' } },\n nodes: { style: { $param: 'nodeStyle' }, fields: { $param: 'cardFields' } },\n edges: { show: true },\n },\n ],\n}\n" };
7
+ //#endregion
8
+ //#region src/node/index.ts
9
+ /**
10
+ * Node-only: finds map view definitions the way structured-context finds plugin schemas.
11
+ * Browser-only hosts receive the result from a server, like graph data.
12
+ */
13
+ var SCHEME = "sctx-map-view://";
14
+ var DEFINITIONS_DIR = "map-views";
15
+ var CONFIG_PLUGINS_DIR = "plugins";
16
+ var PACKAGE_NAME = "structured-context-map-views";
17
+ /**
18
+ * Every map view definition available with `config`, from:
19
+ * - this package's embedded definitions (`sctx-map-view://core/<name>`), for structured-context's bundled schemas;
20
+ * - each plugin found from a config folder or named by a space (`sctx-map-view://<plugin>/<name>`): the
21
+ * folder named by `"sctx": { "mapViews": "…" }` in its package.json, else its `map-views/` folder;
22
+ * - a `map-views/` folder next to each config file (`sctx-map-view://<name>`).
23
+ *
24
+ * Each definition's `$id` must be in the namespace of where it was found, and ids must be unique.
25
+ * Throws on the first invalid definition.
26
+ */
27
+ function loadMapViewDefinitions(config, options = {}) {
28
+ const configDir = options.configDir ?? dirname(resolve(resolveConfigPath().path));
29
+ const configDirs = [.../* @__PURE__ */ new Set([configDir, ...config.spaces.flatMap((s) => s.sourceDir ? [s.sourceDir] : [])])];
30
+ const folders = [...pluginDirs(config, configDir, configDirs).flatMap(([plugin, dir]) => {
31
+ const definitions = pluginDefinitionsDir(dir);
32
+ return definitions ? [{
33
+ dir: definitions,
34
+ source: {
35
+ kind: "plugin",
36
+ plugin
37
+ }
38
+ }] : [];
39
+ }), ...configDirs.map((dir) => ({
40
+ dir: join(dir, DEFINITIONS_DIR),
41
+ source: { kind: "config" }
42
+ }))];
43
+ const foundAt = /* @__PURE__ */ new Map();
44
+ const definitions = [];
45
+ const add = (label, text, source) => {
46
+ const definition = parseMapViewDefinition(label, text);
47
+ assertIdNamespace(definition.id, source, label);
48
+ const existing = foundAt.get(definition.id);
49
+ if (existing) throw new Error(`Map view id "${definition.id}" is defined by both ${existing} and ${label}.`);
50
+ foundAt.set(definition.id, label);
51
+ definitions.push(definition);
52
+ };
53
+ for (const [file, text] of Object.entries(BUNDLED_DEFINITIONS)) add(`${PACKAGE_NAME}/${file}`, text, { kind: "core" });
54
+ for (const { dir, source } of folders) {
55
+ if (!isDirectory(dir)) continue;
56
+ for (const file of readdirSync(dir).filter((f) => f.endsWith(".json5")).sort()) {
57
+ const path = join(dir, file);
58
+ add(path, readFileSync(path, "utf8"), source);
59
+ }
60
+ }
61
+ return definitions;
62
+ }
63
+ /** Plugin short name → root directory, for plugins in a config folder's `plugins/` or named by the config. */
64
+ function pluginDirs(config, configDir, configDirs) {
65
+ const found = /* @__PURE__ */ new Map();
66
+ const add = (name, fromDir) => {
67
+ const short = name.startsWith(PLUGIN_PREFIX) ? name.slice(PLUGIN_PREFIX.length) : name;
68
+ if (found.has(short)) return;
69
+ const dir = resolvePluginDir(name, fromDir);
70
+ if (dir) found.set(short, dir);
71
+ };
72
+ for (const dir of configDirs) {
73
+ const plugins = join(dir, CONFIG_PLUGINS_DIR);
74
+ if (!isDirectory(plugins)) continue;
75
+ for (const entry of readdirSync(plugins).filter((e) => e.startsWith(PLUGIN_PREFIX)).sort()) add(entry, dir);
76
+ }
77
+ const topLevel = config.plugins ?? {};
78
+ for (const name of Object.keys(topLevel)) add(name, configDir);
79
+ for (const space of config.spaces) {
80
+ const fromDir = space.sourceDir ?? configDir;
81
+ const schemaPlugin = /^([^./][^/]*)\/./.exec(space.schema ?? "")?.[1];
82
+ if (schemaPlugin) add(schemaPlugin, fromDir);
83
+ for (const name of Object.keys(space.plugins ?? {})) add(name, fromDir);
84
+ }
85
+ return [...found];
86
+ }
87
+ function pluginDefinitionsDir(pluginDir) {
88
+ const pkgPath = join(pluginDir, "package.json");
89
+ if (existsSync(pkgPath)) {
90
+ const declared = JSON.parse(readFileSync(pkgPath, "utf8")).sctx?.mapViews;
91
+ if (typeof declared === "string") return resolve(pluginDir, declared);
92
+ }
93
+ return join(pluginDir, DEFINITIONS_DIR);
94
+ }
95
+ function assertIdNamespace(id, source, path) {
96
+ const segments = id.slice(16).split("/");
97
+ const expected = source.kind === "core" ? `${SCHEME}core/<name>` : source.kind === "plugin" ? `${SCHEME}${source.plugin}/<name>` : `${SCHEME}<name>`;
98
+ if (!(source.kind === "config" ? segments.length === 1 : segments.length === 2 && segments[0] === (source.kind === "core" ? "core" : source.plugin)) || source.kind === "plugin" && source.plugin === "core") throw new Error(`Map view namespace violation: ${path} has $id "${id}". Use the form "${expected}".`);
99
+ }
100
+ function isDirectory(path) {
101
+ return existsSync(path) && statSync(path).isDirectory();
102
+ }
103
+ //#endregion
104
+ export { loadMapViewDefinitions };
105
+
106
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","names":[],"sources":["../src/node/bundled-definitions.ts","../src/node/index.ts"],"sourcesContent":["// Generated by scripts/generate-definitions.ts from map-views/*.json5. Do not edit.\nexport const BUNDLED_DEFINITIONS: Record<string, string> = {\n \"strategy-tree.json5\": \"// Strategy stack as a tree: vision → mission → goals → opportunities → solutions → experiments.\\n{\\n $schema: 'https://raw.githubusercontent.com/mindsocket/structured-context-map-views/main/schemas/generated/latest/definition.json',\\n $id: 'sctx-map-view://core/strategy-tree',\\n title: 'Strategy tree',\\n schema: 'sctx://core/strategy_general',\\n parameters: {\\n orientation: { $ref: 'settings.json#/$defs/orientation', default: 'vertical' },\\n nodeStyle: { $ref: 'settings.json#/$defs/nodeStyle', default: 'card' },\\n cardFields: {\\n title: 'Card fields',\\n type: 'array',\\n items: { enum: ['status', 'priority', 'summary'] },\\n default: ['status'],\\n },\\n },\\n regions: [\\n {\\n id: 'main',\\n layout: { type: 'tree', orientation: { $param: 'orientation' } },\\n nodes: { style: { $param: 'nodeStyle' }, fields: { $param: 'cardFields' } },\\n edges: { show: true },\\n },\\n ],\\n}\\n\",\n};\n","/// <reference types=\"node\" />\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { type Config, PLUGIN_PREFIX, resolveConfigPath, resolvePluginDir } from 'structured-context/api';\nimport { type MapViewDefinition, parseMapViewDefinition } from '../core';\nimport { BUNDLED_DEFINITIONS } from './bundled-definitions';\n\n/**\n * Node-only: finds map view definitions the way structured-context finds plugin schemas.\n * Browser-only hosts receive the result from a server, like graph data.\n */\n\nconst SCHEME = 'sctx-map-view://';\nconst DEFINITIONS_DIR = 'map-views';\nconst CONFIG_PLUGINS_DIR = 'plugins';\nconst PACKAGE_NAME = 'structured-context-map-views';\n\ntype Source = { kind: 'core' } | { kind: 'plugin'; plugin: string } | { kind: 'config' };\n\nexport type LoadMapViewDefinitionsOptions = {\n /** Directory of the main config file. Defaults to where structured-context finds it. */\n configDir?: string;\n};\n\n/**\n * Every map view definition available with `config`, from:\n * - this package's embedded definitions (`sctx-map-view://core/<name>`), for structured-context's bundled schemas;\n * - each plugin found from a config folder or named by a space (`sctx-map-view://<plugin>/<name>`): the\n * folder named by `\"sctx\": { \"mapViews\": \"…\" }` in its package.json, else its `map-views/` folder;\n * - a `map-views/` folder next to each config file (`sctx-map-view://<name>`).\n *\n * Each definition's `$id` must be in the namespace of where it was found, and ids must be unique.\n * Throws on the first invalid definition.\n */\nexport function loadMapViewDefinitions(\n config: Config,\n options: LoadMapViewDefinitionsOptions = {},\n): MapViewDefinition[] {\n const configDir = options.configDir ?? dirname(resolve(resolveConfigPath().path));\n const configDirs = [...new Set([configDir, ...config.spaces.flatMap((s) => (s.sourceDir ? [s.sourceDir] : []))])];\n\n const folders: { dir: string; source: Source }[] = [\n ...pluginDirs(config, configDir, configDirs).flatMap(([plugin, dir]) => {\n const definitions = pluginDefinitionsDir(dir);\n return definitions ? [{ dir: definitions, source: { kind: 'plugin', plugin } as const }] : [];\n }),\n ...configDirs.map((dir) => ({ dir: join(dir, DEFINITIONS_DIR), source: { kind: 'config' } as const })),\n ];\n\n const foundAt = new Map<string, string>();\n const definitions: MapViewDefinition[] = [];\n const add = (label: string, text: string, source: Source) => {\n const definition = parseMapViewDefinition(label, text);\n assertIdNamespace(definition.id, source, label);\n const existing = foundAt.get(definition.id);\n if (existing) throw new Error(`Map view id \"${definition.id}\" is defined by both ${existing} and ${label}.`);\n foundAt.set(definition.id, label);\n definitions.push(definition);\n };\n\n for (const [file, text] of Object.entries(BUNDLED_DEFINITIONS))\n add(`${PACKAGE_NAME}/${file}`, text, { kind: 'core' });\n for (const { dir, source } of folders) {\n if (!isDirectory(dir)) continue;\n for (const file of readdirSync(dir)\n .filter((f) => f.endsWith('.json5'))\n .sort()) {\n const path = join(dir, file);\n add(path, readFileSync(path, 'utf8'), source);\n }\n }\n return definitions;\n}\n\n/** Plugin short name → root directory, for plugins in a config folder's `plugins/` or named by the config. */\nfunction pluginDirs(config: Config, configDir: string, configDirs: string[]): [string, string][] {\n const found = new Map<string, string>();\n const add = (name: string, fromDir: string) => {\n const short = name.startsWith(PLUGIN_PREFIX) ? name.slice(PLUGIN_PREFIX.length) : name;\n if (found.has(short)) return;\n const dir = resolvePluginDir(name, fromDir);\n if (dir) found.set(short, dir);\n };\n\n for (const dir of configDirs) {\n const plugins = join(dir, CONFIG_PLUGINS_DIR);\n if (!isDirectory(plugins)) continue;\n for (const entry of readdirSync(plugins)\n .filter((e) => e.startsWith(PLUGIN_PREFIX))\n .sort())\n add(entry, dir);\n }\n const topLevel = (config as { plugins?: Record<string, unknown> }).plugins ?? {};\n for (const name of Object.keys(topLevel)) add(name, configDir);\n for (const space of config.spaces) {\n const fromDir = space.sourceDir ?? configDir;\n // A schema named through a plugin, e.g. \"sctx-wardley-mapping/wardley_map.json\".\n const schemaPlugin = /^([^./][^/]*)\\/./.exec(space.schema ?? '')?.[1];\n if (schemaPlugin) add(schemaPlugin, fromDir);\n for (const name of Object.keys(space.plugins ?? {})) add(name, fromDir);\n }\n return [...found];\n}\n\nfunction pluginDefinitionsDir(pluginDir: string): string | undefined {\n const pkgPath = join(pluginDir, 'package.json');\n if (existsSync(pkgPath)) {\n const declared = JSON.parse(readFileSync(pkgPath, 'utf8')).sctx?.mapViews;\n if (typeof declared === 'string') return resolve(pluginDir, declared);\n }\n return join(pluginDir, DEFINITIONS_DIR);\n}\n\nfunction assertIdNamespace(id: string, source: Source, path: string): void {\n const segments = id.slice(SCHEME.length).split('/');\n const expected =\n source.kind === 'core'\n ? `${SCHEME}core/<name>`\n : source.kind === 'plugin'\n ? `${SCHEME}${source.plugin}/<name>`\n : `${SCHEME}<name>`;\n const ok =\n source.kind === 'config'\n ? segments.length === 1\n : segments.length === 2 && segments[0] === (source.kind === 'core' ? 'core' : source.plugin);\n if (!ok || (source.kind === 'plugin' && source.plugin === 'core')) {\n throw new Error(`Map view namespace violation: ${path} has $id \"${id}\". Use the form \"${expected}\".`);\n }\n}\n\nfunction isDirectory(path: string): boolean {\n return existsSync(path) && statSync(path).isDirectory();\n}\n"],"mappings":"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"}