@screenbook/ui 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/astro.config.mjs +2 -0
  3. package/dist/client/_astro/MockFormEditor.0L29BkPy.js +9 -0
  4. package/dist/client/_astro/MockFormEditor.OiYzJAtR.css +1 -0
  5. package/dist/client/_astro/{_baseUniq.BGai4mcc.js → _baseUniq.D9ouiLRJ.js} +1 -1
  6. package/dist/client/_astro/_id_.BqQf-JH6.css +1 -0
  7. package/dist/client/_astro/{arc.DUp0dfXj.js → arc.CLU0JlVT.js} +1 -1
  8. package/dist/client/_astro/{architectureDiagram-VXUJARFQ.De_Gt-YC.js → architectureDiagram-VXUJARFQ.BF0V2v9-.js} +1 -1
  9. package/dist/client/_astro/{blockDiagram-VD42YOAC.BBt_VNhR.js → blockDiagram-VD42YOAC.6LO7yu7m.js} +1 -1
  10. package/dist/client/_astro/{c4Diagram-YG6GDRKO.DfgUlHvt.js → c4Diagram-YG6GDRKO.BqntXNsZ.js} +1 -1
  11. package/dist/client/_astro/channel._8es88oH.js +1 -0
  12. package/dist/client/_astro/{chunk-4BX2VUAB.BL0ar6du.js → chunk-4BX2VUAB.b97OIY94.js} +1 -1
  13. package/dist/client/_astro/{chunk-55IACEB6.CI6SkZkY.js → chunk-55IACEB6.Cf9hglsl.js} +1 -1
  14. package/dist/client/_astro/{chunk-B4BG7PRW.Z25N80K6.js → chunk-B4BG7PRW.vDfM6vb1.js} +1 -1
  15. package/dist/client/_astro/{chunk-DI55MBZ5.BqjPVmi1.js → chunk-DI55MBZ5.8pHdnkw9.js} +1 -1
  16. package/dist/client/_astro/{chunk-FMBD7UC4.bZ9DWnFm.js → chunk-FMBD7UC4.l_Ue7ZYJ.js} +1 -1
  17. package/dist/client/_astro/{chunk-QN33PNHL.BkzuUgWB.js → chunk-QN33PNHL.CyBZ7SCx.js} +1 -1
  18. package/dist/client/_astro/{chunk-QZHKN3VN.C__d68N_.js → chunk-QZHKN3VN.BC5tI2Vw.js} +1 -1
  19. package/dist/client/_astro/{chunk-TZMSLE5B.BIpu8bMn.js → chunk-TZMSLE5B.DxkkbOL4.js} +1 -1
  20. package/dist/client/_astro/classDiagram-2ON5EDUG.DMv55r4w.js +1 -0
  21. package/dist/client/_astro/classDiagram-v2-WZHVMYZB.DMv55r4w.js +1 -0
  22. package/dist/client/_astro/client.9unXo8s5.js +33 -0
  23. package/dist/client/_astro/clone.COAvtnt5.js +1 -0
  24. package/dist/client/_astro/{cose-bilkent-S5V4N54A.D48yfMll.js → cose-bilkent-S5V4N54A.DlRxGhCG.js} +1 -1
  25. package/dist/client/_astro/coverage.DLKSOM4m.css +1 -0
  26. package/dist/client/_astro/{dagre-6UL2VRFP.LKVH7b30.js → dagre-6UL2VRFP.tCLd0f1c.js} +1 -1
  27. package/dist/client/_astro/{diagram-PSM6KHXK.AHgUjH56.js → diagram-PSM6KHXK.CJA_rnN3.js} +1 -1
  28. package/dist/client/_astro/{diagram-QEK2KX5R.DvS33xWZ.js → diagram-QEK2KX5R.BoqLvsuS.js} +1 -1
  29. package/dist/client/_astro/{diagram-S2PKOQOG.BWisaYrQ.js → diagram-S2PKOQOG.T8h5wODN.js} +1 -1
  30. package/dist/client/_astro/{erDiagram-Q2GNP2WA.B7oID6oT.js → erDiagram-Q2GNP2WA.B4hpr69H.js} +1 -1
  31. package/dist/client/_astro/{flowDiagram-NV44I4VS.Bb1qJLxr.js → flowDiagram-NV44I4VS.CV4Oenkb.js} +1 -1
  32. package/dist/client/_astro/{ganttDiagram-JELNMOA3.3vGHETyo.js → ganttDiagram-JELNMOA3.DdpXJeX3.js} +1 -1
  33. package/dist/client/_astro/{gitGraphDiagram-NY62KEGX.Co2SKcif.js → gitGraphDiagram-NY62KEGX.BeY3JZYI.js} +1 -1
  34. package/dist/client/_astro/{graph.B5fevUwB.js → graph.CO6LxWoK.js} +1 -1
  35. package/dist/client/_astro/graph.astro_astro_type_script_index_0_lang.BxaPom9e.js +1 -0
  36. package/dist/client/_astro/{impact.astro_astro_type_script_index_0_lang.D4cAR9AL.js → impact.astro_astro_type_script_index_0_lang.VjeLQGe6.js} +1 -1
  37. package/dist/client/_astro/index.WFquGv8Z.js +9 -0
  38. package/dist/client/_astro/{infoDiagram-WHAUD3N6.B6ULtps1.js → infoDiagram-WHAUD3N6.D2_J_fR6.js} +1 -1
  39. package/dist/client/_astro/{journeyDiagram-XKPGCS4Q.BSOCzWmw.js → journeyDiagram-XKPGCS4Q.7c4ngVDw.js} +1 -1
  40. package/dist/client/_astro/{kanban-definition-3W4ZIXB7.D8KKGc1o.js → kanban-definition-3W4ZIXB7.D_eWex6x.js} +1 -1
  41. package/dist/client/_astro/{layout.8vv24qEg.js → layout.Cn9EjNLq.js} +1 -1
  42. package/dist/client/_astro/{linear.B6O9ymuK.js → linear.BlE_mLsy.js} +1 -1
  43. package/dist/client/_astro/{mermaid.core.CReXU7YN.js → mermaid.core.CgCLOZ6t.js} +5 -5
  44. package/dist/client/_astro/{min.CdGMGVU0.js → min.OtQS-qlD.js} +1 -1
  45. package/dist/client/_astro/{mindmap-definition-VGOIOE7T.G14HgtDw.js → mindmap-definition-VGOIOE7T.CjNYt18e.js} +1 -1
  46. package/dist/client/_astro/{pieDiagram-ADFJNKIX.bC2VkyoW.js → pieDiagram-ADFJNKIX.D18DPyo6.js} +1 -1
  47. package/dist/client/_astro/{quadrantDiagram-AYHSOK5B.BlLaQQxO.js → quadrantDiagram-AYHSOK5B.Cma08fPv.js} +1 -1
  48. package/dist/client/_astro/{requirementDiagram-UZGBJVZJ.DHRnMofO.js → requirementDiagram-UZGBJVZJ.BlhVOrX0.js} +1 -1
  49. package/dist/client/_astro/{sankeyDiagram-TZEHDZUN.BMuaJBmt.js → sankeyDiagram-TZEHDZUN.HRsqw4ej.js} +1 -1
  50. package/dist/client/_astro/{sequenceDiagram-WL72ISMW.CnU62wqy.js → sequenceDiagram-WL72ISMW.3cZYkaSP.js} +1 -1
  51. package/dist/client/_astro/{stateDiagram-FKZM4ZOC.CewI55YO.js → stateDiagram-FKZM4ZOC.DdlRd9N7.js} +1 -1
  52. package/dist/client/_astro/stateDiagram-v2-4FDKWEC3.DCPDjwi7.js +1 -0
  53. package/dist/client/_astro/{timeline-definition-IT6M3QCI.D1PLRwss.js → timeline-definition-IT6M3QCI.BshW8mMW.js} +1 -1
  54. package/dist/client/_astro/{treemap-KMMF4GRG.D3VNVvXF.js → treemap-KMMF4GRG.BxXN_YfK.js} +1 -1
  55. package/dist/client/_astro/{xychartDiagram-PRI3JC2R.CQex0-ul.js → xychartDiagram-PRI3JC2R.Mg5_s08f.js} +1 -1
  56. package/dist/server/entry.mjs +15 -11
  57. package/dist/server/manifest_smcahUO6.mjs +101 -0
  58. package/dist/server/pages/api/save-mock.astro.mjs +142 -0
  59. package/dist/server/pages/coverage.astro.mjs +1 -1
  60. package/dist/server/pages/editor.astro.mjs +30 -0
  61. package/dist/server/pages/graph.astro.mjs +18 -3
  62. package/dist/server/pages/impact.astro.mjs +1 -1
  63. package/dist/server/pages/index.astro.mjs +1 -1
  64. package/dist/server/pages/screen/_id_.astro.mjs +126 -3
  65. package/dist/server/renderers.mjs +200 -1
  66. package/package.json +8 -2
  67. package/src/components/MockFormEditor.tsx +1280 -0
  68. package/src/components/MockPreview.astro +811 -0
  69. package/src/pages/api/save-mock.ts +182 -0
  70. package/src/pages/editor.astro +33 -0
  71. package/src/pages/graph.astro +35 -1
  72. package/src/pages/screen/[id].astro +9 -0
  73. package/src/styles/mock-editor.css +1351 -0
  74. package/tsconfig.json +1 -0
  75. package/dist/client/_astro/channel.CNyr52v1.js +0 -1
  76. package/dist/client/_astro/classDiagram-2ON5EDUG.CxT3aW-h.js +0 -1
  77. package/dist/client/_astro/classDiagram-v2-WZHVMYZB.CxT3aW-h.js +0 -1
  78. package/dist/client/_astro/clone.U_lSZ6fe.js +0 -1
  79. package/dist/client/_astro/coverage.BnE2oGo8.css +0 -1
  80. package/dist/client/_astro/graph.astro_astro_type_script_index_0_lang.B0fEnVdy.js +0 -1
  81. package/dist/client/_astro/stateDiagram-v2-4FDKWEC3.7xUQqoNr.js +0 -1
  82. package/dist/server/manifest_CicDtuHD.mjs +0 -101
  83. /package/dist/server/chunks/{loadScreens_JhK3F9tC.mjs → loadScreens_CkCqdbH2.mjs} +0 -0
