@crvouga/atlas 0.10.2 → 0.11.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 (99) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +7 -0
  3. package/dist/server/src/settings-page.d.ts +4 -4
  4. package/dist/server/src/settings-page.js +17 -78
  5. package/docs/reference/server.md +4 -4
  6. package/docs/ui.md +142 -0
  7. package/docs/workspaces.md +130 -0
  8. package/llms-full.txt +278 -0
  9. package/llms.txt +2 -0
  10. package/package.json +17 -10
  11. package/packages/visualizer/src/admin/SettingsPage.tsx +456 -0
  12. package/packages/visualizer/src/admin/main.tsx +11 -0
  13. package/packages/visualizer/src/app/AppShell.tsx +62 -38
  14. package/packages/visualizer/src/app/gazetteer.tsx +90 -83
  15. package/packages/visualizer/src/app/router.tsx +33 -15
  16. package/packages/visualizer/src/components/ActivityIndicator.tsx +4 -5
  17. package/packages/visualizer/src/components/DataIssues.tsx +36 -54
  18. package/packages/visualizer/src/components/Header.tsx +197 -125
  19. package/packages/visualizer/src/components/Legend.tsx +13 -41
  20. package/packages/visualizer/src/components/ProjectPicker.tsx +61 -26
  21. package/packages/visualizer/src/components/Screen.tsx +80 -71
  22. package/packages/visualizer/src/components/SpecLogo.tsx +15 -0
  23. package/packages/visualizer/src/components/Status.tsx +20 -34
  24. package/packages/visualizer/src/components/labels.tsx +8 -3
  25. package/packages/visualizer/src/components/spec-logos/APACHE-2.0.txt +206 -0
  26. package/packages/visualizer/src/components/spec-logos/CAMUNDA-MIT.txt +21 -0
  27. package/packages/visualizer/src/components/spec-logos/FILE-ICONS-ISC.txt +13 -0
  28. package/packages/visualizer/src/components/spec-logos/README.md +23 -0
  29. package/packages/visualizer/src/components/spec-logos/SIMPLE-ICONS-CC0.txt +30 -0
  30. package/packages/visualizer/src/components/spec-logos/alloy.svg +1 -0
  31. package/packages/visualizer/src/components/spec-logos/dmn.svg +3 -0
  32. package/packages/visualizer/src/components/spec-logos/gherkin.svg +1 -0
  33. package/packages/visualizer/src/components/spec-logos/openslo.svg +63 -0
  34. package/packages/visualizer/src/components/spec-logos/quint.svg +1 -0
  35. package/packages/visualizer/src/evidence/artifacts.tsx +263 -110
  36. package/packages/visualizer/src/evidence/chart-source.tsx +18 -0
  37. package/packages/visualizer/src/evidence/check-detail.tsx +283 -139
  38. package/packages/visualizer/src/evidence/decision-table.tsx +35 -26
  39. package/packages/visualizer/src/evidence/document-page.tsx +51 -29
  40. package/packages/visualizer/src/evidence/feature-page.tsx +121 -53
  41. package/packages/visualizer/src/evidence/marks.tsx +112 -79
  42. package/packages/visualizer/src/evidence/model-page.tsx +77 -46
  43. package/packages/visualizer/src/evidence/model-trace.tsx +55 -35
  44. package/packages/visualizer/src/evidence/overview.tsx +137 -69
  45. package/packages/visualizer/src/evidence/recording-page.tsx +70 -31
  46. package/packages/visualizer/src/evidence/recording.tsx +135 -68
  47. package/packages/visualizer/src/evidence/snapshot.tsx +99 -43
  48. package/packages/visualizer/src/evidence/source-text.tsx +33 -0
  49. package/packages/visualizer/src/evidence/sources-panel.tsx +138 -144
  50. package/packages/visualizer/src/evidence/spec-header.tsx +52 -36
  51. package/packages/visualizer/src/evidence/spec-index.tsx +61 -32
  52. package/packages/visualizer/src/evidence/spec-page.tsx +56 -25
  53. package/packages/visualizer/src/evidence/test-filter.tsx +38 -14
  54. package/packages/visualizer/src/explorer/Explorer.tsx +688 -261
  55. package/packages/visualizer/src/explorer/ExplorerIndex.tsx +136 -68
  56. package/packages/visualizer/src/explorer/RunsView.tsx +307 -94
  57. package/packages/visualizer/src/explorer/TestDetail.tsx +393 -118
  58. package/packages/visualizer/src/explorer/parts.tsx +35 -13
  59. package/packages/visualizer/src/find/Finder.tsx +106 -90
  60. package/packages/visualizer/src/find/open.ts +17 -20
  61. package/packages/visualizer/src/live/test-progress.tsx +60 -22
  62. package/packages/visualizer/src/looks/Stage.tsx +223 -110
  63. package/packages/visualizer/src/main.tsx +9 -11
  64. package/packages/visualizer/src/map/ChartMap.tsx +125 -69
  65. package/packages/visualizer/src/map/EventEdge.tsx +59 -37
  66. package/packages/visualizer/src/map/map.module.css +12 -546
  67. package/packages/visualizer/src/map/nodes.tsx +124 -70
  68. package/packages/visualizer/src/reachable.test.ts +3 -2
  69. package/packages/visualizer/src/spec-kind.ts +2 -2
  70. package/packages/visualizer/src/ui/grammars/ALLOY-MIT.txt +21 -0
  71. package/packages/visualizer/src/ui/grammars/QUINT-APACHE-2.0.txt +206 -0
  72. package/packages/visualizer/src/ui/grammars/README.md +14 -0
  73. package/packages/visualizer/src/ui/grammars/alloy.json +71 -0
  74. package/packages/visualizer/src/ui/grammars/quint.json +152 -0
  75. package/packages/visualizer/src/ui/highlight.ts +50 -0
  76. package/packages/visualizer/src/ui/theme.tsx +109 -0
  77. package/packages/visualizer/src/views/ChartPage.tsx +46 -26
  78. package/packages/visualizer/src/views/ChartTests.tsx +59 -22
  79. package/packages/visualizer/src/views/ProductPage.tsx +16 -15
  80. package/packages/visualizer/src/views/SidePanel.tsx +241 -131
  81. package/packages/visualizer/src/app/gazetteer.module.css +0 -107
  82. package/packages/visualizer/src/components/ActivityIndicator.module.css +0 -4
  83. package/packages/visualizer/src/components/DataIssues.module.css +0 -136
  84. package/packages/visualizer/src/components/Header.module.css +0 -114
  85. package/packages/visualizer/src/components/Legend.module.css +0 -84
  86. package/packages/visualizer/src/components/Screen.module.css +0 -312
  87. package/packages/visualizer/src/components/Status.module.css +0 -116
  88. package/packages/visualizer/src/components/icons.tsx +0 -156
  89. package/packages/visualizer/src/evidence/artifacts.module.css +0 -213
  90. package/packages/visualizer/src/evidence/evidence.module.css +0 -907
  91. package/packages/visualizer/src/evidence/recording.module.css +0 -460
  92. package/packages/visualizer/src/evidence/sources.module.css +0 -168
  93. package/packages/visualizer/src/evidence/spec-snapshot.tsx +0 -26
  94. package/packages/visualizer/src/explorer/explorer.module.css +0 -1238
  95. package/packages/visualizer/src/find/Finder.module.css +0 -168
  96. package/packages/visualizer/src/looks/stage.module.css +0 -300
  97. package/packages/visualizer/src/styles/global.css +0 -105
  98. package/packages/visualizer/src/styles/tokens.css +0 -80
  99. package/packages/visualizer/src/views/views.module.css +0 -546
