@screenbook/ui 0.0.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 (101) hide show
  1. package/.astro/content-assets.mjs +1 -0
  2. package/.astro/content-modules.mjs +1 -0
  3. package/.astro/content.d.ts +199 -0
  4. package/.astro/data-store.json +1 -0
  5. package/.astro/settings.json +5 -0
  6. package/.astro/types.d.ts +2 -0
  7. package/.prettierrc +15 -0
  8. package/.screenbook/coverage.json +37 -0
  9. package/.screenbook/screens.json +90 -0
  10. package/LICENSE +21 -0
  11. package/astro.config.mjs +18 -0
  12. package/dist/client/_astro/_baseUniq.BGai4mcc.js +1 -0
  13. package/dist/client/_astro/arc.DUp0dfXj.js +1 -0
  14. package/dist/client/_astro/architectureDiagram-VXUJARFQ.De_Gt-YC.js +36 -0
  15. package/dist/client/_astro/blockDiagram-VD42YOAC.BBt_VNhR.js +122 -0
  16. package/dist/client/_astro/c4Diagram-YG6GDRKO.DfgUlHvt.js +10 -0
  17. package/dist/client/_astro/channel.CNyr52v1.js +1 -0
  18. package/dist/client/_astro/chunk-4BX2VUAB.BL0ar6du.js +1 -0
  19. package/dist/client/_astro/chunk-55IACEB6.CI6SkZkY.js +1 -0
  20. package/dist/client/_astro/chunk-B4BG7PRW.Z25N80K6.js +165 -0
  21. package/dist/client/_astro/chunk-DI55MBZ5.BqjPVmi1.js +220 -0
  22. package/dist/client/_astro/chunk-FMBD7UC4.bZ9DWnFm.js +15 -0
  23. package/dist/client/_astro/chunk-QN33PNHL.BkzuUgWB.js +1 -0
  24. package/dist/client/_astro/chunk-QZHKN3VN.C__d68N_.js +1 -0
  25. package/dist/client/_astro/chunk-TZMSLE5B.BIpu8bMn.js +1 -0
  26. package/dist/client/_astro/classDiagram-2ON5EDUG.CxT3aW-h.js +1 -0
  27. package/dist/client/_astro/classDiagram-v2-WZHVMYZB.CxT3aW-h.js +1 -0
  28. package/dist/client/_astro/clone.U_lSZ6fe.js +1 -0
  29. package/dist/client/_astro/cose-bilkent-S5V4N54A.D48yfMll.js +1 -0
  30. package/dist/client/_astro/coverage.CKIVg4LY.css +1 -0
  31. package/dist/client/_astro/coverage.DDJMzKCq.css +1 -0
  32. package/dist/client/_astro/cytoscape.esm.DtBltrT8.js +331 -0
  33. package/dist/client/_astro/dagre-6UL2VRFP.LKVH7b30.js +4 -0
  34. package/dist/client/_astro/defaultLocale.C4B-KCzX.js +1 -0
  35. package/dist/client/_astro/diagram-PSM6KHXK.AHgUjH56.js +24 -0
  36. package/dist/client/_astro/diagram-QEK2KX5R.DvS33xWZ.js +43 -0
  37. package/dist/client/_astro/diagram-S2PKOQOG.BWisaYrQ.js +24 -0
  38. package/dist/client/_astro/erDiagram-Q2GNP2WA.B7oID6oT.js +60 -0
  39. package/dist/client/_astro/flowDiagram-NV44I4VS.Bb1qJLxr.js +162 -0
  40. package/dist/client/_astro/ganttDiagram-JELNMOA3.3vGHETyo.js +267 -0
  41. package/dist/client/_astro/gitGraphDiagram-NY62KEGX.Co2SKcif.js +65 -0
  42. package/dist/client/_astro/graph.B5fevUwB.js +1 -0
  43. package/dist/client/_astro/graph.astro_astro_type_script_index_0_lang.1HlATQ1g.js +1 -0
  44. package/dist/client/_astro/impact.Cvhl64u1.css +1 -0
  45. package/dist/client/_astro/impact.astro_astro_type_script_index_0_lang.D4cAR9AL.js +6 -0
  46. package/dist/client/_astro/infoDiagram-WHAUD3N6.B6ULtps1.js +2 -0
  47. package/dist/client/_astro/init.Gi6I4Gst.js +1 -0
  48. package/dist/client/_astro/journeyDiagram-XKPGCS4Q.BSOCzWmw.js +139 -0
  49. package/dist/client/_astro/kanban-definition-3W4ZIXB7.D8KKGc1o.js +89 -0
  50. package/dist/client/_astro/katex.XbL3y5x-.js +261 -0
  51. package/dist/client/_astro/layout.8vv24qEg.js +1 -0
  52. package/dist/client/_astro/linear.B6O9ymuK.js +1 -0
  53. package/dist/client/_astro/mermaid.core.CReXU7YN.js +256 -0
  54. package/dist/client/_astro/min.CdGMGVU0.js +1 -0
  55. package/dist/client/_astro/mindmap-definition-VGOIOE7T.G14HgtDw.js +68 -0
  56. package/dist/client/_astro/ordinal.BYWQX77i.js +1 -0
  57. package/dist/client/_astro/pieDiagram-ADFJNKIX.bC2VkyoW.js +30 -0
  58. package/dist/client/_astro/quadrantDiagram-AYHSOK5B.BlLaQQxO.js +7 -0
  59. package/dist/client/_astro/requirementDiagram-UZGBJVZJ.DHRnMofO.js +64 -0
  60. package/dist/client/_astro/sankeyDiagram-TZEHDZUN.BMuaJBmt.js +10 -0
  61. package/dist/client/_astro/sequenceDiagram-WL72ISMW.CnU62wqy.js +145 -0
  62. package/dist/client/_astro/stateDiagram-FKZM4ZOC.CewI55YO.js +1 -0
  63. package/dist/client/_astro/stateDiagram-v2-4FDKWEC3.7xUQqoNr.js +1 -0
  64. package/dist/client/_astro/timeline-definition-IT6M3QCI.D1PLRwss.js +61 -0
  65. package/dist/client/_astro/treemap-KMMF4GRG.D3VNVvXF.js +128 -0
  66. package/dist/client/_astro/xychartDiagram-PRI3JC2R.CQex0-ul.js +7 -0
  67. package/dist/client/logo.svg +5 -0
  68. package/dist/server/_@astrojs-ssr-adapter.mjs +1 -0
  69. package/dist/server/_noop-middleware.mjs +3 -0
  70. package/dist/server/chunks/_@astrojs-ssr-adapter_DgsYudHz.mjs +4385 -0
  71. package/dist/server/chunks/astro/server_m7yT4wCr.mjs +2787 -0
  72. package/dist/server/chunks/astro-designed-error-pages_BvPhMmw0.mjs +364 -0
  73. package/dist/server/chunks/fs-lite_COtHaKzy.mjs +157 -0
  74. package/dist/server/chunks/impactAnalysis_Bz5lMdmy.mjs +188 -0
  75. package/dist/server/chunks/loadScreens_DJf-tycc.mjs +39 -0
  76. package/dist/server/chunks/node_DoTkMCOi.mjs +1673 -0
  77. package/dist/server/chunks/remote_B3W5fv4r.mjs +188 -0
  78. package/dist/server/chunks/sharp_DHNfMLYY.mjs +99 -0
  79. package/dist/server/entry.mjs +47 -0
  80. package/dist/server/manifest_-V1HEnR8.mjs +101 -0
  81. package/dist/server/noop-entrypoint.mjs +3 -0
  82. package/dist/server/pages/_image.astro.mjs +2 -0
  83. package/dist/server/pages/coverage.astro.mjs +65 -0
  84. package/dist/server/pages/graph.astro.mjs +107 -0
  85. package/dist/server/pages/impact.astro.mjs +52 -0
  86. package/dist/server/pages/index.astro.mjs +28 -0
  87. package/dist/server/pages/screen/_id_.astro.mjs +52 -0
  88. package/dist/server/renderers.mjs +3 -0
  89. package/package.json +42 -0
  90. package/public/logo.svg +5 -0
  91. package/src/layouts/Layout.astro +60 -0
  92. package/src/pages/coverage.astro +399 -0
  93. package/src/pages/graph.astro +330 -0
  94. package/src/pages/impact.astro +459 -0
  95. package/src/pages/index.astro +167 -0
  96. package/src/pages/screen/[id].astro +186 -0
  97. package/src/styles/global.css +862 -0
  98. package/src/utils/impactAnalysis.ts +297 -0
  99. package/src/utils/loadCoverage.ts +30 -0
  100. package/src/utils/loadScreens.ts +18 -0
  101. package/tsconfig.json +9 -0
