@burdenoff/microfe-cosmicintersection 2026.704.2 → 2026.709.1

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 (186) hide show
  1. package/dist/cosmicintersection/CosmicIntersectionRoot.d.ts +15 -0
  2. package/dist/cosmicintersection/CosmicIntersectionRoot.d.ts.map +1 -0
  3. package/dist/cosmicintersection/CosmicIntersectionRoutes.d.ts +3 -0
  4. package/dist/cosmicintersection/CosmicIntersectionRoutes.d.ts.map +1 -0
  5. package/dist/cosmicintersection/CosmicIntersectionRoutes.js +39 -27
  6. package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
  7. package/dist/cosmicintersection/api/cosmicApi.d.ts +728 -0
  8. package/dist/cosmicintersection/api/cosmicApi.d.ts.map +1 -0
  9. package/dist/cosmicintersection/api/cosmicApi.js +6 -0
  10. package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
  11. package/dist/cosmicintersection/components/ActionMenu.d.ts +41 -0
  12. package/dist/cosmicintersection/components/ActionMenu.d.ts.map +1 -0
  13. package/dist/cosmicintersection/components/ActivityFeedPanel.d.ts +10 -0
  14. package/dist/cosmicintersection/components/ActivityFeedPanel.d.ts.map +1 -0
  15. package/dist/cosmicintersection/components/DiscussionPanel.d.ts +14 -0
  16. package/dist/cosmicintersection/components/DiscussionPanel.d.ts.map +1 -0
  17. package/dist/cosmicintersection/components/DiscussionPanel.js +9 -9
  18. package/dist/cosmicintersection/components/EmptyState.d.ts +18 -0
  19. package/dist/cosmicintersection/components/EmptyState.d.ts.map +1 -0
  20. package/dist/cosmicintersection/components/EntityFormShell.d.ts +25 -0
  21. package/dist/cosmicintersection/components/EntityFormShell.d.ts.map +1 -0
  22. package/dist/cosmicintersection/components/EntityTagsField.d.ts +20 -0
  23. package/dist/cosmicintersection/components/EntityTagsField.d.ts.map +1 -0
  24. package/dist/cosmicintersection/components/ErrorState.d.ts +14 -0
  25. package/dist/cosmicintersection/components/ErrorState.d.ts.map +1 -0
  26. package/dist/cosmicintersection/components/FacetPanel.d.ts +21 -0
  27. package/dist/cosmicintersection/components/FacetPanel.d.ts.map +1 -0
  28. package/dist/cosmicintersection/components/FacetPanel.js +66 -0
  29. package/dist/cosmicintersection/components/FacetPanel.js.map +1 -0
  30. package/dist/cosmicintersection/components/FileUploadField.d.ts +35 -0
  31. package/dist/cosmicintersection/components/FileUploadField.d.ts.map +1 -0
  32. package/dist/cosmicintersection/components/FileUploadField.js +16 -16
  33. package/dist/cosmicintersection/components/LineageGraph.d.ts +59 -0
  34. package/dist/cosmicintersection/components/LineageGraph.d.ts.map +1 -0
  35. package/dist/cosmicintersection/components/LineageGraph.js +388 -0
  36. package/dist/cosmicintersection/components/LineageGraph.js.map +1 -0
  37. package/dist/cosmicintersection/components/LoadingState.d.ts +15 -0
  38. package/dist/cosmicintersection/components/LoadingState.d.ts.map +1 -0
  39. package/dist/cosmicintersection/components/PageLayout.d.ts +18 -0
  40. package/dist/cosmicintersection/components/PageLayout.d.ts.map +1 -0
  41. package/dist/cosmicintersection/components/Pagination.d.ts +32 -0
  42. package/dist/cosmicintersection/components/Pagination.d.ts.map +1 -0
  43. package/dist/cosmicintersection/components/ResultsChart.d.ts +7 -0
  44. package/dist/cosmicintersection/components/ResultsChart.d.ts.map +1 -0
  45. package/dist/cosmicintersection/components/ResultsChart.js +329 -0
  46. package/dist/cosmicintersection/components/ResultsChart.js.map +1 -0
  47. package/dist/cosmicintersection/components/SafeLink.d.ts +35 -0
  48. package/dist/cosmicintersection/components/SafeLink.d.ts.map +1 -0
  49. package/dist/cosmicintersection/components/StatusBadge.d.ts +23 -0
  50. package/dist/cosmicintersection/components/StatusBadge.d.ts.map +1 -0
  51. package/dist/cosmicintersection/components/StepDAG.d.ts +20 -0
  52. package/dist/cosmicintersection/components/StepDAG.d.ts.map +1 -0
  53. package/dist/cosmicintersection/components/StepDAG.js +104 -0
  54. package/dist/cosmicintersection/components/StepDAG.js.map +1 -0
  55. package/dist/cosmicintersection/components/index.d.ts +18 -0
  56. package/dist/cosmicintersection/components/index.d.ts.map +1 -0
  57. package/dist/cosmicintersection/context/CosmicIntersectionContext.d.ts +26 -0
  58. package/dist/cosmicintersection/context/CosmicIntersectionContext.d.ts.map +1 -0
  59. package/dist/cosmicintersection/index.d.ts +18 -0
  60. package/dist/cosmicintersection/index.d.ts.map +1 -0
  61. package/dist/cosmicintersection/lib/mutationResult.d.ts +23 -0
  62. package/dist/cosmicintersection/lib/mutationResult.d.ts.map +1 -0
  63. package/dist/cosmicintersection/lib/toast.d.ts +22 -0
  64. package/dist/cosmicintersection/lib/toast.d.ts.map +1 -0
  65. package/dist/cosmicintersection/pages/access/AccessPage.d.ts +2 -0
  66. package/dist/cosmicintersection/pages/access/AccessPage.d.ts.map +1 -0
  67. package/dist/cosmicintersection/pages/access/AccessPage.js +207 -155
  68. package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
  69. package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts +2 -0
  70. package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts.map +1 -0
  71. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +325 -165
  72. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
  73. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.d.ts +2 -0
  74. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.d.ts.map +1 -0
  75. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +138 -93
  76. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
  77. package/dist/cosmicintersection/pages/collections/CollectionFormPage.d.ts +2 -0
  78. package/dist/cosmicintersection/pages/collections/CollectionFormPage.d.ts.map +1 -0
  79. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
  80. package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts +2 -0
  81. package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts.map +1 -0
  82. package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts +2 -0
  83. package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts.map +1 -0
  84. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +26 -15
  85. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
  86. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.d.ts +2 -0
  87. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.d.ts.map +1 -0
  88. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +345 -219
  89. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
  90. package/dist/cosmicintersection/pages/datasets/DatasetFormPage.d.ts +2 -0
  91. package/dist/cosmicintersection/pages/datasets/DatasetFormPage.d.ts.map +1 -0
  92. package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts +2 -0
  93. package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts.map +1 -0
  94. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +321 -163
  95. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
  96. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts +2 -0
  97. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts.map +1 -0
  98. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +358 -0
  99. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -0
  100. package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts +2 -0
  101. package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts.map +1 -0
  102. package/dist/cosmicintersection/pages/lineage/LineagePage.js +171 -0
  103. package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -0
  104. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.d.ts +2 -0
  105. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.d.ts.map +1 -0
  106. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +232 -162
  107. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
  108. package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.d.ts +2 -0
  109. package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.d.ts.map +1 -0
  110. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts +2 -0
  111. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts.map +1 -0
  112. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +143 -82
  113. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
  114. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.d.ts +2 -0
  115. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.d.ts.map +1 -0
  116. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +286 -142
  117. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
  118. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.d.ts +2 -0
  119. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.d.ts.map +1 -0
  120. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +305 -103
  121. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
  122. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts +2 -0
  123. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts.map +1 -0
  124. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +183 -79
  125. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
  126. package/dist/cosmicintersection/pages/releases/ReleaseFormPage.d.ts +2 -0
  127. package/dist/cosmicintersection/pages/releases/ReleaseFormPage.d.ts.map +1 -0
  128. package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts +2 -0
  129. package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts.map +1 -0
  130. package/dist/cosmicintersection/pages/releases/ReleasesPage.js +262 -170
  131. package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
  132. package/dist/cosmicintersection/pages/runs/RunDetailPage.d.ts +2 -0
  133. package/dist/cosmicintersection/pages/runs/RunDetailPage.d.ts.map +1 -0
  134. package/dist/cosmicintersection/pages/runs/RunDetailPage.js +179 -136
  135. package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
  136. package/dist/cosmicintersection/pages/runs/RunsPage.d.ts +2 -0
  137. package/dist/cosmicintersection/pages/runs/RunsPage.d.ts.map +1 -0
  138. package/dist/cosmicintersection/pages/runs/RunsPage.js +124 -47
  139. package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
  140. package/dist/cosmicintersection/pages/sources/SourceDetailPage.d.ts +2 -0
  141. package/dist/cosmicintersection/pages/sources/SourceDetailPage.d.ts.map +1 -0
  142. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js +114 -85
  143. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -1
  144. package/dist/cosmicintersection/pages/sources/SourceFormPage.d.ts +2 -0
  145. package/dist/cosmicintersection/pages/sources/SourceFormPage.d.ts.map +1 -0
  146. package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts +2 -0
  147. package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts.map +1 -0
  148. package/dist/cosmicintersection/pages/sources/SourcesPage.js +263 -152
  149. package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
  150. package/dist/cosmicintersection/types/index.d.ts +7 -0
  151. package/dist/cosmicintersection/types/index.d.ts.map +1 -0
  152. package/dist/cosmicintersection/utils/cn.d.ts +4 -0
  153. package/dist/cosmicintersection/utils/cn.d.ts.map +1 -0
  154. package/dist/cosmicintersection/utils/i18n.d.ts +15 -0
  155. package/dist/cosmicintersection/utils/i18n.d.ts.map +1 -0
  156. package/dist/cosmicintersection/utils/navigation.d.ts +2 -0
  157. package/dist/cosmicintersection/utils/navigation.d.ts.map +1 -0
  158. package/dist/dev/main.d.ts +2 -0
  159. package/dist/dev/main.d.ts.map +1 -0
  160. package/dist/generated/global-operations.d.ts +2 -0
  161. package/dist/generated/global-operations.d.ts.map +1 -0
  162. package/dist/generated/global-types.d.ts +18199 -0
  163. package/dist/generated/global-types.d.ts.map +1 -0
  164. package/dist/generated/wspace-operations.d.ts +2 -0
  165. package/dist/generated/wspace-operations.d.ts.map +1 -0
  166. package/dist/generated/wspace-types.d.ts +43046 -0
  167. package/dist/generated/wspace-types.d.ts.map +1 -0
  168. package/dist/index.d.ts +19 -0
  169. package/dist/index.d.ts.map +1 -0
  170. package/dist/index.js +2 -2
  171. package/dist/services/assistantApi.d.ts +66 -0
  172. package/dist/services/assistantApi.d.ts.map +1 -0
  173. package/dist/services/assistantApi.js +250 -0
  174. package/dist/services/assistantApi.js.map +1 -0
  175. package/dist/services/createSandboxAssistantTransport.d.ts +40 -0
  176. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -0
  177. package/dist/services/createSandboxAssistantTransport.js +120 -0
  178. package/dist/services/createSandboxAssistantTransport.js.map +1 -0
  179. package/dist/types/assistant.d.ts +48 -0
  180. package/dist/types/assistant.d.ts.map +1 -0
  181. package/dist/utils/pageContext.d.ts +28 -0
  182. package/dist/utils/pageContext.d.ts.map +1 -0
  183. package/dist/utils/pageContext.js +50 -0
  184. package/dist/utils/pageContext.js.map +1 -0
  185. package/package.json +5 -1
  186. package/dist/cosmicintersection/index.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LineageGraph.js","names":[],"sources":["../../../src/cosmicintersection/components/LineageGraph.tsx"],"sourcesContent":["/**\n * LineageGraph — a reusable, dependency-free provenance DAG for CosmicIntersection.\n *\n * Renders a typed, left-to-right lineage graph (Source → Dataset → Pipeline Run →\n * Notebook / Analysis → Release) as custom inline SVG edges + absolutely-positioned\n * token-styled node cards. No graph library — the layout is a simple column/row\n * solver, edges are cubic-bezier `<path>`s. Every colour comes from fe-libs semantic\n * tokens (bg-*, text-*, border-*, status-*, accent-*) so it renders in every theme.\n *\n * The graph MODEL is derived client-side from existing cos entities by the pure\n * `buildDatasetLineage` / `buildReleaseLineage` builders exported below — the\n * component itself never fetches. Nodes with a `route` are clickable and call\n * `onNavigate` (basePath-relative), matching the rest of the MFE's navigation.\n *\n * Modes:\n * - `full` — page mode (larger cards, legend, dotted canvas). See mockup 20.\n * - `compact` — detail-panel mini map (smaller cards, no legend). See mockup 03.\n */\nimport { useId, useMemo } from 'react';\nimport {\n Boxes,\n Cog,\n Database,\n LineChart,\n NotebookText,\n Radar,\n Tag,\n type LucideIcon,\n} from 'lucide-react';\n\nimport type {\n CosAnalysis,\n CosCollection,\n CosDataset,\n CosNotebook,\n CosPipeline,\n CosPipelineRun,\n CosRelease,\n CosSource,\n} from '../api/cosmicApi';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// MODEL\n// ============================================================================\n\nexport type LineageNodeType =\n | 'source'\n | 'collection'\n | 'dataset'\n | 'run'\n | 'notebook'\n | 'analysis'\n | 'release';\n\nexport interface LineageNode {\n /** Stable, unique within a model (e.g. `ds:<uuid>`). */\n id: string;\n type: LineageNodeType;\n title: string;\n /** Overrides the default type label (e.g. \"Pipeline Run #340\"). */\n typeLabel?: string;\n /** Small third line — status, version, DOI hint (hidden in compact mode). */\n subtitle?: string;\n /** basePath-relative route; when set (and `onNavigate` given) the node is clickable. */\n route?: string;\n /** Semantic column / layer index, 0 = leftmost (upstream). */\n column: number;\n /** Highlight the graph root (the entity the lineage was requested for). */\n emphasis?: boolean;\n}\n\nexport interface LineageEdge {\n from: string;\n to: string;\n}\n\nexport interface LineageModel {\n nodes: LineageNode[];\n edges: LineageEdge[];\n}\n\n/** Data pools the builders read from — resolved maps + already-filtered lists. */\nexport interface LineageDataPools {\n datasetsById: Map<string, CosDataset>;\n sourcesById: Map<string, CosSource>;\n collectionsById: Map<string, CosCollection>;\n runs: CosPipelineRun[];\n notebooks: CosNotebook[];\n analyses: CosAnalysis[];\n releases: CosRelease[];\n pipelinesById?: Map<string, CosPipeline>;\n}\n\n// ============================================================================\n// BUILDERS (pure — no hooks, no fetching)\n// ============================================================================\n\nconst COLUMN: Record<LineageNodeType, number> = {\n source: 0,\n collection: 0,\n dataset: 1,\n run: 2,\n notebook: 3,\n analysis: 4,\n release: 5,\n};\n\nfunction humanEnum(value: string): string {\n const lower = value.replace(/_/g, ' ').toLowerCase();\n return lower.charAt(0).toUpperCase() + lower.slice(1);\n}\n\n/** Incrementally assembles a de-duplicated node/edge model. */\nclass LineageBuilder {\n private readonly nodes = new Map<string, LineageNode>();\n private readonly edgeKeys = new Set<string>();\n private readonly edges: LineageEdge[] = [];\n\n addNode(node: LineageNode): void {\n const existing = this.nodes.get(node.id);\n if (!existing) {\n this.nodes.set(node.id, node);\n return;\n }\n // Preserve an emphasis flag once set (root wins over a later plain add).\n if (node.emphasis && !existing.emphasis) {\n this.nodes.set(node.id, { ...existing, emphasis: true });\n }\n }\n\n link(from: string, to: string): void {\n if (from === to) {\n return;\n }\n // Only connect endpoints that actually exist in the graph.\n if (!this.nodes.has(from) || !this.nodes.has(to)) {\n return;\n }\n const key = `${from}»${to}`;\n if (this.edgeKeys.has(key)) {\n return;\n }\n this.edgeKeys.add(key);\n this.edges.push({ from, to });\n }\n\n has(id: string): boolean {\n return this.nodes.has(id);\n }\n\n build(): LineageModel {\n return { nodes: [...this.nodes.values()], edges: this.edges };\n }\n}\n\nconst nid = {\n source: (id: string) => `src:${id}`,\n collection: (id: string) => `col:${id}`,\n dataset: (id: string) => `ds:${id}`,\n run: (id: string) => `run:${id}`,\n notebook: (id: string) => `nb:${id}`,\n analysis: (id: string) => `an:${id}`,\n release: (id: string) => `rel:${id}`,\n};\n\n/** Add a dataset node plus its upstream source/collection to the builder. */\nfunction addDatasetUpstream(\n builder: LineageBuilder,\n dataset: CosDataset,\n pools: LineageDataPools,\n emphasis: boolean\n): void {\n builder.addNode({\n id: nid.dataset(dataset.id),\n type: 'dataset',\n title: dataset.name,\n subtitle: humanEnum(dataset.status),\n route: `datasets/${dataset.id}`,\n column: COLUMN.dataset,\n emphasis,\n });\n\n if (dataset.sourceId) {\n const source = pools.sourcesById.get(dataset.sourceId);\n builder.addNode({\n id: nid.source(dataset.sourceId),\n type: 'source',\n title: source?.name ?? 'Linked source',\n subtitle: source ? humanEnum(source.type) : undefined,\n route: `sources/${dataset.sourceId}`,\n column: COLUMN.source,\n });\n builder.link(nid.source(dataset.sourceId), nid.dataset(dataset.id));\n }\n\n if (dataset.collectionId) {\n const collection = pools.collectionsById.get(dataset.collectionId);\n builder.addNode({\n id: nid.collection(dataset.collectionId),\n type: 'collection',\n title: collection?.name ?? 'Collection',\n route: `collections/${dataset.collectionId}`,\n column: COLUMN.collection,\n });\n builder.link(nid.collection(dataset.collectionId), nid.dataset(dataset.id));\n }\n}\n\n/** Add the downstream fan for a dataset: runs, notebooks, analyses, releases. */\nfunction addDatasetDownstream(\n builder: LineageBuilder,\n dataset: CosDataset,\n pools: LineageDataPools\n): void {\n const datasetNode = nid.dataset(dataset.id);\n\n for (const run of pools.runs) {\n if (run.datasetId !== dataset.id) {\n continue;\n }\n const pipelineName = run.pipelineId\n ? pools.pipelinesById?.get(run.pipelineId)?.name\n : undefined;\n builder.addNode({\n id: nid.run(run.id),\n type: 'run',\n typeLabel: `Pipeline Run #${run.runNumber}`,\n title: pipelineName ?? `Run #${run.runNumber}`,\n subtitle: humanEnum(run.status),\n route: `runs/${run.id}`,\n column: COLUMN.run,\n });\n builder.link(datasetNode, nid.run(run.id));\n }\n\n for (const notebook of pools.notebooks) {\n if (!notebook.datasetIds.includes(dataset.id)) {\n continue;\n }\n builder.addNode({\n id: nid.notebook(notebook.id),\n type: 'notebook',\n title: notebook.title,\n subtitle: humanEnum(notebook.status),\n route: `notebooks/${notebook.id}`,\n column: COLUMN.notebook,\n });\n builder.link(datasetNode, nid.notebook(notebook.id));\n }\n\n for (const analysis of pools.analyses) {\n const linkedToNotebook =\n analysis.notebookId != null && builder.has(nid.notebook(analysis.notebookId));\n if (analysis.datasetId !== dataset.id && !linkedToNotebook) {\n continue;\n }\n builder.addNode({\n id: nid.analysis(analysis.id),\n type: 'analysis',\n title: analysis.title,\n subtitle: humanEnum(analysis.status),\n // No per-analysis detail route exists — land on the analyses list.\n route: 'analyses',\n column: COLUMN.analysis,\n });\n if (linkedToNotebook && analysis.notebookId != null) {\n builder.link(nid.notebook(analysis.notebookId), nid.analysis(analysis.id));\n } else {\n builder.link(datasetNode, nid.analysis(analysis.id));\n }\n }\n\n for (const release of pools.releases) {\n if (!release.datasetIds.includes(dataset.id)) {\n continue;\n }\n addReleaseNode(builder, release);\n builder.link(datasetNode, nid.release(release.id));\n if (release.notebookId != null && builder.has(nid.notebook(release.notebookId))) {\n builder.link(nid.notebook(release.notebookId), nid.release(release.id));\n }\n }\n}\n\nfunction addReleaseNode(builder: LineageBuilder, release: CosRelease, emphasis = false): void {\n const doiSuffix = release.doi ? ' · DOI' : '';\n builder.addNode({\n id: nid.release(release.id),\n type: 'release',\n title: release.title,\n subtitle: `v${release.version}${doiSuffix}`,\n // No per-release detail route exists — land on the releases list.\n route: 'releases',\n column: COLUMN.release,\n emphasis,\n });\n}\n\n/**\n * Provenance for a single dataset: its upstream (source + collection) and its\n * full downstream fan (runs, notebooks, analyses, releases). The dataset is the\n * emphasised root.\n */\nexport function buildDatasetLineage(datasetId: string, pools: LineageDataPools): LineageModel {\n const builder = new LineageBuilder();\n const dataset = pools.datasetsById.get(datasetId);\n if (!dataset) {\n return builder.build();\n }\n addDatasetUpstream(builder, dataset, pools, true);\n addDatasetDownstream(builder, dataset, pools);\n return builder.build();\n}\n\n/**\n * Provenance for a release: the release plus its input datasets and notebook,\n * each expanded UPSTREAM (source/collection for datasets; the notebook's own\n * datasets). The release is the emphasised root.\n */\nexport function buildReleaseLineage(releaseId: string, pools: LineageDataPools): LineageModel {\n const builder = new LineageBuilder();\n const release = pools.releases.find((item) => item.id === releaseId);\n if (!release) {\n return builder.build();\n }\n\n addReleaseNode(builder, release, true);\n\n for (const datasetId of release.datasetIds) {\n const dataset = pools.datasetsById.get(datasetId);\n if (!dataset) {\n continue;\n }\n addDatasetUpstream(builder, dataset, pools, false);\n builder.link(nid.dataset(dataset.id), nid.release(release.id));\n }\n\n if (release.notebookId != null) {\n const notebook = pools.notebooks.find((item) => item.id === release.notebookId);\n builder.addNode({\n id: nid.notebook(release.notebookId),\n type: 'notebook',\n title: notebook?.title ?? 'Notebook',\n subtitle: notebook ? humanEnum(notebook.status) : undefined,\n route: `notebooks/${release.notebookId}`,\n column: COLUMN.notebook,\n });\n builder.link(nid.notebook(release.notebookId), nid.release(release.id));\n\n for (const datasetId of notebook?.datasetIds ?? []) {\n const dataset = pools.datasetsById.get(datasetId);\n if (!dataset) {\n continue;\n }\n addDatasetUpstream(builder, dataset, pools, false);\n builder.link(nid.dataset(dataset.id), nid.notebook(release.notebookId));\n }\n }\n\n return builder.build();\n}\n\n// ============================================================================\n// PRESENTATION\n// ============================================================================\n\ninterface NodeMeta {\n label: string;\n Icon: LucideIcon;\n /** Icon-chip classes (semantic accent tokens only). */\n chip: string;\n /** Card border colour class. */\n border: string;\n}\n\nconst NODE_META: Record<LineageNodeType, NodeMeta> = {\n source: {\n label: 'Source',\n Icon: Radar,\n chip: 'bg-accent-blue-subtle text-accent-blue',\n border: 'border-accent-blue',\n },\n collection: {\n label: 'Collection',\n Icon: Boxes,\n chip: 'bg-accent-teal-subtle text-accent-teal',\n border: 'border-accent-teal',\n },\n dataset: {\n label: 'Dataset',\n Icon: Database,\n chip: 'bg-accent-indigo-subtle text-accent-indigo',\n border: 'border-accent-indigo',\n },\n run: {\n label: 'Pipeline Run',\n Icon: Cog,\n chip: 'bg-accent-purple-subtle text-accent-purple',\n border: 'border-accent-purple',\n },\n notebook: {\n label: 'Notebook',\n Icon: NotebookText,\n chip: 'bg-accent-teal-subtle text-accent-teal',\n border: 'border-accent-teal',\n },\n analysis: {\n label: 'Analysis',\n Icon: LineChart,\n chip: 'bg-accent-purple-subtle text-accent-purple',\n border: 'border-accent-purple',\n },\n release: {\n label: 'Release',\n Icon: Tag,\n chip: 'bg-status-success-bg-subtle text-status-success-text',\n border: 'border-status-success-border',\n },\n};\n\ninterface Dims {\n nodeW: number;\n nodeH: number;\n colGap: number;\n rowGap: number;\n pad: number;\n iconSize: string;\n showSubtitle: boolean;\n}\n\nconst DIMS: Record<'full' | 'compact', Dims> = {\n full: {\n nodeW: 190,\n nodeH: 78,\n colGap: 74,\n rowGap: 30,\n pad: 32,\n iconSize: 'size-8',\n showSubtitle: true,\n },\n compact: {\n nodeW: 150,\n nodeH: 54,\n colGap: 46,\n rowGap: 18,\n pad: 18,\n iconSize: 'size-6',\n showSubtitle: false,\n },\n};\n\ninterface Positioned {\n node: LineageNode;\n x: number;\n y: number;\n}\n\ninterface Layout {\n positions: Map<string, Positioned>;\n width: number;\n height: number;\n}\n\nfunction computeLayout(model: LineageModel, dims: Dims): Layout {\n const positions = new Map<string, Positioned>();\n if (model.nodes.length === 0) {\n return { positions, width: dims.pad * 2, height: dims.pad * 2 };\n }\n\n // Present columns, remapped to contiguous indices so empty layers collapse.\n const presentCols = [...new Set(model.nodes.map((n) => n.column))].sort((a, b) => a - b);\n const colOrder = new Map(presentCols.map((c, i) => [c, i]));\n\n const byColumn = new Map<number, LineageNode[]>();\n for (const node of model.nodes) {\n const list = byColumn.get(node.column);\n if (list) {\n list.push(node);\n } else {\n byColumn.set(node.column, [node]);\n }\n }\n\n let contentH = dims.nodeH;\n for (const list of byColumn.values()) {\n const h = list.length * dims.nodeH + (list.length - 1) * dims.rowGap;\n contentH = Math.max(contentH, h);\n }\n\n for (const col of presentCols) {\n const list = byColumn.get(col) ?? [];\n const colH = list.length * dims.nodeH + (list.length - 1) * dims.rowGap;\n const startY = dims.pad + (contentH - colH) / 2;\n const x = dims.pad + (colOrder.get(col) ?? 0) * (dims.nodeW + dims.colGap);\n list.forEach((node, i) => {\n positions.set(node.id, { node, x, y: startY + i * (dims.nodeH + dims.rowGap) });\n });\n }\n\n const width =\n dims.pad * 2 + presentCols.length * dims.nodeW + (presentCols.length - 1) * dims.colGap;\n const height = dims.pad * 2 + contentH;\n return { positions, width, height };\n}\n\nfunction edgePath(x1: number, y1: number, x2: number, y2: number): string {\n const dx = Math.max(24, (x2 - x1) * 0.5);\n return `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`;\n}\n\nexport interface LineageGraphProps {\n model: LineageModel;\n mode?: 'full' | 'compact';\n onNavigate?: (route: string) => void;\n className?: string;\n}\n\nexport function LineageGraph({ model, mode = 'full', onNavigate, className }: LineageGraphProps) {\n const markerId = `cos-lineage-arrow-${useId().replace(/:/g, '')}`;\n const dims = DIMS[mode];\n\n const layout = useMemo(() => computeLayout(model, dims), [model, dims]);\n\n const legendTypes = useMemo(() => {\n const seen = new Set<LineageNodeType>();\n const ordered: LineageNodeType[] = [];\n for (const node of [...model.nodes].sort((a, b) => a.column - b.column)) {\n if (!seen.has(node.type)) {\n seen.add(node.type);\n ordered.push(node.type);\n }\n }\n return ordered;\n }, [model.nodes]);\n\n if (model.nodes.length === 0) {\n return null;\n }\n\n return (\n <div className={cn('flex flex-col gap-3', className)}>\n {mode === 'full' && legendTypes.length > 0 ? (\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2\">\n {legendTypes.map((type) => {\n const meta = NODE_META[type];\n return (\n <span\n key={type}\n className=\"inline-flex items-center gap-1.5 text-xs text-text-secondary\"\n >\n <span\n className={cn(\n 'flex size-5 items-center justify-center rounded-button',\n meta.chip\n )}\n >\n <meta.Icon className=\"size-3\" aria-hidden=\"true\" />\n </span>\n {meta.label}\n </span>\n );\n })}\n </div>\n ) : null}\n\n <div\n className={cn(\n 'overflow-x-auto rounded-card border border-border-seam bg-bg-canvas',\n mode === 'full' ? 'p-1' : 'p-0.5'\n )}\n >\n <div\n className=\"relative\"\n style={{\n width: layout.width,\n height: layout.height,\n minWidth: '100%',\n backgroundImage: 'radial-gradient(var(--color-border-subtle) 1px, transparent 1px)',\n backgroundSize: mode === 'full' ? '20px 20px' : '16px 16px',\n }}\n >\n {/* Edges (behind the node cards). */}\n <svg\n className=\"pointer-events-none absolute inset-0 text-border-strong\"\n width={layout.width}\n height={layout.height}\n viewBox={`0 0 ${layout.width} ${layout.height}`}\n aria-hidden=\"true\"\n >\n <defs>\n <marker\n id={markerId}\n viewBox=\"0 0 8 8\"\n refX=\"6.5\"\n refY=\"4\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n >\n <path d=\"M1 1 L7 4 L1 7 z\" fill=\"currentColor\" />\n </marker>\n </defs>\n {model.edges.map((edge) => {\n const from = layout.positions.get(edge.from);\n const to = layout.positions.get(edge.to);\n if (!from || !to) {\n return null;\n }\n const x1 = from.x + dims.nodeW;\n const y1 = from.y + dims.nodeH / 2;\n const x2 = to.x;\n const y2 = to.y + dims.nodeH / 2;\n return (\n <path\n key={`${edge.from}»${edge.to}`}\n d={edgePath(x1, y1, x2, y2)}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={mode === 'full' ? 1.5 : 1.25}\n strokeLinecap=\"round\"\n markerEnd={`url(#${markerId})`}\n opacity={0.7}\n />\n );\n })}\n </svg>\n\n {/* Node cards. */}\n {[...layout.positions.values()].map(({ node, x, y }) => {\n const meta = NODE_META[node.type];\n const clickable = Boolean(node.route && onNavigate);\n const content = (\n <div\n className={cn(\n 'flex h-full w-full items-center gap-2.5 rounded-card border bg-bg-elevated px-3 shadow-sm transition-shadow',\n meta.border,\n clickable && 'group-hover:shadow-md',\n node.emphasis && 'ring-1 ring-border-strong'\n )}\n >\n <span\n className={cn(\n 'flex shrink-0 items-center justify-center rounded-button',\n dims.iconSize,\n meta.chip\n )}\n aria-hidden=\"true\"\n >\n <meta.Icon className={mode === 'full' ? 'size-4' : 'size-3.5'} />\n </span>\n <span className=\"flex min-w-0 flex-1 flex-col text-left\">\n <span className=\"truncate text-[10px] font-medium uppercase tracking-wide text-text-tertiary\">\n {node.typeLabel ?? meta.label}\n </span>\n <span\n className={cn(\n 'truncate font-semibold text-text-primary',\n mode === 'full' ? 'text-sm' : 'text-xs'\n )}\n >\n {node.title}\n </span>\n {dims.showSubtitle && node.subtitle ? (\n <span className=\"truncate text-[11px] text-text-secondary\">\n {node.subtitle}\n </span>\n ) : null}\n </span>\n </div>\n );\n\n const style = { left: x, top: y, width: dims.nodeW, height: dims.nodeH } as const;\n\n if (clickable && node.route) {\n const route = node.route;\n return (\n <button\n key={node.id}\n type=\"button\"\n onClick={() => onNavigate?.(route)}\n className=\"group absolute rounded-card focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n style={style}\n title={`${node.typeLabel ?? meta.label}: ${node.title}`}\n >\n {content}\n </button>\n );\n }\n\n return (\n <div key={node.id} className=\"absolute\" style={style}>\n {content}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n}\n\nexport default LineageGraph;\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface LoadingStateProps {