@@ -1,3 +1,6 @@
1
+ import { SpecLogo } from '../components/SpecLogo';
2
+ import { IconChevronDown, IconChevronRight } from '@tabler/icons-react';
3
+ import { ActionIcon, Badge, Box, Group, NavLink, Stack, Title } from '@mantine/core';
1
4
  /**
2
5
  * @module Project index
3
6
  *
@@ -7,34 +10,39 @@
7
10
  import type { StatusCounts } from '@crvouga/atlas/reports/evidence-types';
8
11
  import { Link } from '@tanstack/react-router';
9
12
  import { useMemo, useState } from 'react';
10
-
11
- import evidenceStyles from '../evidence/evidence.module.css';
12
13
  import { useEvidence } from '../evidence/evidence-context';
13
14
  import { StatusMark } from '../evidence/marks';
14
15
  import { SPEC_KINDS } from '../spec-kind';
15
16
  import { useTestCatalog } from './catalog';
16
- import styles from './explorer.module.css';
17
17
  import { patched, useExplorerSearch } from './navigate';
18
18
  import { values } from './search';
19
-
20
19
  /** Domains start open when the index has at most this many specs. */
21
20
  const OPEN_SPECS = 80;
22
-
23
21
  function statusOf(counts: StatusCounts) {
24
22
  return counts.failed ? 'failed' : counts.running ? 'running' : counts.passed ? 'passed' : 'not-run';
25
23
  }