@@ -0,0 +1,182 @@
1
+ import { existsSync, readFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import type { APIRoute } from "astro"
4
+ import { Project, SyntaxKind } from "ts-morph"
5
+
6
+ interface ScreenWithFilePath {
7
+ id: string
8
+ filePath: string
9
+ [key: string]: unknown
10
+ }
11
+
12
+ interface SaveMockRequest {
13
+ screenId: string
14
+ mock: {
15
+ sections: Array<{
16
+ title?: string
17
+ layout?: string
18
+ elements: Array<Record<string, unknown>>
19
+ }>
20
+ }
21
+ }
22
+
23
+ export const POST: APIRoute = async ({ request }) => {
24
+ try {
25
+ const body = (await request.json()) as SaveMockRequest
26
+ const { screenId, mock } = body
27
+
28
+ if (!screenId || !mock) {
29
+ return new Response(
30
+ JSON.stringify({ error: "screenId and mock are required" }),
31
+ { status: 400, headers: { "Content-Type": "application/json" } },
32
+ )
33
+ }
34
+
35
+ // Load screens.json to get filePath
36
+ const screensPath = join(process.cwd(), ".screenbook", "screens.json")
37
+ if (!existsSync(screensPath)) {
38
+ return new Response(
39
+ JSON.stringify({
40
+ error: "screens.json not found. Run screenbook build first.",
41
+ }),
42
+ { status: 404, headers: { "Content-Type": "application/json" } },
43
+ )
44
+ }
45
+
46
+ const screens = JSON.parse(
47
+ readFileSync(screensPath, "utf-8"),
48
+ ) as ScreenWithFilePath[]
49
+ const screen = screens.find((s) => s.id === screenId)
50
+
51
+ if (!screen) {
52
+ return new Response(
53
+ JSON.stringify({ error: `Screen '${screenId}' not found` }),
54
+ { status: 404, headers: { "Content-Type": "application/json" } },
55
+ )
56
+ }
57
+
58
+ if (!screen.filePath) {
59
+ return new Response(
60
+ JSON.stringify({
61
+ error: `Screen '${screenId}' does not have filePath. Rebuild with latest CLI.`,
62
+ }),
63
+ { status: 400, headers: { "Content-Type": "application/json" } },
64
+ )
65
+ }
66
+
67
+ // Use ts-morph to update the file
68
+ const result = await updateMockInFile(screen.filePath, mock)
69
+
70
+ if (!result.success) {
71
+ return new Response(JSON.stringify({ error: result.error }), {
72
+ status: 500,
73
+ headers: { "Content-Type": "application/json" },
74
+ })
75
+ }
76
+
77
+ return new Response(
78
+ JSON.stringify({ success: true, filePath: screen.filePath }),
79
+ { status: 200, headers: { "Content-Type": "application/json" } },
80
+ )
81
+ } catch (error) {
82
+ const message = error instanceof Error ? error.message : "Unknown error"
83
+ return new Response(JSON.stringify({ error: message }), {
84
+ status: 500,
85
+ headers: { "Content-Type": "application/json" },
86
+ })
87
+ }
88
+ }
89
+
90
+ async function updateMockInFile(
91
+ filePath: string,
92
+ mock: SaveMockRequest["mock"],
93
+ ): Promise<{ success: boolean; error?: string }> {
94
+ try {
95
+ const project = new Project()
96
+ const sourceFile = project.addSourceFileAtPath(filePath)
97
+
98
+ // Find the defineScreen call
99
+ const callExpressions = sourceFile.getDescendantsOfKind(
100
+ SyntaxKind.CallExpression,
101
+ )
102
+ const defineScreenCall = callExpressions.find((call) => {
103
+ const expression = call.getExpression()
104
+ return expression.getText() === "defineScreen"
105
+ })
106
+
107
+ if (!defineScreenCall) {
108
+ return { success: false, error: "defineScreen() call not found in file" }
109
+ }
110
+
111
+ // Get the object literal argument
112
+ const args = defineScreenCall.getArguments()
113
+ const firstArg = args[0]
114
+ if (!firstArg) {
115
+ return { success: false, error: "defineScreen() has no arguments" }
116
+ }
117
+
118
+ if (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {
119
+ return {
120
+ success: false,
121
+ error: "defineScreen() argument is not an object literal",
122
+ }
123
+ }
124
+
125
+ const obj = firstArg.asKind(SyntaxKind.ObjectLiteralExpression)
126
+ if (!obj) {
127
+ return { success: false, error: "Failed to parse object literal" }
128
+ }
129
+
130
+ // Find existing mock property
131
+ const mockProperty = obj.getProperty("mock")
132
+
133
+ // Generate mock code string
134
+ const mockCode = generateMockCode(mock)
135
+
136
+ if (mockProperty) {
137
+ // Update existing mock property
138
+ mockProperty.replaceWithText(`mock: ${mockCode}`)
139
+ } else {
140
+ // Add new mock property at the end
141
+ obj.addPropertyAssignment({
142
+ name: "mock",
143
+ initializer: mockCode,
144
+ })
145
+ }
146
+
147
+ // Save the file
148
+ await sourceFile.save()
149
+
150
+ return { success: true }
151
+ } catch (error) {
152
+ const message = error instanceof Error ? error.message : "Unknown error"
153
+ return { success: false, error: message }
154
+ }
155
+ }
156
+
157
+ function generateMockCode(mock: SaveMockRequest["mock"]): string {
158
+ const sections = mock.sections.map((section) => {
159
+ const props: string[] = []
160
+
161
+ if (section.title) {
162
+ props.push(`title: ${JSON.stringify(section.title)}`)
163
+ }
164
+ if (section.layout && section.layout !== "vertical") {
165
+ props.push(`layout: ${JSON.stringify(section.layout)}`)
166
+ }
167
+
168
+ const elements = section.elements.map((element) => {
169
+ const elementProps = Object.entries(element)
170
+ .filter(([_, value]) => value !== undefined && value !== "")
171
+ .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
172
+ .join(", ")
173
+ return `{ ${elementProps} }`
174
+ })
175
+
176
+ props.push(`elements: [\n\t\t\t${elements.join(",\n\t\t\t")},\n\t\t]`)
177
+
178
+ return `{\n\t\t${props.join(",\n\t\t")},\n\t}`
179
+ })
180
+
181
+ return `{\n\tsections: [\n\t${sections.join(",\n\t")},\n\t],\n}`
182
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ import Layout from "@/layouts/Layout.astro"
3
+ import { MockFormEditor } from "@/components/MockFormEditor"
4
+ import { loadScreens } from "@/utils/loadScreens"
5
+
6
+ const screens = loadScreens()
7
+ const url = Astro.url
8
+ const screenId = url.searchParams.get("screen")
9
+
10
+ const screen = screenId ? screens.find((s) => s.id === screenId) : null
11
+ ---
12
+
13
+ <Layout title="Mock Editor">
14
+ <div class="editor-container">
15
+ <MockFormEditor
16
+ client:only="react"
17
+ screenId={screen?.id}
18
+ screenTitle={screen?.title || "New Screen"}
19
+ initialMock={screen?.mock}
20
+ />
21
+ </div>
22
+ </Layout>
23
+
24
+ <style>
25
+ .editor-container {
26
+ position: fixed;
27
+ top: 60px;
28
+ left: 0;
29
+ right: 0;
30
+ bottom: 0;
31
+ background: #141822;
32
+ }
33
+ </style>
@@ -6,13 +6,19 @@ const screens = loadScreens()
6
6
 
7
7
  // Generate Navigation Mermaid graph
8
8
  let navigationGraph = ""
9
+ const screensWithMock: string[] = []
9
10
  if (screens.length > 0) {
10
11
  const lines: string[] = ["flowchart TD"]
11
12
 
12
13
  for (const screen of screens) {
13
14
  const label = screen.title.replace(/"/g, "'")
14
15
  const id = screen.id.replace(/\./g, "_")
15
- lines.push(` ${id}["${label}"]`)
16
+ const mockIndicator = screen.mock ? " 📱" : ""
17
+ lines.push(` ${id}["${label}${mockIndicator}"]`)
18
+ lines.push(` click ${id} "/screen/${screen.id}"`)
19
+ if (screen.mock) {
20
+ screensWithMock.push(id)
21
+ }
16
22
  }
17
23
 
18
24
  lines.push("")
@@ -27,9 +33,22 @@ if (screens.length > 0) {
27
33
  }
28
34
  }
29
35
 
36
+ // Add styling for screens with mock
37
+ if (screensWithMock.length > 0) {
38
+ lines.push("")
39
+ lines.push(" %% Mock styling")
40
+ lines.push(" classDef hasMock stroke:#14b8a6,stroke-width:2px")
41
+ for (const id of screensWithMock) {
42
+ lines.push(` class ${id} hasMock`)
43
+ }
44
+ }
45
+
30
46
  navigationGraph = lines.join("\n")
31
47
  }
32
48
 
49
+ // Count screens with mock
50
+ const mockCount = screens.filter(s => s.mock).length
51
+
33
52
  // Generate API Dependency Mermaid graph
34
53
  let apiGraph = ""
35
54
  if (screens.length > 0) {
@@ -145,6 +164,7 @@ const apiCount = new Set(
145
164
  <div class="graph-stats">
146
165
  <span class="stat">{screens.length} screens</span>
147
166
  <span class="stat">{apiCount} APIs</span>
167
+ {mockCount > 0 && <span class="stat stat-mock">📱 {mockCount} with mock</span>}
148
168
  </div>
149
169
  </div>
150
170
 
@@ -161,6 +181,10 @@ const apiCount = new Set(
161
181
  <div class="legend-node"></div>
162
182
  <span>Screen</span>
163
183
  </div>
184
+ <div class="graph-legend-item">
185
+ <div class="legend-node legend-mock"></div>
186
+ <span>Screen with mock 📱</span>
187
+ </div>
164
188
  <div class="graph-legend-item">
165
189
  <div class="legend-edge"></div>
166
190
  <span>Navigation flow</span>
@@ -191,6 +215,7 @@ const apiCount = new Set(
191
215
  const mermaidConfig = {
192
216
  startOnLoad: false,
193
217
  theme: "dark",
218
+ securityLevel: "loose", // Enable click handlers
194
219
  themeVariables: {
195
220
  darkMode: true,
196
221
  background: "transparent",
@@ -331,4 +356,13 @@ const apiCount = new Set(
331
356
  background: #14b8a6 !important;
332
357
  border-color: #5eead4 !important;
333
358
  }
359
+
360
+ .legend-mock {
361
+ border-color: #14b8a6 !important;
362
+ border-width: 2px !important;
363
+ }
364
+
365
+ .stat-mock {
366
+ color: #14b8a6;
367
+ }
334
368
  </style>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import Layout from "@/layouts/Layout.astro"
3
+ import MockPreview from "@/components/MockPreview.astro"
3
4
  import { loadScreens } from "@/utils/loadScreens"
4
5
  import { getApiDependencyCount } from "@/utils/impactAnalysis"
5
6
 
@@ -128,6 +129,14 @@ const nextScreens = screens.filter((s) => screen.next?.includes(s.id))
128
129
  </div>
129
130
  </div>
130
131
  )}
132
+
133
+ <!-- Mock Preview -->
134
+ {screen.mock && (
135
+ <div class="section">
136
+ <h2 class="section-title">UI Wireframe</h2>
137
+ <MockPreview mock={screen.mock} screenId={screen.id} />
138
+ </div>
139
+ )}
131
140
  </div>
132
141
 
133
142
  <div>