@@ -0,0 +1,297 @@
1
+ import type { Screen } from "@screenbook/core"
2
+
3
+ export interface TransitiveDependency {
4
+ screen: Screen
5
+ path: string[]
6
+ }
7
+
8
+ export interface ImpactResult {
9
+ api: string
10
+ direct: Screen[]
11
+ transitive: TransitiveDependency[]
12
+ totalCount: number
13
+ }
14
+
15
+ /**
16
+ * Check if a screen's dependsOn matches the API name (supports partial matching).
17
+ */
18
+ function matchesDependency(dependency: string, apiName: string): boolean {
19
+ if (dependency === apiName) {
20
+ return true
21
+ }
22
+ if (dependency.startsWith(`${apiName}.`)) {
23
+ return true
24
+ }
25
+ if (apiName.startsWith(`${dependency}.`)) {
26
+ return true
27
+ }
28
+ return false
29
+ }
30
+
31
+ /**
32
+ * Find screens that directly depend on the given API.
33
+ */
34
+ function findDirectDependents(screens: Screen[], apiName: string): Screen[] {
35
+ return screens.filter((screen) =>
36
+ screen.dependsOn?.some((dep) => matchesDependency(dep, apiName)),
37
+ )
38
+ }
39
+
40
+ /**
41
+ * Build a navigation graph from `next` field.
42
+ */
43
+ function buildNavigationGraph(screens: Screen[]): Map<string, Set<string>> {
44
+ const graph = new Map<string, Set<string>>()
45
+
46
+ for (const screen of screens) {
47
+ if (!screen.next) continue
48
+
49
+ if (!graph.has(screen.id)) {
50
+ graph.set(screen.id, new Set())
51
+ }
52
+ for (const nextId of screen.next) {
53
+ graph.get(screen.id)!.add(nextId)
54
+ }
55
+ }
56
+
57
+ return graph
58
+ }
59
+
60
+ /**
61
+ * Find a path from a screen to any directly dependent screen using BFS.
62
+ */
63
+ function findPathToDirectDependent(
64
+ startId: string,
65
+ targetIds: Set<string>,
66
+ graph: Map<string, Set<string>>,
67
+ maxDepth: number,
68
+ ): string[] | null {
69
+ const queue: Array<{ id: string; path: string[] }> = [
70
+ { id: startId, path: [startId] },
71
+ ]
72
+ const localVisited = new Set<string>([startId])
73
+
74
+ while (queue.length > 0) {
75
+ const current = queue.shift()!
76
+
77
+ if (current.path.length > maxDepth + 1) {
78
+ continue
79
+ }
80
+
81
+ const neighbors = graph.get(current.id)
82
+ if (!neighbors) {
83
+ continue
84
+ }
85
+
86
+ for (const neighborId of neighbors) {
87
+ if (localVisited.has(neighborId)) {
88
+ continue
89
+ }
90
+
91
+ const newPath = [...current.path, neighborId]
92
+
93
+ if (newPath.length > maxDepth + 1) {
94
+ continue
95
+ }
96
+
97
+ if (targetIds.has(neighborId)) {
98
+ return newPath
99
+ }
100
+
101
+ localVisited.add(neighborId)
102
+ queue.push({ id: neighborId, path: newPath })
103
+ }
104
+ }
105
+
106
+ return null
107
+ }
108
+
109
+ /**
110
+ * Find all transitive dependents using BFS.
111
+ */
112
+ function findTransitiveDependents(
113
+ screens: Screen[],
114
+ directDependentIds: Set<string>,
115
+ maxDepth: number,
116
+ ): TransitiveDependency[] {
117
+ const navigationGraph = buildNavigationGraph(screens)
118
+ const transitive: TransitiveDependency[] = []
119
+ const visited = new Set<string>()
120
+
121
+ for (const screen of screens) {
122
+ if (directDependentIds.has(screen.id)) {
123
+ continue
124
+ }
125
+
126
+ const path = findPathToDirectDependent(
127
+ screen.id,
128
+ directDependentIds,
129
+ navigationGraph,
130
+ maxDepth,
131
+ )
132
+
133
+ if (path && !visited.has(screen.id)) {
134
+ visited.add(screen.id)
135
+ transitive.push({
136
+ screen,
137
+ path,
138
+ })
139
+ }
140
+ }
141
+
142
+ return transitive
143
+ }
144
+
145
+ /**
146
+ * Analyze the impact of a change to an API on the screen catalog.
147
+ */
148
+ export function analyzeImpact(
149
+ screens: Screen[],
150
+ apiName: string,
151
+ maxDepth = 3,
152
+ ): ImpactResult {
153
+ const direct = findDirectDependents(screens, apiName)
154
+ const directIds = new Set(direct.map((s) => s.id))
155
+ const transitive = findTransitiveDependents(screens, directIds, maxDepth)
156
+
157
+ return {
158
+ api: apiName,
159
+ direct,
160
+ transitive,
161
+ totalCount: direct.length + transitive.length,
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Get all unique APIs from screens' dependsOn fields.
167
+ */
168
+ export function getAllApis(screens: Screen[]): string[] {
169
+ const apis = new Set<string>()
170
+ for (const screen of screens) {
171
+ if (screen.dependsOn) {
172
+ for (const dep of screen.dependsOn) {
173
+ apis.add(dep)
174
+ }
175
+ }
176
+ }
177
+ return Array.from(apis).sort()
178
+ }
179
+
180
+ /**
181
+ * Count how many screens depend on each API.
182
+ */
183
+ export function getApiDependencyCount(screens: Screen[]): Map<string, number> {
184
+ const counts = new Map<string, number>()
185
+ for (const screen of screens) {
186
+ if (screen.dependsOn) {
187
+ for (const dep of screen.dependsOn) {
188
+ counts.set(dep, (counts.get(dep) || 0) + 1)
189
+ }
190
+ }
191
+ }
192
+ return counts
193
+ }
194
+
195
+ /**
196
+ * Generate Mermaid graph with impact highlighting.
197
+ */
198
+ export function generateImpactMermaid(
199
+ screens: Screen[],
200
+ result: ImpactResult,
201
+ ): string {
202
+ const directIds = new Set(result.direct.map((s) => s.id))
203
+ const transitiveIds = new Set(result.transitive.map((t) => t.screen.id))
204
+
205
+ const lines: string[] = ["flowchart TD"]
206
+
207
+ // Define styles - high contrast colors with readable text
208
+ lines.push(" classDef direct fill:#dc2626,stroke:#fef2f2,color:#ffffff,stroke-width:3px,font-weight:bold")
209
+ lines.push(" classDef transitive fill:#ea580c,stroke:#fff7ed,color:#ffffff,stroke-width:3px,font-weight:bold")
210
+ lines.push(" classDef normal fill:#1e293b,stroke:#64748b,color:#e2e8f0,stroke-width:1px")
211
+
212
+ // Add nodes
213
+ for (const screen of screens) {
214
+ const label = screen.title.replace(/"/g, "'")
215
+ const id = screen.id.replace(/\./g, "_")
216
+ lines.push(` ${id}["${label}"]`)
217
+ }
218
+
219
+ lines.push("")
220
+
221
+ // Add edges
222
+ for (const screen of screens) {
223
+ if (screen.next) {
224
+ const fromId = screen.id.replace(/\./g, "_")
225
+ for (const nextId of screen.next) {
226
+ const toId = nextId.replace(/\./g, "_")
227
+ lines.push(` ${fromId} --> ${toId}`)
228
+ }
229
+ }
230
+ }
231
+
232
+ lines.push("")
233
+
234
+ // Apply styles
235
+ const directNodes = screens
236
+ .filter((s) => directIds.has(s.id))
237
+ .map((s) => s.id.replace(/\./g, "_"))
238
+ const transitiveNodes = screens
239
+ .filter((s) => transitiveIds.has(s.id))
240
+ .map((s) => s.id.replace(/\./g, "_"))
241
+ const normalNodes = screens
242
+ .filter((s) => !directIds.has(s.id) && !transitiveIds.has(s.id))
243
+ .map((s) => s.id.replace(/\./g, "_"))
244
+
245
+ if (directNodes.length > 0) {
246
+ lines.push(` class ${directNodes.join(",")} direct`)
247
+ }
248
+ if (transitiveNodes.length > 0) {
249
+ lines.push(` class ${transitiveNodes.join(",")} transitive`)
250
+ }
251
+ if (normalNodes.length > 0) {
252
+ lines.push(` class ${normalNodes.join(",")} normal`)
253
+ }
254
+
255
+ return lines.join("\n")
256
+ }
257
+
258
+ /**
259
+ * Format the impact result as Markdown for PR comments.
260
+ */
261
+ export function formatImpactMarkdown(result: ImpactResult): string {
262
+ const lines: string[] = []
263
+
264
+ lines.push(`## Impact Analysis: \`${result.api}\``)
265
+ lines.push("")
266
+
267
+ if (result.totalCount === 0) {
268
+ lines.push("No screens depend on this API.")
269
+ return lines.join("\n")
270
+ }
271
+
272
+ lines.push(
273
+ `**${result.totalCount} screen${result.totalCount > 1 ? "s" : ""} affected**`,
274
+ )
275
+ lines.push("")
276
+
277
+ if (result.direct.length > 0) {
278
+ lines.push(`### Direct Dependencies (${result.direct.length})`)
279
+ lines.push("")
280
+ for (const screen of result.direct) {
281
+ const owner = screen.owner?.length ? ` - ${screen.owner.join(", ")}` : ""
282
+ lines.push(`- **${screen.title}** (\`${screen.route}\`)${owner}`)
283
+ }
284
+ lines.push("")
285
+ }
286
+
287
+ if (result.transitive.length > 0) {
288
+ lines.push(`### Transitive Dependencies (${result.transitive.length})`)
289
+ lines.push("")
290
+ for (const { screen, path } of result.transitive) {
291
+ lines.push(`- **${screen.title}** via \`${path.join(" → ")}\``)
292
+ }
293
+ lines.push("")
294
+ }
295
+
296
+ return lines.join("\n")
297
+ }
@@ -0,0 +1,30 @@
1
+ import { existsSync, readFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+
4
+ export interface CoverageData {
5
+ total: number
6
+ covered: number
7
+ percentage: number
8
+ missing: Array<{
9
+ route: string
10
+ suggestedPath: string
11
+ }>
12
+ byOwner: Record<string, { count: number; screens: string[] }>
13
+ byTag: Record<string, number>
14
+ timestamp: string
15
+ }
16
+
17
+ export function loadCoverage(): CoverageData | null {
18
+ const coveragePath = join(process.cwd(), ".screenbook", "coverage.json")
19
+
20
+ if (!existsSync(coveragePath)) {
21
+ return null
22
+ }
23
+
24
+ try {
25
+ const content = readFileSync(coveragePath, "utf-8")
26
+ return JSON.parse(content) as CoverageData
27
+ } catch {
28
+ return null
29
+ }
30
+ }
@@ -0,0 +1,18 @@
1
+ import { existsSync, readFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import type { Screen } from "@screenbook/core"
4
+
5
+ export function loadScreens(): Screen[] {
6
+ const screensPath = join(process.cwd(), ".screenbook", "screens.json")
7
+
8
+ if (!existsSync(screensPath)) {
9
+ return []
10
+ }
11
+
12
+ try {
13
+ const content = readFileSync(screensPath, "utf-8")
14
+ return JSON.parse(content) as Screen[]
15
+ } catch {
16
+ return []
17
+ }
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "paths": {
6
+ "@/*": ["src/*"]
7
+ }
8
+ }
9
+ }