26
-
27
24
  /** A tally for an index row: failures in red, otherwise how many tests, quietly. */
28
25
  function Tally({ counts, tests }: { counts: StatusCounts; tests: number }) {
29
- if (counts.failed) return <span className={evidenceStyles.tally} data-status="failed">{counts.failed} failed</span>;
30
- if (counts.running) return <span className={evidenceStyles.tally} data-status="running">Running</span>;
31
- return <span className={evidenceStyles.tally} data-status={counts.passed === tests ? 'passed' : 'not-run'}>{counts.passed === tests ? `${tests} passed` : `${counts.passed}/${tests}`}</span>;
26
+ return (
27
+ <Badge size="xs" color={counts.failed ? 'red' : counts.running ? 'blue' : counts.passed === tests ? 'green' : 'gray'}>
28
+ {counts.failed
29
+ ? `${counts.failed} failed`
30
+ : counts.running
31
+ ? 'Running'
32
+ : counts.passed === tests
33
+ ? `${tests} passed`
34
+ : `${counts.passed}/${tests}`}
35
+ </Badge>
36
+ );
32
37
  }
33
-
34
38
  function add(a: StatusCounts, b: StatusCounts): StatusCounts {
35
- return { passed: a.passed + b.passed, failed: a.failed + b.failed, running: a.running + b.running, 'not-run': a['not-run'] + b['not-run'] };
39
+ return {
40
+ passed: a.passed + b.passed,
41
+ failed: a.failed + b.failed,
42
+ running: a.running + b.running,
43
+ 'not-run': a['not-run'] + b['not-run']
44
+ };
36
45
  }
37
-
38
46
  /** The index beside the explorer. */