2
+ /** `spinner` centres a spinner; `rows` renders shimmering list placeholders. */
3
+ variant?: 'spinner' | 'rows';
4
+ /** Number of placeholder rows for the `rows` variant. */
5
+ rows?: number;
6
+ label?: string;
7
+ className?: string;
8
+ }
9
+ /**
10
+ * Consistent loading affordance for every CosmicIntersection query surface.
11
+ * Token-styled only — no raw palette colours.
12
+ */
13
+ export declare function LoadingState({ variant, rows, label, className, }: LoadingStateProps): import("react/jsx-runtime").JSX.Element;
14
+ export default LoadingState;
15
+ //# sourceMappingURL=LoadingState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoadingState.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/LoadingState.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAmB,EACnB,IAAQ,EACR,KAAkB,EAClB,SAAS,GACV,EAAE,iBAAiB,2CAqBnB;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ interface PageLayoutProps {
4
+ title: ReactNode;
5
+ description?: ReactNode;
6
+ actions?: ReactNode;
7
+ children: ReactNode;
8
+ /** basePath-relative path for the header back button (e.g. "datasets"). */
9
+ backPath?: string;
10
+ }
11
+ /**
12
+ * Standard page scaffold: a sticky-feeling header (optional back button, title,
13
+ * description, right-aligned actions) over a scrollable content region. Token
14
+ * styling only.
15
+ */
16
+ export declare function PageLayout({ title, description, actions, children, backPath }: PageLayoutProps): import("react/jsx-runtime").JSX.Element;
17
+ export default PageLayout;
18
+ //# sourceMappingURL=PageLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/PageLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAkC9F;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,32 @@
1
+ export interface UsePaginationOptions {
2
+ pageSize?: number;
3
+ /** When this changes (a filter/search term), the window resets to page 1. */
4
+ resetKey?: unknown;
5
+ }
6
+ export interface UsePaginationResult {
7
+ limit: number;
8
+ offset: number;
9
+ /** 1-based page index derived from offset/limit. */
10
+ page: number;
11
+ nextPage: () => void;
12
+ prevPage: () => void;
13
+ reset: () => void;
14
+ }
15
+ export declare function usePagination(options?: UsePaginationOptions): UsePaginationResult;
16
+ export interface PaginationProps {
17
+ offset: number;
18
+ limit: number;
19
+ /** Number of rows returned for the current page (`items.length`). */
20
+ loadedCount: number;
21
+ /** Whether the backend reports more rows after this page (`pageInfo.hasMore`). */
22
+ hasMore: boolean;
23
+ onPrev: () => void;
24
+ onNext: () => void;
25
+ /** Plural noun for the range label, e.g. "datasets". Defaults to "items". */
26
+ itemLabel?: string;
27
+ loading?: boolean;
28
+ className?: string;
29
+ }
30
+ export declare function Pagination({ offset, limit, loadedCount, hasMore, onPrev, onNext, itemLabel, loading, className, }: PaginationProps): import("react/jsx-runtime").JSX.Element | null;
31
+ export default Pagination;
32
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/Pagination.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAsBrF;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,WAAW,EACX,OAAO,EACP,MAAM,EACN,MAAM,EACN,SAAmB,EACnB,OAAe,EACf,SAAS,GACV,EAAE,eAAe,kDA8CjB;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface ResultsChartProps {
2
+ results: Record<string, unknown>;
3
+ className?: string;
4
+ }
5
+ export declare function ResultsChart({ results, className }: ResultsChartProps): import("react/jsx-runtime").JSX.Element;
6
+ export default ResultsChart;
7
+ //# sourceMappingURL=ResultsChart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResultsChart.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/ResultsChart.tsx"],"names":[],"mappings":"AA8YA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CAiLrE;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,329 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useMemo as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region src/cosmicintersection/components/ResultsChart.tsx
5
+ var i = [
6
+ 1,
7
+ 2,
8
+ 3,
9
+ 4,
10
+ 5,
11
+ 6
12
+ ].map((e) => `var(--color-chart-${e})`), a = 600;
13
+ function o(e) {
14
+ return typeof e == "number" && Number.isFinite(e);
15
+ }
16
+ function s(e) {
17
+ let t = e.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
18
+ return t ? t.charAt(0).toUpperCase() + t.slice(1) : e;
19
+ }
20
+ function c(e, t) {
21
+ if (o(e)) return {
22
+ x: t,
23
+ y: e
24
+ };
25
+ if (Array.isArray(e)) {
26
+ let [n, r] = e;
27
+ return o(n) && o(r) ? {
28
+ x: n,
29
+ y: r
30
+ } : o(n) && r === void 0 ? {
31
+ x: t,
32
+ y: n
33
+ } : null;
34
+ }
35
+ if (e && typeof e == "object") {
36
+ let n = e, r = n.y ?? n.value ?? n.val ?? n.count;
37
+ if (!o(r)) return null;
38
+ let i = n.x ?? n.t ?? n.time ?? n.index ?? n.bin;
39
+ return {
40
+ x: o(i) ? i : t,
41
+ y: r
42
+ };
43
+ }
44
+ return null;
45
+ }
46
+ function l(e) {
47
+ let t = [];
48
+ for (let n = 0; n < e.length; n += 1) {
49
+ let r = c(e[n], n);
50
+ r && t.push(r);
51
+ }
52
+ return t;
53
+ }
54
+ function u(e) {
55
+ if (e.length <= a) return e;
56
+ let t = e.length / a, n = [];
57
+ for (let r = 0; r < a; r += 1) n.push(e[Math.floor(r * t)]);
58
+ return n;
59
+ }
60
+ function d(e) {
61
+ if (/scatter|point|dot|observ|raw/i.test(e)) return "scatter";
62
+ if (/line|fit|curve|model|trend|series/i.test(e)) return "line";
63
+ }
64
+ function f(e) {
65
+ let t = [], n = 0, r = () => {
66
+ let e = i[n % i.length];
67
+ return n += 1, e;
68
+ }, a = (e, n, i) => {
69
+ let a = u(l(e));
70
+ a.length >= 2 && t.push({
71
+ name: n,
72
+ kind: i,
73
+ points: a,
74
+ colorVar: r()
75
+ });
76
+ }, o = e.series;
77
+ if (Array.isArray(o) && o.forEach((e, t) => {
78
+ if (Array.isArray(e)) {
79
+ a(e, `Series ${t + 1}`, "line");
80
+ return;
81
+ }
82
+ if (e && typeof e == "object") {
83
+ let n = e, r = n.points ?? n.data ?? n.values;
84
+ if (!Array.isArray(r)) return;
85
+ a(r, typeof n.name == "string" && n.name.trim() ? n.name : typeof n.label == "string" && n.label.trim() ? n.label : `Series ${t + 1}`, d([
86
+ n.kind,
87
+ n.type,
88
+ n.name,
89
+ n.label
90
+ ].filter((e) => typeof e == "string").join(" ")) ?? "line");
91
+ }
92
+ }), t.length === 0 && (Array.isArray(e.points) ? a(e.points, "Observations", "scatter") : Array.isArray(e.data) ? a(e.data, "Data", "line") : Array.isArray(e.values) && a(e.values, "Values", "line"), Array.isArray(e.fit) && a(e.fit, "Fit", "line"), Array.isArray(e.model) && a(e.model, "Model", "line")), t.length === 0) {
93
+ for (let [n, r] of Object.entries(e)) if (Array.isArray(r) && a(r, s(n), "line"), t.length >= i.length) break;
94
+ }
95
+ return t;
96
+ }
97
+ function p(e, t, n, r) {
98
+ for (let n of t) {
99
+ let t = e[n];
100
+ if (typeof t == "string" && t.trim()) return t;
101
+ }
102
+ let i = e.axes ?? e.axis;
103
+ if (i && typeof i == "object") {
104
+ let e = i[n];
105
+ if (typeof e == "string" && e.trim()) return e;
106
+ }
107
+ return r;
108
+ }
109
+ function m(e, t, n) {
110
+ if (!Number.isFinite(e) || !Number.isFinite(t) || e === t) return [e];
111
+ let r = (t - e) / Math.max(1, n), i = 10 ** Math.floor(Math.log10(r)), a = r / i, o = (a < 1.5 ? 1 : a < 3 ? 2 : a < 7 ? 5 : 10) * i, s = Math.floor(e / o) * o, c = Math.ceil(t / o) * o, l = [];
112
+ for (let e = s; e <= c + o * 1e-6; e += o) l.push(Number(e.toFixed(12)));
113
+ return l;
114
+ }
115
+ function h(e) {
116
+ if (e === 0) return "0";
117
+ let t = Math.abs(e);
118
+ return t < .001 || t >= 1e6 ? e.toExponential(1) : Number(e.toPrecision(4)).toLocaleString(void 0, { maximumFractionDigits: 4 });
119
+ }
120
+ var g = 640, _ = 340, v = {
121
+ top: 16,
122
+ right: 20,
123
+ bottom: 48,
124
+ left: 62
125
+ }, y = g - v.left - v.right, b = _ - v.top - v.bottom;
126
+ function x(e) {
127
+ let t = f(e);
128
+ if (t.length === 0) return null;
129
+ let n = [], r = [];
130
+ for (let e of t) for (let t of e.points) n.push(t.x), r.push(t.y);
131
+ let i = Math.min(...n), a = Math.max(...n), o = Math.min(...r), s = Math.max(...r);
132
+ if (i === a) {
133
+ let e = Math.abs(i) || 1;
134
+ i -= e, a += e;
135
+ }
136
+ if (o === s) {
137
+ let e = Math.abs(o) || 1;
138
+ o -= e, s += e;
139
+ }
140
+ let c = (s - o) * .08;
141
+ o -= c, s += c;
142
+ let l = m(i, a, 5), u = m(o, s, 5), d = [Math.min(i, l[0]), Math.max(a, l[l.length - 1])], h = [Math.min(o, u[0]), Math.max(s, u[u.length - 1])];
143
+ return {
144
+ series: t,
145
+ xTicks: l.filter((e) => e >= d[0] && e <= d[1]),
146
+ yTicks: u.filter((e) => e >= h[0] && e <= h[1]),
147
+ domainX: d,
148
+ domainY: h,
149
+ xLabel: p(e, [
150
+ "xLabel",
151
+ "xlabel",
152
+ "xAxis",
153
+ "xaxis",
154
+ "xTitle",
155
+ "xUnit"
156
+ ], "x", "x"),
157
+ yLabel: p(e, [
158
+ "yLabel",
159
+ "ylabel",
160
+ "yAxis",
161
+ "yaxis",
162
+ "yTitle",
163
+ "yUnit"
164
+ ], "y", "y")
165
+ };
166
+ }
167
+ function S(e) {
168
+ if (e == null) return "—";
169
+ if (typeof e == "number") return e.toLocaleString(void 0, { maximumFractionDigits: 6 });
170
+ if (typeof e == "boolean") return e ? "Yes" : "No";
171
+ if (typeof e == "string") return e;
172
+ if (Array.isArray(e)) return `${e.length} item${e.length === 1 ? "" : "s"}`;
173
+ try {
174
+ return JSON.stringify(e);
175
+ } catch {
176
+ return String(e);
177
+ }
178
+ }
179
+ function C({ results: e }) {
180
+ let t = Object.entries(e);
181
+ return t.length === 0 ? /* @__PURE__ */ n("p", {
182
+ className: "rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary",
183
+ children: "No structured results were recorded for this analysis."
184
+ }) : /* @__PURE__ */ n("dl", {
185
+ className: "grid grid-cols-1 gap-px overflow-hidden rounded-card border border-border-default bg-border-subtle sm:grid-cols-2",
186
+ children: t.map(([e, t]) => {
187
+ let i = S(t);
188
+ return /* @__PURE__ */ r("div", {
189
+ className: "flex items-baseline justify-between gap-3 bg-bg-elevated px-3 py-2",
190
+ children: [/* @__PURE__ */ n("dt", {
191
+ className: "min-w-0 truncate text-xs font-medium uppercase tracking-wide text-text-secondary",
192
+ children: s(e)
193
+ }), /* @__PURE__ */ n("dd", {
194
+ className: "max-w-[60%] truncate text-right text-sm tabular-nums text-text-primary",
195
+ title: i,
196
+ children: i
197
+ })]
198
+ }, e);
199
+ })
200
+ });
201
+ }
202
+ function w({ results: i, className: a }) {
203
+ let o = t(() => x(i), [i]);
204
+ if (!o) return /* @__PURE__ */ n("div", {
205
+ className: a,
206
+ children: /* @__PURE__ */ n(C, { results: i })
207
+ });
208
+ let { series: s, xTicks: c, yTicks: l, domainX: u, domainY: d, xLabel: f, yLabel: p } = o, m = (e) => v.left + (e - u[0]) / (u[1] - u[0]) * y, S = (e) => v.top + b - (e - d[0]) / (d[1] - d[0]) * b, w = s.length > 1, T = `Chart of ${s.map((e) => e.name).join(", ")}; x-axis ${f}, y-axis ${p}`;
209
+ return /* @__PURE__ */ r("div", {
210
+ className: e("space-y-3", a),
211
+ children: [/* @__PURE__ */ n("div", {
212
+ className: "rounded-card border border-border-default bg-bg-sunken p-3",
213
+ children: /* @__PURE__ */ r("svg", {
214
+ viewBox: `0 0 ${g} ${_}`,
215
+ className: "h-auto w-full",
216
+ preserveAspectRatio: "xMidYMid meet",
217
+ role: "img",
218
+ "aria-label": T,
219
+ children: [
220
+ l.map((e) => {
221
+ let t = S(e);
222
+ return /* @__PURE__ */ r("g", { children: [/* @__PURE__ */ n("line", {
223
+ x1: v.left,
224
+ x2: v.left + y,
225
+ y1: t,
226
+ y2: t,
227
+ stroke: "var(--color-chart-grid)",
228
+ strokeWidth: 1
229
+ }), /* @__PURE__ */ n("text", {
230
+ x: v.left - 8,
231
+ y: t,
232
+ textAnchor: "end",
233
+ dominantBaseline: "central",
234
+ fontSize: 11,
235
+ fill: "var(--color-text-tertiary)",
236
+ children: h(e)
237
+ })] }, `y-${e}`);
238
+ }),
239
+ c.map((e) => {
240
+ let t = m(e);
241
+ return /* @__PURE__ */ r("g", { children: [/* @__PURE__ */ n("line", {
242
+ x1: t,
243
+ x2: t,
244
+ y1: v.top,
245
+ y2: v.top + b,
246
+ stroke: "var(--color-chart-grid)",
247
+ strokeWidth: 1,
248
+ strokeDasharray: "2 4"
249
+ }), /* @__PURE__ */ n("text", {
250
+ x: t,
251
+ y: v.top + b + 18,
252
+ textAnchor: "middle",
253
+ fontSize: 11,
254
+ fill: "var(--color-text-tertiary)",
255
+ children: h(e)
256
+ })] }, `x-${e}`);
257
+ }),
258
+ /* @__PURE__ */ n("rect", {
259
+ x: v.left,
260
+ y: v.top,
261
+ width: y,
262
+ height: b,
263
+ fill: "none",
264
+ stroke: "var(--color-chart-axis)",
265
+ strokeWidth: 1
266
+ }),
267
+ s.map((e) => {
268
+ let t = [...e.points].sort((e, t) => e.x - t.x).map((e, t) => `${t === 0 ? "M" : "L"} ${m(e.x).toFixed(2)} ${S(e.y).toFixed(2)}`).join(" "), i = e.kind === "scatter" || e.points.length <= 40;
269
+ return /* @__PURE__ */ r("g", { children: [e.kind === "line" ? /* @__PURE__ */ n("path", {
270
+ d: t,
271
+ fill: "none",
272
+ stroke: e.colorVar,
273
+ strokeWidth: 2,
274
+ strokeLinejoin: "round",
275
+ strokeLinecap: "round"
276
+ }) : null, i ? e.points.map((t, r) => /* @__PURE__ */ n("circle", {
277
+ cx: m(t.x),
278
+ cy: S(t.y),
279
+ r: e.kind === "scatter" ? 2.4 : 2.8,
280
+ fill: e.colorVar,
281
+ fillOpacity: e.kind === "scatter" ? .85 : 1
282
+ }, `${e.name}-${r}`)) : null] }, e.name);
283
+ }),
284
+ /* @__PURE__ */ n("text", {
285
+ x: v.left + y / 2,
286
+ y: _ - 6,
287
+ textAnchor: "middle",
288
+ fontSize: 12,
289
+ fill: "var(--color-text-secondary)",
290
+ children: f
291
+ }),
292
+ /* @__PURE__ */ n("text", {
293
+ x: 14,
294
+ y: v.top + b / 2,
295
+ textAnchor: "middle",
296
+ fontSize: 12,
297
+ fill: "var(--color-text-secondary)",
298
+ transform: `rotate(-90 14 ${v.top + b / 2})`,
299
+ children: p
300
+ })
301
+ ]
302
+ })
303
+ }), w ? /* @__PURE__ */ n("ul", {
304
+ className: "flex flex-wrap gap-x-4 gap-y-1.5",
305
+ children: s.map((e) => /* @__PURE__ */ r("li", {
306
+ className: "flex items-center gap-1.5 text-xs text-text-secondary",
307
+ children: [/* @__PURE__ */ n("span", {
308
+ "aria-hidden": "true",
309
+ className: "inline-block size-2.5 rounded-full",
310
+ style: { backgroundColor: e.colorVar }
311
+ }), /* @__PURE__ */ r("span", {
312
+ className: "truncate",
313
+ children: [e.name, /* @__PURE__ */ r("span", {
314
+ className: "ml-1 text-text-tertiary",
315
+ children: [
316
+ "(",
317
+ e.kind === "scatter" ? "points" : "line",
318
+ ")"
319
+ ]
320
+ })]
321
+ })]
322
+ }, e.name))
323
+ }) : null]
324
+ });
325
+ }
326
+ //#endregion
327
+ export { w as default };
328
+
329
+ //# sourceMappingURL=ResultsChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResultsChart.js","names":[],"sources":["../../../src/cosmicintersection/components/ResultsChart.tsx"],"sourcesContent":["/**\n * ResultsChart — inline visualization for a completed analysis' `results` JSON.\n *\n * There is NO chart library here: axes, gridlines, scatter dots and line series\n * are hand-drawn as SVG using the design system's dataviz tokens\n * (`var(--color-chart-*)`, `var(--color-chart-axis)`, `var(--color-chart-grid)`)\n * plus the standard text tokens — never a raw palette colour. The SVG uses a\n * `viewBox` and `width:100%`, so it scales fluidly on phones (no horizontal\n * scroll).\n *\n * The `results` payload is free-form JSON (whatever `cosCompleteAnalysis` stored),\n * so the component sniffs it for numeric series. Accepted shapes:\n * - `results.series: Array<{ name?, kind?: 'line'|'scatter', points }>`\n * - `results.points` (scatter) and/or `results.fit` / `results.model` (line)\n * - any top-level numeric arrays (rendered as line series)\n * where a `points` array may hold `{x,y}` objects, `[x,y]` pairs, `{value}` /\n * `{y}` objects, or bare numbers (index used as x).\n *\n * When nothing chartable is found, a compact, responsive key/value metrics list\n * is rendered instead. Everything comes from the real payload — nothing is\n * fabricated; an empty payload renders an honest \"no results\" message.\n */\nimport { useMemo } from 'react';\n\nimport { cn } from '../utils/cn';\n\ntype Json = Record<string, unknown>;\n\ninterface Point {\n x: number;\n y: number;\n}\n\ntype SeriesKind = 'line' | 'scatter';\n\ninterface Series {\n name: string;\n kind: SeriesKind;\n points: Point[];\n colorVar: string;\n}\n\nconst SERIES_PALETTE = [1, 2, 3, 4, 5, 6].map((n) => `var(--color-chart-${n})`);\n\nconst MAX_POINTS_PER_SERIES = 600;\n\n// ---------------------------------------------------------------------------\n// Payload parsing\n// ---------------------------------------------------------------------------\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nfunction humanize(key: string): string {\n const spaced = key\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .trim();\n if (!spaced) {\n return key;\n }\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\n/** Coerce a single raw element into a {x,y} point (index is the fallback x). */\nfunction toPoint(element: unknown, index: number): Point | null {\n if (isFiniteNumber(element)) {\n return { x: index, y: element };\n }\n if (Array.isArray(element)) {\n const [a, b] = element;\n if (isFiniteNumber(a) && isFiniteNumber(b)) {\n return { x: a, y: b };\n }\n if (isFiniteNumber(a) && b === undefined) {\n return { x: index, y: a };\n }\n return null;\n }\n if (element && typeof element === 'object') {\n const obj = element as Record<string, unknown>;\n const yRaw = obj.y ?? obj.value ?? obj.val ?? obj.count;\n if (!isFiniteNumber(yRaw)) {\n return null;\n }\n const xRaw = obj.x ?? obj.t ?? obj.time ?? obj.index ?? obj.bin;\n return { x: isFiniteNumber(xRaw) ? xRaw : index, y: yRaw };\n }\n return null;\n}\n\n/** Map an array to points, dropping unparseable elements. */\nfunction pointsFrom(raw: unknown[]): Point[] {\n const points: Point[] = [];\n for (let i = 0; i < raw.length; i += 1) {\n const point = toPoint(raw[i], i);\n if (point) {\n points.push(point);\n }\n }\n return points;\n}\n\n/** Evenly downsample a long series so the SVG stays light on huge payloads. */\nfunction sample(points: Point[]): Point[] {\n if (points.length <= MAX_POINTS_PER_SERIES) {\n return points;\n }\n const stride = points.length / MAX_POINTS_PER_SERIES;\n const out: Point[] = [];\n for (let i = 0; i < MAX_POINTS_PER_SERIES; i += 1) {\n out.push(points[Math.floor(i * stride)]);\n }\n return out;\n}\n\nfunction kindFrom(raw: string): SeriesKind | undefined {\n if (/scatter|point|dot|observ|raw/i.test(raw)) {\n return 'scatter';\n }\n if (/line|fit|curve|model|trend|series/i.test(raw)) {\n return 'line';\n }\n return undefined;\n}\n\n/** Sniff the payload for one or more numeric series (empty = not chartable). */\nfunction extractSeries(results: Json): Series[] {\n const out: Series[] = [];\n let colorIndex = 0;\n const nextColor = (): string => {\n const color = SERIES_PALETTE[colorIndex % SERIES_PALETTE.length];\n colorIndex += 1;\n return color;\n };\n const push = (raw: unknown[], name: string, kind: SeriesKind): void => {\n const points = sample(pointsFrom(raw));\n if (points.length >= 2) {\n out.push({ name, kind, points, colorVar: nextColor() });\n }\n };\n\n // 1. Explicit `series` array — the richest shape.\n const series = results.series;\n if (Array.isArray(series)) {\n series.forEach((entry, i) => {\n if (Array.isArray(entry)) {\n push(entry, `Series ${i + 1}`, 'line');\n return;\n }\n if (entry && typeof entry === 'object') {\n const so = entry as Record<string, unknown>;\n const data = so.points ?? so.data ?? so.values;\n if (!Array.isArray(data)) {\n return;\n }\n const name =\n typeof so.name === 'string' && so.name.trim()\n ? so.name\n : typeof so.label === 'string' && so.label.trim()\n ? so.label\n : `Series ${i + 1}`;\n const kindRaw = [so.kind, so.type, so.name, so.label]\n .filter((v): v is string => typeof v === 'string')\n .join(' ');\n push(data, name, kindFrom(kindRaw) ?? 'line');\n }\n });\n }\n\n // 2. `points` (scatter) plus optional `fit` / `model` overlay line.\n if (out.length === 0) {\n if (Array.isArray(results.points)) {\n push(results.points, 'Observations', 'scatter');\n } else if (Array.isArray(results.data)) {\n push(results.data, 'Data', 'line');\n } else if (Array.isArray(results.values)) {\n push(results.values, 'Values', 'line');\n }\n if (Array.isArray(results.fit)) {\n push(results.fit, 'Fit', 'line');\n }\n if (Array.isArray(results.model)) {\n push(results.model, 'Model', 'line');\n }\n }\n\n // 3. Scan for the first few bare numeric arrays (e.g. { loss, accuracy }).\n if (out.length === 0) {\n for (const [key, value] of Object.entries(results)) {\n if (Array.isArray(value)) {\n push(value, humanize(key), 'line');\n }\n if (out.length >= SERIES_PALETTE.length) {\n break;\n }\n }\n }\n\n return out;\n}\n\nfunction readLabel(results: Json, keys: string[], axisKey: 'x' | 'y', fallback: string): string {\n for (const key of keys) {\n const value = results[key];\n if (typeof value === 'string' && value.trim()) {\n return value;\n }\n }\n const axes = results.axes ?? results.axis;\n if (axes && typeof axes === 'object') {\n const value = (axes as Record<string, unknown>)[axisKey];\n if (typeof value === 'string' && value.trim()) {\n return value;\n }\n }\n return fallback;\n}\n\n// ---------------------------------------------------------------------------\n// Axis math\n// ---------------------------------------------------------------------------\n\n/** Rounded, human-friendly tick values across [min, max]. */\nfunction niceTicks(min: number, max: number, count: number): number[] {\n if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {\n return [min];\n }\n const span = max - min;\n const rawStep = span / Math.max(1, count);\n const mag = Math.pow(10, Math.floor(Math.log10(rawStep)));\n const norm = rawStep / mag;\n const niceNorm = norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10;\n const step = niceNorm * mag;\n const start = Math.floor(min / step) * step;\n const end = Math.ceil(max / step) * step;\n const ticks: number[] = [];\n for (let v = start; v <= end + step * 1e-6; v += step) {\n ticks.push(Number(v.toFixed(12)));\n }\n return ticks;\n}\n\nfunction formatTick(value: number): string {\n if (value === 0) {\n return '0';\n }\n const abs = Math.abs(value);\n if (abs < 1e-3 || abs >= 1e6) {\n return value.toExponential(1);\n }\n const rounded = Number(value.toPrecision(4));\n return rounded.toLocaleString(undefined, { maximumFractionDigits: 4 });\n}\n\n// ---------------------------------------------------------------------------\n// Chart geometry\n// ---------------------------------------------------------------------------\n\nconst VIEW_W = 640;\nconst VIEW_H = 340;\nconst MARGIN = { top: 16, right: 20, bottom: 48, left: 62 };\nconst INNER_W = VIEW_W - MARGIN.left - MARGIN.right;\nconst INNER_H = VIEW_H - MARGIN.top - MARGIN.bottom;\n\ninterface ChartModel {\n series: Series[];\n xTicks: number[];\n yTicks: number[];\n domainX: [number, number];\n domainY: [number, number];\n xLabel: string;\n yLabel: string;\n}\n\nfunction buildModel(results: Json): ChartModel | null {\n const series = extractSeries(results);\n if (series.length === 0) {\n return null;\n }\n\n const xs: number[] = [];\n const ys: number[] = [];\n for (const s of series) {\n for (const p of s.points) {\n xs.push(p.x);\n ys.push(p.y);\n }\n }\n\n let minX = Math.min(...xs);\n let maxX = Math.max(...xs);\n let minY = Math.min(...ys);\n let maxY = Math.max(...ys);\n\n if (minX === maxX) {\n const pad = Math.abs(minX) || 1;\n minX -= pad;\n maxX += pad;\n }\n if (minY === maxY) {\n const pad = Math.abs(minY) || 1;\n minY -= pad;\n maxY += pad;\n }\n // Breathing room above/below the data so markers are not clipped.\n const yPad = (maxY - minY) * 0.08;\n minY -= yPad;\n maxY += yPad;\n\n const xTicks = niceTicks(minX, maxX, 5);\n const yTicks = niceTicks(minY, maxY, 5);\n const domainX: [number, number] = [\n Math.min(minX, xTicks[0]),\n Math.max(maxX, xTicks[xTicks.length - 1]),\n ];\n const domainY: [number, number] = [\n Math.min(minY, yTicks[0]),\n Math.max(maxY, yTicks[yTicks.length - 1]),\n ];\n\n return {\n series,\n xTicks: xTicks.filter((t) => t >= domainX[0] && t <= domainX[1]),\n yTicks: yTicks.filter((t) => t >= domainY[0] && t <= domainY[1]),\n domainX,\n domainY,\n xLabel: readLabel(results, ['xLabel', 'xlabel', 'xAxis', 'xaxis', 'xTitle', 'xUnit'], 'x', 'x'),\n yLabel: readLabel(results, ['yLabel', 'ylabel', 'yAxis', 'yaxis', 'yTitle', 'yUnit'], 'y', 'y'),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Fallback key/value table\n// ---------------------------------------------------------------------------\n\nfunction displayValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '—';\n }\n if (typeof value === 'number') {\n return value.toLocaleString(undefined, { maximumFractionDigits: 6 });\n }\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No';\n }\n if (typeof value === 'string') {\n return value;\n }\n if (Array.isArray(value)) {\n return `${value.length} item${value.length === 1 ? '' : 's'}`;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction MetricsTable({ results }: { results: Json }) {\n const entries = Object.entries(results);\n if (entries.length === 0) {\n return (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary\">\n No structured results were recorded for this analysis.\n </p>\n );\n }\n return (\n <dl className=\"grid grid-cols-1 gap-px overflow-hidden rounded-card border border-border-default bg-border-subtle sm:grid-cols-2\">\n {entries.map(([key, value]) => {\n const text = displayValue(value);\n return (\n <div\n key={key}\n className=\"flex items-baseline justify-between gap-3 bg-bg-elevated px-3 py-2\"\n >\n <dt className=\"min-w-0 truncate text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {humanize(key)}\n </dt>\n <dd\n className=\"max-w-[60%] truncate text-right text-sm tabular-nums text-text-primary\"\n title={text}\n >\n {text}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface ResultsChartProps {\n results: Record<string, unknown>;\n className?: string;\n}\n\nexport function ResultsChart({ results, className }: ResultsChartProps) {\n const model = useMemo(() => buildModel(results), [results]);\n\n if (!model) {\n return (\n <div className={className}>\n <MetricsTable results={results} />\n </div>\n );\n }\n\n const { series, xTicks, yTicks, domainX, domainY, xLabel, yLabel } = model;\n\n const sx = (x: number): number =>\n MARGIN.left + ((x - domainX[0]) / (domainX[1] - domainX[0])) * INNER_W;\n const sy = (y: number): number =>\n MARGIN.top + INNER_H - ((y - domainY[0]) / (domainY[1] - domainY[0])) * INNER_H;\n\n const showLegend = series.length > 1;\n const ariaLabel = `Chart of ${series\n .map((s) => s.name)\n .join(', ')}; x-axis ${xLabel}, y-axis ${yLabel}`;\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"rounded-card border border-border-default bg-bg-sunken p-3\">\n <svg\n viewBox={`0 0 ${VIEW_W} ${VIEW_H}`}\n className=\"h-auto w-full\"\n preserveAspectRatio=\"xMidYMid meet\"\n role=\"img\"\n aria-label={ariaLabel}\n >\n {/* horizontal gridlines + y tick labels */}\n {yTicks.map((tick) => {\n const y = sy(tick);\n return (\n <g key={`y-${tick}`}>\n <line\n x1={MARGIN.left}\n x2={MARGIN.left + INNER_W}\n y1={y}\n y2={y}\n stroke=\"var(--color-chart-grid)\"\n strokeWidth={1}\n />\n <text\n x={MARGIN.left - 8}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"central\"\n fontSize={11}\n fill=\"var(--color-text-tertiary)\"\n >\n {formatTick(tick)}\n </text>\n </g>\n );\n })}\n\n {/* vertical gridlines + x tick labels */}\n {xTicks.map((tick) => {\n const x = sx(tick);\n return (\n <g key={`x-${tick}`}>\n <line\n x1={x}\n x2={x}\n y1={MARGIN.top}\n y2={MARGIN.top + INNER_H}\n stroke=\"var(--color-chart-grid)\"\n strokeWidth={1}\n strokeDasharray=\"2 4\"\n />\n <text\n x={x}\n y={MARGIN.top + INNER_H + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--color-text-tertiary)\"\n >\n {formatTick(tick)}\n </text>\n </g>\n );\n })}\n\n {/* plot frame */}\n <rect\n x={MARGIN.left}\n y={MARGIN.top}\n width={INNER_W}\n height={INNER_H}\n fill=\"none\"\n stroke=\"var(--color-chart-axis)\"\n strokeWidth={1}\n />\n\n {/* series */}\n {series.map((s) => {\n const ordered = [...s.points].sort((a, b) => a.x - b.x);\n const path = ordered\n .map((p, i) => `${i === 0 ? 'M' : 'L'} ${sx(p.x).toFixed(2)} ${sy(p.y).toFixed(2)}`)\n .join(' ');\n const showDots = s.kind === 'scatter' || s.points.length <= 40;\n return (\n <g key={s.name}>\n {s.kind === 'line' ? (\n <path\n d={path}\n fill=\"none\"\n stroke={s.colorVar}\n strokeWidth={2}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null}\n {showDots\n ? s.points.map((p, i) => (\n <circle\n key={`${s.name}-${i}`}\n cx={sx(p.x)}\n cy={sy(p.y)}\n r={s.kind === 'scatter' ? 2.4 : 2.8}\n fill={s.colorVar}\n fillOpacity={s.kind === 'scatter' ? 0.85 : 1}\n />\n ))\n : null}\n </g>\n );\n })}\n\n {/* axis titles */}\n <text\n x={MARGIN.left + INNER_W / 2}\n y={VIEW_H - 6}\n textAnchor=\"middle\"\n fontSize={12}\n fill=\"var(--color-text-secondary)\"\n >\n {xLabel}\n </text>\n <text\n x={14}\n y={MARGIN.top + INNER_H / 2}\n textAnchor=\"middle\"\n fontSize={12}\n fill=\"var(--color-text-secondary)\"\n transform={`rotate(-90 14 ${MARGIN.top + INNER_H / 2})`}\n >\n {yLabel}\n </text>\n </svg>\n </div>\n\n {showLegend ? (\n <ul className=\"flex flex-wrap gap-x-4 gap-y-1.5\">\n {series.map((s) => (\n <li key={s.name} className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <span\n aria-hidden=\"true\"\n className=\"inline-block size-2.5 rounded-full\"\n style={{ backgroundColor: s.colorVar }}\n />\n <span className=\"truncate\">\n {s.name}\n <span className=\"ml-1 text-text-tertiary\">\n ({s.kind === 'scatter' ? 'points' : 'line'})\n </span>\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\nexport default ResultsChart;\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { AnchorHTMLAttributes, ReactNode } from 'react';
2
+
3
+ /**
4
+ * Validate and normalise a user-supplied URL before it is ever placed in an
5
+ * `href`. Free-text URL fields (source endpoints, external references) are
6
+ * persisted verbatim by the backend, so rendering them directly into an anchor
7
+ * is a stored-XSS sink: a value like `javascript:alert(1)` or `data:text/html,...`
8
+ * would execute on click. This helper is the single choke point that neutralises
9
+ * that.
10
+ *
11
+ * Rules:
12
+ * - empty / whitespace → `null` (caller renders plain text)
13
+ * - bare domain → prefixed with `https://` (`example.com` → `https://example.com`)
14
+ * - `http:` / `https:` → accepted, returned normalised
15
+ * - anything else → `null` (rejects `javascript:`, `data:`, `file:`, `mailto:`…)
16
+ *
17
+ * Also used for absolute in-app links (e.g. `/conversations`), which are returned
18
+ * as-is when they are root-relative.
19
+ */
20
+ export declare function safeUrl(raw: string | null | undefined): string | null;
21
+ export interface SafeLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
22
+ href: string | null | undefined;
23
+ children?: ReactNode;
24
+ /** Class applied to the rendered plain-text span when the href is unsafe. */
25
+ fallbackClassName?: string;
26
+ }
27
+ /**
28
+ * Anchor that only renders a clickable link when `href` passes `safeUrl`.
29
+ * Unsafe/blank values fall back to inert text so a malicious `javascript:` URL
30
+ * can never be activated. External links open in a new tab with
31
+ * `noopener noreferrer`; root-relative in-app links open in the same tab.
32
+ */
33
+ export declare function SafeLink({ href, children, className, fallbackClassName, ...rest }: SafeLinkProps): import("react/jsx-runtime").JSX.Element;
34
+ export default SafeLink;
35
+ //# sourceMappingURL=SafeLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SafeLink.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/SafeLink.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CA0BrE;AAED,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC1F,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,2CAmBhG;AAED,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * StatusBadge — maps the Cos* enum values to the semantic status token families.
3
+ *
4
+ * One badge component covers every CosmicIntersection enum (dataset/source/
5
+ * pipeline/run/step/notebook/analysis/release/grant status, plus access level).
6
+ * Colour comes only from the `status-{success,info,warning,error,neutral}`
7
+ * token families — never a raw palette colour — so it renders correctly in every
8
+ * theme.
9
+ */
10
+ export type StatusTone = 'success' | 'info' | 'warning' | 'error' | 'neutral';
11
+ /** Turn an ENUM_VALUE into a human label, e.g. GROUND_SENSOR -> "Ground sensor". */
12
+ export declare function formatEnumLabel(value: string): string;
13
+ export interface StatusBadgeProps {
14
+ status: string;
15
+ /** Override the derived tone (e.g. to force a colour for a domain-specific case). */
16
+ tone?: StatusTone;
17
+ /** Override the rendered label; defaults to a title-cased enum value. */
18
+ label?: string;
19
+ className?: string;
20
+ }
21
+ export declare function StatusBadge({ status, tone, label, className }: StatusBadgeProps): import("react/jsx-runtime").JSX.Element;
22
+ export default StatusBadge;
23
+ //# sourceMappingURL=StatusBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/cosmicintersection/components/StatusBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAiD9E,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,gBAAgB,2CAS/E;AAED,eAAe,WAAW,CAAC"}