39
47
  export function ProjectIndex() {
40
48
  const { files } = useEvidence();
@@ -42,7 +50,14 @@ export function ProjectIndex() {
42
50
  const search = useExplorerSearch();
43
51
  const kinds = SPEC_KINDS.filter((kind) => files.some(kind.accepts));
44
52
  const domains = useMemo(() => {
45
- const byDomain = new Map<string, { specs: NonNullable<typeof model>['facets']['specs'][number][]; counts: StatusCounts; tests: number }>();
53
+ const byDomain = new Map<
54
+ string,
55
+ {
56
+ specs: NonNullable<typeof model>['facets']['specs'][number][];
57
+ counts: StatusCounts;
58
+ tests: number;
59
+ }
60
+ >();
46
61
  for (const spec of model?.facets.specs ?? []) {
47
62
  const domain = spec.domain || '(no domain)';
48
63
  const entry = byDomain.get(domain) ?? { specs: [], counts: { passed: 0, failed: 0, running: 0, 'not-run': 0 }, tests: 0 };
@@ -57,64 +72,117 @@ export function ProjectIndex() {
57
72
  const chosenSpecs = values(search.spec);
58
73
  const chosenDomains = values(search.domain);
59
74
  const [toggled, setToggled] = useState<ReadonlyMap<string, boolean>>(new Map());
60
- const isOpen = (domain: string, specs: readonly { path: string }[]) => toggled.get(domain) ?? (specCount <= OPEN_SPECS || chosenDomains.includes(domain) || specs.some((spec) => chosenSpecs.includes(spec.path)));
75
+ const isOpen = (
76
+ domain: string,
77
+ specs: readonly {
78
+ path: string;
79
+ }[]
80
+ ) =>
81
+ toggled.get(domain) ??
82
+ (specCount <= OPEN_SPECS || chosenDomains.includes(domain) || specs.some((spec) => chosenSpecs.includes(spec.path)));
61
83
  const everything = !search.spec && !search.domain;
62
84
  return (
63
- <nav className={evidenceStyles.index} aria-label="Project specs">
64
- {kinds.length > 0 && (
65
- <>
66
- <h2 className={styles.indexHeading}>Specs</h2>
67
- <ul>
85
+ <Box component="nav" aria-label="Project specs" p="md">
86
+ <Stack gap="lg">
87
+ {kinds.length > 0 && (
88
+ <Stack gap="xs">
89
+ <Title order={3} c="dimmed">
90
+ Specs
91
+ </Title>
68
92
  {kinds.map((kind) => (
69
- <li key={kind.id}>
70
- <Link className={evidenceStyles.indexRow} to="/specs/$kind" params={{ kind: kind.id }} search={{}}>{kind.label}</Link>
71
- </li>
93
+ <NavLink
94
+ key={kind.id}
95
+ label={kind.label}
96
+ leftSection={<SpecLogo kind={kind} />}
97
+ renderRoot={(props) => <Link {...props} to="/specs/$kind" params={{ kind: kind.id }} search={{}} />}
98
+ />
72
99
  ))}
73
- </ul>
74
- </>
75
- )}
76
- {domains.length > 0 && (
77
- <section aria-label="Tests by spec">
78
- <h2 className={styles.indexHeading}>Tests</h2>
79
- <ul>
80
- <li>
81
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ spec: undefined, domain: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} aria-current={everything ? 'page' : undefined}>
82
- <span className={evidenceStyles.indexName}>All tests</span>
83
- {model && <Tally counts={model.summary.counts} tests={model.summary.tests} />}
84
- </Link>
85
- </li>
86
- </ul>
87
- {domains.map(([domain, entry]) => {
88
- const open = isOpen(domain, entry.specs);
89
- return (
90
- <div key={domain} className={styles.indexDomain}>
91
- <div className={styles.indexDomainHead}>
92
- <button type="button" className={styles.indexToggle} aria-expanded={open} aria-label={`${open ? 'Collapse' : 'Expand'} ${domain}`} onClick={() => setToggled(new Map(toggled).set(domain, !open))}>
93
- <span className={styles.chevron} data-open={open || undefined} aria-hidden="true" />
94
- </button>
95
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ domain, spec: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} aria-current={chosenDomains.length === 1 && chosenDomains[0] === domain && !search.spec ? 'page' : undefined}>
96
- <span className={evidenceStyles.indexName}><strong>{domain}</strong></span>
97
- <Tally counts={entry.counts} tests={entry.tests} />
98
- </Link>
99
- </div>
100
- {open && (
101
- <ul className={styles.indexSpecs}>
102
- {entry.specs.map((spec) => (
103
- <li key={spec.path}>
104
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ spec: spec.path, domain: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} title={spec.path} aria-current={chosenSpecs.length === 1 && chosenSpecs[0] === spec.path ? 'page' : undefined}>
105
- <StatusMark status={statusOf(spec.counts)} size={14} />
106
- <span className={evidenceStyles.indexName}>{spec.title}</span>
107
- <Tally counts={spec.counts} tests={spec.tests} />
108
- </Link>
109
- </li>
110
- ))}
111
- </ul>
112
- )}
113
- </div>
114
- );
115
- })}
116
- </section>
117
- )}
118
- </nav>
100
+ </Stack>
101
+ )}
102
+ {domains.length > 0 && (
103
+ <Box component="section" aria-label="Tests by spec">
104
+ <Title order={3} c="dimmed" mb="sm">
105
+ Tests
106
+ </Title>
107
+ <NavLink
108
+ label="All tests"
109
+ active={everything}
110
+ rightSection={model && <Tally counts={model.summary.counts} tests={model.summary.tests} />}
111
+ aria-current={everything ? 'page' : undefined}
112
+ renderRoot={(props) => (
113
+ <Link
114
+ {...props}
115
+ to="/"
116
+ search={patched({ spec: undefined, domain: undefined, tab: undefined })}
117
+ resetScroll={false}
118
+ activeOptions={{ exact: true }}
119
+ />
120
+ )}
121
+ />
122
+ {domains.map(([domain, entry]) => {
123
+ const open = isOpen(domain, entry.specs);
124
+ const chosen = chosenDomains.length === 1 && chosenDomains[0] === domain && !search.spec;
125
+ return (
126
+ <Box key={domain} mt="sm">
127
+ <Group wrap="nowrap" gap={0}>
128
+ <ActionIcon
129
+ color="gray"
130
+ aria-expanded={open}
131
+ aria-label={`${open ? 'Collapse' : 'Expand'} ${domain}`}
132
+ onClick={() => setToggled(new Map(toggled).set(domain, !open))}
133
+ >
134
+ {open ? <IconChevronDown size={16} aria-hidden="true" /> : <IconChevronRight size={16} aria-hidden="true" />}
135
+ </ActionIcon>
136
+ <NavLink
137
+ flex={1}
138
+ label={domain}
139
+ active={chosen}
140
+ rightSection={<Tally counts={entry.counts} tests={entry.tests} />}
141
+ aria-current={chosen ? 'page' : undefined}
142
+ renderRoot={(props) => (
143
+ <Link
144
+ {...props}
145
+ to="/"
146
+ search={patched({ domain, spec: undefined, tab: undefined })}
147
+ resetScroll={false}
148
+ activeOptions={{ exact: true }}
149
+ />
150
+ )}
151
+ />
152
+ </Group>
153
+ {open && (
154
+ <Box pl="sm">
155
+ {entry.specs.map((spec) => {
156
+ const chosen = chosenSpecs.length === 1 && chosenSpecs[0] === spec.path;
157
+ return (
158
+ <NavLink
159
+ key={spec.path}
160
+ label={spec.title}
161
+ title={spec.path}
162
+ active={chosen}
163
+ leftSection={<StatusMark status={statusOf(spec.counts)} size={14} />}
164
+ rightSection={<Tally counts={spec.counts} tests={spec.tests} />}
165
+ aria-current={chosen ? 'page' : undefined}
166
+ renderRoot={(props) => (
167
+ <Link
168
+ {...props}
169
+ to="/"
170
+ search={patched({ spec: spec.path, domain: undefined, tab: undefined })}
171
+ resetScroll={false}
172
+ activeOptions={{ exact: true }}
173
+ />
174
+ )}
175
+ />
176
+ );
177
+ })}
178
+ </Box>
179
+ )}
180
+ </Box>
181
+ );
182
+ })}
183
+ </Box>
184
+ )}
185
+ </Stack>
186
+ </Box>
119
187
  );
120
188
  }