@fugood/bricks-ctor 2.25.0-beta.60 → 2.25.0-beta.62

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 (192) hide show
  1. package/package.json +4 -28
  2. package/tools/__tests__/legacy-forwarder.test.js +91 -0
  3. package/tools/_forward.ts +26 -0
  4. package/tools/deploy.ts +3 -175
  5. package/tools/mcp-server.ts +3 -35
  6. package/tools/postinstall.ts +3 -291
  7. package/tools/pull.ts +3 -198
  8. package/tools/push-config.ts +3 -113
  9. package/tools/simulator.ts +3 -149
  10. package/compile/__tests__/config-diff.test.js +0 -100
  11. package/compile/__tests__/index.test.js +0 -461
  12. package/compile/__tests__/util.test.js +0 -450
  13. package/compile/action-name-map.ts +0 -1079
  14. package/compile/config-diff.ts +0 -155
  15. package/compile/index.ts +0 -1594
  16. package/compile/util.ts +0 -482
  17. package/index.ts +0 -6
  18. package/skills/bricks-ctor/SKILL.md +0 -38
  19. package/skills/bricks-ctor/references/animation.md +0 -160
  20. package/skills/bricks-ctor/references/architecture-patterns.md +0 -88
  21. package/skills/bricks-ctor/references/automations.md +0 -232
  22. package/skills/bricks-ctor/references/buttress.md +0 -245
  23. package/skills/bricks-ctor/references/data-calculation.md +0 -252
  24. package/skills/bricks-ctor/references/local-sync.md +0 -129
  25. package/skills/bricks-ctor/references/media-flow.md +0 -165
  26. package/skills/bricks-ctor/references/remote-data-bank.md +0 -196
  27. package/skills/bricks-ctor/references/simulator.md +0 -132
  28. package/skills/bricks-ctor/references/source-editing-tools.md +0 -81
  29. package/skills/bricks-ctor/references/standby-transition.md +0 -124
  30. package/skills/bricks-ctor/references/verification-toolchain.md +0 -200
  31. package/skills/bricks-design/SKILL.md +0 -171
  32. package/skills/bricks-design/references/architecture-truths.md +0 -132
  33. package/skills/bricks-design/references/avoiding-complexity.md +0 -91
  34. package/skills/bricks-design/references/design-critique.md +0 -195
  35. package/skills/bricks-design/references/design-languages.md +0 -265
  36. package/skills/bricks-design/references/performance.md +0 -116
  37. package/skills/bricks-design/references/presentation-and-slideshow.md +0 -137
  38. package/skills/bricks-design/references/translating-inputs.md +0 -152
  39. package/skills/bricks-design/references/variations-and-tweaks.md +0 -124
  40. package/skills/bricks-design/references/when-the-brief-is-branded.md +0 -284
  41. package/skills/bricks-design/references/when-the-brief-is-vague.md +0 -85
  42. package/skills/bricks-design/references/workflow.md +0 -134
  43. package/skills/bricks-ux/SKILL.md +0 -114
  44. package/skills/bricks-ux/references/accessibility.md +0 -162
  45. package/skills/bricks-ux/references/flow-states.md +0 -175
  46. package/skills/bricks-ux/references/interaction-archetypes.md +0 -189
  47. package/skills/bricks-ux/references/monitoring-screens.md +0 -153
  48. package/skills/bricks-ux/references/pressable-composition.md +0 -126
  49. package/skills/bricks-ux/references/user-journey.md +0 -168
  50. package/skills/bricks-ux/references/ux-critique.md +0 -256
  51. package/skills/rive-marketplace/SKILL.md +0 -99
  52. package/tools/__tests__/_cli-error.test.ts +0 -35
  53. package/tools/__tests__/_mcp-config.test.ts +0 -67
  54. package/tools/__tests__/pull.test.ts +0 -108
  55. package/tools/_cli-error.ts +0 -17
  56. package/tools/_edits-log.ts +0 -41
  57. package/tools/_git-author.ts +0 -37
  58. package/tools/_last-pushed-commit.ts +0 -28
  59. package/tools/_mcp-config.ts +0 -42
  60. package/tools/_shell.ts +0 -180
  61. package/tools/icons/.gitattributes +0 -1
  62. package/tools/icons/fa6pro-glyphmap.json +0 -4686
  63. package/tools/icons/fa6pro-meta.json +0 -1
  64. package/tools/mcp-env.ts +0 -13
  65. package/tools/mcp-tools/__tests__/data-calc-editing.test.js +0 -516
  66. package/tools/mcp-tools/__tests__/entry-editing.test.js +0 -866
  67. package/tools/mcp-tools/__tests__/huggingface.test.ts +0 -49
  68. package/tools/mcp-tools/__tests__/icons.test.ts +0 -21
  69. package/tools/mcp-tools/__tests__/mcp-env.test.js +0 -19
  70. package/tools/mcp-tools/_editing-helpers.ts +0 -98
  71. package/tools/mcp-tools/_verify.ts +0 -50
  72. package/tools/mcp-tools/compile.ts +0 -104
  73. package/tools/mcp-tools/data-calc-editing.ts +0 -1311
  74. package/tools/mcp-tools/entry-editing.ts +0 -2297
  75. package/tools/mcp-tools/huggingface.ts +0 -772
  76. package/tools/mcp-tools/icons.ts +0 -97
  77. package/tools/mcp-tools/lottie.ts +0 -102
  78. package/tools/mcp-tools/media.ts +0 -113
  79. package/tools/simulator-main.mjs +0 -488
  80. package/tools/simulator-preload.cjs +0 -16
  81. package/types/animation.d.ts +0 -116
  82. package/types/automation.d.ts +0 -231
  83. package/types/brick-base.d.ts +0 -80
  84. package/types/bricks/Camera.d.ts +0 -246
  85. package/types/bricks/Chart.d.ts +0 -372
  86. package/types/bricks/GenerativeMedia.d.ts +0 -290
  87. package/types/bricks/Icon.d.ts +0 -98
  88. package/types/bricks/Image.d.ts +0 -126
  89. package/types/bricks/Items.d.ts +0 -480
  90. package/types/bricks/Lottie.d.ts +0 -168
  91. package/types/bricks/Maps.d.ts +0 -262
  92. package/types/bricks/QrCode.d.ts +0 -117
  93. package/types/bricks/Rect.d.ts +0 -150
  94. package/types/bricks/RichText.d.ts +0 -131
  95. package/types/bricks/Rive.d.ts +0 -220
  96. package/types/bricks/Scene3D.d.ts +0 -676
  97. package/types/bricks/Sketch.d.ts +0 -256
  98. package/types/bricks/Slideshow.d.ts +0 -201
  99. package/types/bricks/Svg.d.ts +0 -99
  100. package/types/bricks/Text.d.ts +0 -148
  101. package/types/bricks/TextInput.d.ts +0 -242
  102. package/types/bricks/Video.d.ts +0 -242
  103. package/types/bricks/VideoStreaming.d.ts +0 -112
  104. package/types/bricks/WebRtcStream.d.ts +0 -65
  105. package/types/bricks/WebView.d.ts +0 -168
  106. package/types/bricks/index.d.ts +0 -23
  107. package/types/canvas.d.ts +0 -82
  108. package/types/common.d.ts +0 -141
  109. package/types/data-calc-command/base.d.ts +0 -57
  110. package/types/data-calc-command/collection.d.ts +0 -418
  111. package/types/data-calc-command/color.d.ts +0 -432
  112. package/types/data-calc-command/constant.d.ts +0 -50
  113. package/types/data-calc-command/datetime.d.ts +0 -147
  114. package/types/data-calc-command/file.d.ts +0 -129
  115. package/types/data-calc-command/index.d.ts +0 -13
  116. package/types/data-calc-command/iteratee.d.ts +0 -23
  117. package/types/data-calc-command/logictype.d.ts +0 -190
  118. package/types/data-calc-command/math.d.ts +0 -275
  119. package/types/data-calc-command/object.d.ts +0 -119
  120. package/types/data-calc-command/sandbox.d.ts +0 -66
  121. package/types/data-calc-command/string.d.ts +0 -407
  122. package/types/data-calc-script.d.ts +0 -21
  123. package/types/data-calc.d.ts +0 -12
  124. package/types/data.d.ts +0 -97
  125. package/types/generators/AlarmClock.d.ts +0 -110
  126. package/types/generators/Assistant.d.ts +0 -640
  127. package/types/generators/BleCentral.d.ts +0 -247
  128. package/types/generators/BlePeripheral.d.ts +0 -208
  129. package/types/generators/CanvasMap.d.ts +0 -74
  130. package/types/generators/CastlesPay.d.ts +0 -87
  131. package/types/generators/DataBank.d.ts +0 -160
  132. package/types/generators/File.d.ts +0 -432
  133. package/types/generators/GraphQl.d.ts +0 -132
  134. package/types/generators/Http.d.ts +0 -222
  135. package/types/generators/HttpServer.d.ts +0 -230
  136. package/types/generators/Information.d.ts +0 -103
  137. package/types/generators/Intent.d.ts +0 -168
  138. package/types/generators/Iterator.d.ts +0 -108
  139. package/types/generators/Keyboard.d.ts +0 -105
  140. package/types/generators/LlmAnthropicCompat.d.ts +0 -212
  141. package/types/generators/LlmAppleBuiltin.d.ts +0 -159
  142. package/types/generators/LlmGgml.d.ts +0 -903
  143. package/types/generators/LlmMediaTekNeuroPilot.d.ts +0 -235
  144. package/types/generators/LlmMlx.d.ts +0 -228
  145. package/types/generators/LlmOnnx.d.ts +0 -213
  146. package/types/generators/LlmOpenAiCompat.d.ts +0 -312
  147. package/types/generators/LlmQualcommAiEngine.d.ts +0 -247
  148. package/types/generators/Mcp.d.ts +0 -637
  149. package/types/generators/McpServer.d.ts +0 -289
  150. package/types/generators/MediaFlow.d.ts +0 -170
  151. package/types/generators/MqttBroker.d.ts +0 -141
  152. package/types/generators/MqttClient.d.ts +0 -141
  153. package/types/generators/Question.d.ts +0 -408
  154. package/types/generators/RealtimeTranscription.d.ts +0 -287
  155. package/types/generators/RerankerGgml.d.ts +0 -195
  156. package/types/generators/SerialPort.d.ts +0 -151
  157. package/types/generators/SoundPlayer.d.ts +0 -94
  158. package/types/generators/SoundRecorder.d.ts +0 -139
  159. package/types/generators/SpeechToTextGgml.d.ts +0 -424
  160. package/types/generators/SpeechToTextOnnx.d.ts +0 -236
  161. package/types/generators/SpeechToTextPlatform.d.ts +0 -85
  162. package/types/generators/SqLite.d.ts +0 -159
  163. package/types/generators/Step.d.ts +0 -107
  164. package/types/generators/SttAppleBuiltin.d.ts +0 -153
  165. package/types/generators/Tcp.d.ts +0 -126
  166. package/types/generators/TcpServer.d.ts +0 -147
  167. package/types/generators/TextToSpeechAppleBuiltin.d.ts +0 -127
  168. package/types/generators/TextToSpeechGgml.d.ts +0 -221
  169. package/types/generators/TextToSpeechOnnx.d.ts +0 -178
  170. package/types/generators/TextToSpeechOpenAiLike.d.ts +0 -121
  171. package/types/generators/ThermalPrinter.d.ts +0 -193
  172. package/types/generators/Tick.d.ts +0 -83
  173. package/types/generators/Udp.d.ts +0 -120
  174. package/types/generators/VadGgml.d.ts +0 -260
  175. package/types/generators/VadOnnx.d.ts +0 -231
  176. package/types/generators/VadTraditional.d.ts +0 -138
  177. package/types/generators/VectorStore.d.ts +0 -257
  178. package/types/generators/Watchdog.d.ts +0 -107
  179. package/types/generators/WebCrawler.d.ts +0 -103
  180. package/types/generators/WebRtc.d.ts +0 -181
  181. package/types/generators/WebSocket.d.ts +0 -148
  182. package/types/generators/index.d.ts +0 -57
  183. package/types/index.d.ts +0 -13
  184. package/types/subspace.d.ts +0 -60
  185. package/types/switch.d.ts +0 -51
  186. package/types/system.d.ts +0 -707
  187. package/utils/__tests__/calc.test.js +0 -25
  188. package/utils/__tests__/id.test.js +0 -154
  189. package/utils/calc.ts +0 -130
  190. package/utils/data.ts +0 -495
  191. package/utils/event-props.ts +0 -912
  192. package/utils/id.ts +0 -133
@@ -1,488 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
- import { app, BrowserWindow, ipcMain } from 'electron'
3
- import { mkdir, readdir, readFile, rm, writeFile } from 'fs/promises'
4
- import { watchFile } from 'fs'
5
- import { createServer } from 'http'
6
- import path from 'node:path'
7
- import { fileURLToPath } from 'node:url'
8
- import { parseArgs } from 'util'
9
- import * as TOON from '@toon-format/toon'
10
-
11
- for (const stream of [process.stdout, process.stderr]) {
12
- stream.on('error', (err) => {
13
- if (err.code !== 'EPIPE') throw err
14
- })
15
- }
16
-
17
- const { values } = parseArgs({
18
- args: process.argv,
19
- options: {
20
- 'clear-cache': { type: 'boolean' },
21
- 'take-screenshot': { type: 'string' },
22
- 'show-menu': { type: 'boolean' },
23
- 'test-id': { type: 'string' },
24
- 'test-title-like': { type: 'string' },
25
- 'no-keep-open': { type: 'boolean' },
26
- 'cdp-port': { type: 'string' },
27
- },
28
- strict: true,
29
- allowPositionals: true,
30
- })
31
-
32
- const cwd = process.cwd()
33
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
34
- const AUTOMATION_SCREENSHOT_PROJECT_DIR = path.join('.bricks', 'automation_screenshots')
35
- const DEFAULT_SIMULATOR_SECURITY_SETTINGS = Object.freeze({
36
- allowedInsecureHosts: [],
37
- })
38
-
39
- const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value)
40
-
41
- const resolveAutomationScreenshotPath = (requestedPath = '') => {
42
- const baseDir = path.resolve(cwd, AUTOMATION_SCREENSHOT_PROJECT_DIR)
43
- const raw = String(requestedPath || AUTOMATION_SCREENSHOT_PROJECT_DIR)
44
- const fromProject = path.resolve(cwd, raw)
45
- const target =
46
- fromProject === baseDir || fromProject.startsWith(`${baseDir}${path.sep}`)
47
- ? fromProject
48
- : path.resolve(baseDir, raw)
49
-
50
- if (target !== baseDir && !target.startsWith(`${baseDir}${path.sep}`)) {
51
- throw new Error('Automation screenshot path must stay inside the project screenshot directory.')
52
- }
53
- return target
54
- }
55
-
56
- const normalizeAutomationScreenshotEncoding = (encoding) =>
57
- encoding === 'base64' ? 'base64' : 'utf8'
58
-
59
- const registerAutomationScreenshotIpc = () => {
60
- ipcMain.handle('bricks-automation-screenshots:exists', async (_event, filePath) => {
61
- try {
62
- await readFile(resolveAutomationScreenshotPath(filePath))
63
- return true
64
- } catch {
65
- return false
66
- }
67
- })
68
- ipcMain.handle('bricks-automation-screenshots:readdir', async (_event, dirPath) => {
69
- try {
70
- return await readdir(resolveAutomationScreenshotPath(dirPath))
71
- } catch {
72
- return []
73
- }
74
- })
75
- ipcMain.handle('bricks-automation-screenshots:mkdir', async (_event, dirPath) => {
76
- await mkdir(resolveAutomationScreenshotPath(dirPath), { recursive: true })
77
- })
78
- ipcMain.handle('bricks-automation-screenshots:unlink', async (_event, filePath) => {
79
- await rm(resolveAutomationScreenshotPath(filePath), { recursive: true, force: true })
80
- })
81
- ipcMain.handle('bricks-automation-screenshots:read-file', async (_event, filePath, encoding) =>
82
- readFile(
83
- resolveAutomationScreenshotPath(filePath),
84
- normalizeAutomationScreenshotEncoding(encoding),
85
- ),
86
- )
87
- ipcMain.handle(
88
- 'bricks-automation-screenshots:write-file',
89
- async (_event, filePath, contents, encoding) => {
90
- const target = resolveAutomationScreenshotPath(filePath)
91
- await mkdir(path.dirname(target), { recursive: true })
92
- await writeFile(target, contents, normalizeAutomationScreenshotEncoding(encoding))
93
- },
94
- )
95
- }
96
-
97
- const normalizeSimulatorSecuritySettings = (settings = {}) => {
98
- const source = isObject(settings) ? settings : {}
99
- const allowedHosts = Array.isArray(source.allowedInsecureHosts)
100
- ? source.allowedInsecureHosts
101
- : DEFAULT_SIMULATOR_SECURITY_SETTINGS.allowedInsecureHosts
102
-
103
- return {
104
- allowedInsecureHosts: allowedHosts
105
- .filter((host) => typeof host === 'string')
106
- .map((host) => host.trim().toLowerCase())
107
- .filter(Boolean),
108
- }
109
- }
110
-
111
- const normalizeAllowedConnection = (value) => {
112
- const raw = String(value || '')
113
- .trim()
114
- .toLowerCase()
115
- if (!raw) return null
116
- if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return { host: raw, protocol: null }
117
- try {
118
- const url = new URL(raw)
119
- return {
120
- host: url.host.toLowerCase(),
121
- protocol: url.protocol,
122
- }
123
- } catch {
124
- return null
125
- }
126
- }
127
-
128
- const shouldBlockInsecureConnection = (rawUrl, settings = {}) => {
129
- const normalized = normalizeSimulatorSecuritySettings(settings)
130
-
131
- try {
132
- const url = new URL(String(rawUrl || ''))
133
- if (url.protocol !== 'http:' && url.protocol !== 'ws:') return false
134
- const host = url.host.toLowerCase()
135
- const hostname = url.hostname.toLowerCase().replace(/^\[|\]$/g, '')
136
- return !normalized.allowedInsecureHosts.some((allowedHost) => {
137
- const allowed = normalizeAllowedConnection(allowedHost)
138
- if (!allowed) return false
139
- if (allowed.protocol && allowed.protocol !== url.protocol) return false
140
- return allowed.host === host || allowed.host === hostname
141
- })
142
- } catch {
143
- return false
144
- }
145
- }
146
-
147
- const getUnsecureConnectionHost = (rawUrl) => {
148
- try {
149
- const url = new URL(String(rawUrl || ''))
150
- if (url.protocol !== 'http:' && url.protocol !== 'ws:') return null
151
- return url.host.toLowerCase()
152
- } catch {
153
- return null
154
- }
155
- }
156
-
157
- const readSimulatorProjectSettings = async () => {
158
- try {
159
- return normalizeSimulatorSecuritySettings(
160
- JSON.parse(await readFile(`${cwd}/simulator.json`, 'utf8')),
161
- )
162
- } catch {
163
- return normalizeSimulatorSecuritySettings()
164
- }
165
- }
166
-
167
- let takeScreenshotConfig = null
168
- try {
169
- if (values['take-screenshot']) {
170
- takeScreenshotConfig = JSON.parse(values['take-screenshot'])
171
- if (!takeScreenshotConfig.path) takeScreenshotConfig.path = `${cwd}/screenshot.jpg`
172
- if (!takeScreenshotConfig.width) takeScreenshotConfig.width = 1280
173
- if (!takeScreenshotConfig.height) takeScreenshotConfig.height = 768
174
- if (!takeScreenshotConfig.delay) takeScreenshotConfig.delay = 1000
175
- }
176
- } catch (e) {
177
- console.error('Invalid take-screenshot config', e)
178
- // eslint-disable-next-line unicorn/no-process-exit
179
- process.exit(1)
180
- }
181
-
182
- let config = JSON.parse(await readFile(`${cwd}/.bricks/build/application-config.json`))
183
- let simulatorSettings = await readSimulatorProjectSettings()
184
-
185
- // Resolve testId from testTitleLike
186
- let testId = values['test-id'] || null
187
- if (!testId && values['test-title-like']) {
188
- const titleLike = values['test-title-like'].toLowerCase()
189
- const automationMap = config.automation_map || {}
190
- for (const group of Object.values(automationMap)) {
191
- const found = Object.entries(group.map || {}).find(([, test]) =>
192
- test.title?.toLowerCase().includes(titleLike),
193
- )
194
- if (found) {
195
- ;[testId] = found
196
- break
197
- }
198
- }
199
- if (!testId) {
200
- throw new Error(`No automation found matching title: ${values['test-title-like']}`)
201
- }
202
- }
203
-
204
- const noKeepOpen = values['no-keep-open'] ?? false
205
-
206
- const stage = process.env.BRICKS_STAGE || 'production'
207
-
208
- const previewUrlMap = {
209
- production: 'https://control.bricks.tools/applicationPreview.html',
210
- beta: 'https://control-beta.bricks.tools/applicationPreview.html',
211
- development: 'http://localhost:3006/dev-applicationPreview.html',
212
- }
213
-
214
- const previewUrl = previewUrlMap[stage]
215
- if (!previewUrl) throw new Error(`Invalid BRICKS_STAGE: ${stage}`)
216
- simulatorSettings = normalizeSimulatorSecuritySettings({
217
- ...simulatorSettings,
218
- allowedInsecureHosts: [
219
- ...simulatorSettings.allowedInsecureHosts,
220
- getUnsecureConnectionHost(previewUrl),
221
- ].filter(Boolean),
222
- })
223
-
224
- // --- CDP WebSocket Server ---
225
- // Bridges external CDP clients to the preview's postMessage-based CDP bridge.
226
- // Usage: --cdp-port 9222
227
-
228
- const startCdpServer = async (mainWindow, port) => {
229
- const { WebSocketServer } = await import('ws')
230
- const clients = new Set()
231
-
232
- // Inject a listener in the preview that forwards CDP responses/events via console
233
- const injectCdpRelay = () =>
234
- mainWindow.webContents.executeJavaScript(`
235
- if (!window.__cdpRelayInstalled) {
236
- window.__cdpRelayInstalled = true
237
- window.addEventListener('message', (evt) => {
238
- try {
239
- const d = typeof evt.data === 'string' ? JSON.parse(evt.data) : evt.data
240
- if (d && (d.type === 'cdp-response' || d.type === 'cdp-event'))
241
- console.log('[CDP]' + JSON.stringify(d))
242
- } catch {}
243
- })
244
- }
245
- `)
246
-
247
- // Capture CDP messages from the preview's console output
248
- mainWindow.webContents.on('console-message', (_event, ...rest) => {
249
- const message = typeof rest[0] === 'object' ? rest[0].message : rest[1]
250
- if (!message?.startsWith('[CDP]')) return
251
- try {
252
- const data = JSON.parse(message.slice(5))
253
- // Translate to standard CDP wire format (strip internal type field)
254
- const wireMsg =
255
- data.type === 'cdp-response'
256
- ? { id: data.id, result: data.result, error: data.error }
257
- : { method: data.method, params: data.params }
258
- if (data.sessionId) wireMsg.sessionId = data.sessionId
259
- const payload = JSON.stringify(wireMsg)
260
- for (const c of clients) if (c.readyState === 1) c.send(payload)
261
- } catch {}
262
- })
263
-
264
- // Find an available port before binding
265
- const findPort = (p) =>
266
- new Promise((resolve) => {
267
- const probe = createServer()
268
- probe.once('error', () => {
269
- if (p < port + 100) resolve(findPort(p + 1))
270
- else resolve(null)
271
- })
272
- probe.listen(p, () => probe.close(() => resolve(p)))
273
- })
274
-
275
- const actualPort = await findPort(port)
276
- if (!actualPort) {
277
- console.warn(`CDP server: no available port in range ${port}-${port + 99}`)
278
- return
279
- }
280
-
281
- // HTTP discovery endpoints (chrome://inspect, Playwright, etc.)
282
- const httpServer = createServer((req, res) => {
283
- if (req.url === '/json/list' || req.url === '/json') {
284
- res.writeHead(200, { 'Content-Type': 'application/json' })
285
- res.end(
286
- JSON.stringify([
287
- {
288
- description: 'BRICKS Simulator',
289
- id: 'bricks-simulator',
290
- title: 'BRICKS Simulator',
291
- type: 'page',
292
- url: previewUrl,
293
- webSocketDebuggerUrl: `ws://localhost:${actualPort}/ws`,
294
- },
295
- ]),
296
- )
297
- return
298
- }
299
- if (req.url === '/json/version') {
300
- res.writeHead(200, { 'Content-Type': 'application/json' })
301
- res.end(JSON.stringify({ Browser: 'BRICKS Simulator', 'Protocol-Version': '1.3' }))
302
- return
303
- }
304
- // bricks-cli discovery endpoint
305
- if (req.url === '/devtools/info') {
306
- res.writeHead(200, { 'Content-Type': 'application/json' })
307
- res.end(
308
- JSON.stringify({
309
- name: 'BRICKS Simulator',
310
- port: actualPort,
311
- protocols: ['cdp'],
312
- hasPasscode: false,
313
- }),
314
- )
315
- return
316
- }
317
- res.writeHead(404)
318
- res.end()
319
- })
320
-
321
- const wss = new WebSocketServer({ server: httpServer, path: '/ws' })
322
- wss.on('connection', (ws) => {
323
- clients.add(ws)
324
- injectCdpRelay()
325
-
326
- ws.on('message', (raw) => {
327
- try {
328
- const req = JSON.parse(raw.toString())
329
- const cdpReq = JSON.stringify({
330
- type: 'cdp-request',
331
- id: req.id,
332
- method: req.method,
333
- params: req.params || {},
334
- sessionId: req.sessionId,
335
- })
336
- mainWindow.webContents.executeJavaScript(`window.postMessage(${JSON.stringify(cdpReq)})`)
337
- } catch {}
338
- })
339
-
340
- ws.on('close', () => clients.delete(ws))
341
- })
342
-
343
- httpServer.listen(actualPort, () => {
344
- console.log(`CDP server: ws://localhost:${actualPort}/ws`)
345
- console.log(
346
- `Inspect: devtools://devtools/bundled/inspector.html?ws=localhost:${actualPort}/ws`,
347
- )
348
- })
349
- }
350
-
351
- app.on('ready', () => {
352
- registerAutomationScreenshotIpc()
353
- let show = true
354
- if (takeScreenshotConfig && !takeScreenshotConfig.noHeadless) show = false
355
- const mainWindow = new BrowserWindow({
356
- width: takeScreenshotConfig?.width || 1280,
357
- height: takeScreenshotConfig?.height || 768,
358
- frame: !takeScreenshotConfig,
359
- show,
360
- webPreferences: {
361
- preload: path.join(__dirname, 'simulator-preload.cjs'),
362
- contextIsolation: true,
363
- nodeIntegration: false,
364
- },
365
- })
366
- mainWindow.setBackgroundColor('#333')
367
- mainWindow.webContents.session.webRequest.onBeforeRequest(
368
- { urls: ['http://*/*', 'ws://*/*'] },
369
- (details, callback) => {
370
- callback({ cancel: shouldBlockInsecureConnection(details.url, simulatorSettings) })
371
- },
372
- )
373
- mainWindow.loadURL(previewUrl)
374
-
375
- const sendConfig = () => {
376
- const payload = {
377
- type: 'config',
378
- configFile: { _originTitle: 'Test', ...config, title: Math.random().toString() },
379
- workspace: { billing: { lock: {}, plan: 'free' } },
380
- showMenu: values['show-menu'] || false,
381
- testId,
382
- }
383
- mainWindow.webContents.executeJavaScript(
384
- `window.postMessage(JSON.stringify(${JSON.stringify(payload)}))`,
385
- )
386
- if (takeScreenshotConfig) {
387
- const { delay, width, height, path } = takeScreenshotConfig
388
- setTimeout(async () => {
389
- let screenshotFailed = false
390
- console.log('Taking screenshot')
391
- try {
392
- const image = await mainWindow.webContents.capturePage()
393
- console.log('Writing screenshot to', path)
394
- await writeFile(path, image.resize({ width, height }).toJPEG(75))
395
- } catch (err) {
396
- screenshotFailed = true
397
- console.error('Screenshot capture/write failed', err)
398
- } finally {
399
- if (noKeepOpen) {
400
- console.log('Closing app')
401
- if (screenshotFailed) app.exit(1)
402
- else app.quit()
403
- }
404
- }
405
- }, delay)
406
- }
407
- }
408
-
409
- mainWindow.webContents.once('dom-ready', () => {
410
- sendConfig()
411
- // Listen for test result messages from the preview
412
- if (testId) {
413
- mainWindow.webContents.executeJavaScript(`
414
- window.addEventListener('message', (evt) => {
415
- try {
416
- const data = JSON.parse(evt.data)
417
- if (data.type === 'bricks-preview-test-result') {
418
- console.log('[TEST_RESULT]' + JSON.stringify(data))
419
- }
420
- } catch (e) {}
421
- })
422
- `)
423
- }
424
- // Start CDP WebSocket server if requested
425
- const cdpPort = values['cdp-port'] ? parseInt(values['cdp-port'], 10) : null
426
- if (cdpPort) startCdpServer(mainWindow, cdpPort)
427
- })
428
-
429
- // Capture console messages from the preview
430
- if (testId) {
431
- mainWindow.webContents.on('console-message', (_event, ...rest) => {
432
- const message = typeof rest[0] === 'object' ? rest[0].message : rest[1]
433
- if (message?.startsWith('[TEST_RESULT]')) {
434
- const data = JSON.parse(message.replace('[TEST_RESULT]', ''))
435
- console.log(`[TEST_RESULT_TOON]${TOON.encode(data.result)}`)
436
- if (!takeScreenshotConfig && noKeepOpen) app.quit()
437
- }
438
- })
439
- }
440
-
441
- if (values['clear-cache']) {
442
- console.log('Clearing cache')
443
- mainWindow.webContents.session.clearCache()
444
- }
445
-
446
- mainWindow.on('close', () => app.quit())
447
-
448
- watchFile(
449
- `${cwd}/.bricks/build/application-config.json`,
450
- {
451
- bigint: false,
452
- persistent: true,
453
- interval: 1000,
454
- },
455
- async () => {
456
- console.log('Detected config changed')
457
- config = JSON.parse(await readFile(`${cwd}/.bricks/build/application-config.json`))
458
- const nextSimulatorSettings = await readSimulatorProjectSettings()
459
- simulatorSettings = normalizeSimulatorSecuritySettings({
460
- ...nextSimulatorSettings,
461
- allowedInsecureHosts: [
462
- ...nextSimulatorSettings.allowedInsecureHosts,
463
- getUnsecureConnectionHost(previewUrl),
464
- ].filter(Boolean),
465
- })
466
- sendConfig()
467
- },
468
- )
469
- watchFile(
470
- `${cwd}/simulator.json`,
471
- {
472
- bigint: false,
473
- persistent: true,
474
- interval: 1000,
475
- },
476
- async () => {
477
- console.log('Detected simulator settings changed')
478
- const nextSimulatorSettings = await readSimulatorProjectSettings()
479
- simulatorSettings = normalizeSimulatorSecuritySettings({
480
- ...nextSimulatorSettings,
481
- allowedInsecureHosts: [
482
- ...nextSimulatorSettings.allowedInsecureHosts,
483
- getUnsecureConnectionHost(previewUrl),
484
- ].filter(Boolean),
485
- })
486
- },
487
- )
488
- })
@@ -1,16 +0,0 @@
1
- const { contextBridge, ipcRenderer } = require('electron')
2
-
3
- const AUTOMATION_SCREENSHOT_ROOT = '.bricks/automation_screenshots'
4
-
5
- contextBridge.exposeInMainWorld('BRICKS_AUTOMATION_SCREENSHOTS', {
6
- rootDir: AUTOMATION_SCREENSHOT_ROOT,
7
- exists: (filePath) => ipcRenderer.invoke('bricks-automation-screenshots:exists', filePath),
8
- readdir: (dirPath) => ipcRenderer.invoke('bricks-automation-screenshots:readdir', dirPath),
9
- readDir: (dirPath) => ipcRenderer.invoke('bricks-automation-screenshots:readdir', dirPath),
10
- mkdir: (dirPath) => ipcRenderer.invoke('bricks-automation-screenshots:mkdir', dirPath),
11
- unlink: (filePath) => ipcRenderer.invoke('bricks-automation-screenshots:unlink', filePath),
12
- readFile: (filePath, encoding) =>
13
- ipcRenderer.invoke('bricks-automation-screenshots:read-file', filePath, encoding),
14
- writeFile: (filePath, contents, encoding) =>
15
- ipcRenderer.invoke('bricks-automation-screenshots:write-file', filePath, contents, encoding),
16
- })
@@ -1,116 +0,0 @@
1
- export type Easing =
2
- | ''
3
- | 'easeInSine'
4
- | 'easeOutSine'
5
- | 'easeInOutSine'
6
- | 'easeInQuad'
7
- | 'easeOutQuad'
8
- | 'easeInOutQuad'
9
- | 'easeInCubic'
10
- | 'easeOutCubic'
11
- | 'easeInOutCubic'
12
- | 'easeInQuart'
13
- | 'easeOutQuart'
14
- | 'easeInOutQuart'
15
- | 'easeInQuint'
16
- | 'easeOutQuint'
17
- | 'easeInOutQuint'
18
- | 'easeInExpo'
19
- | 'easeOutExpo'
20
- | 'easeInOutExpo'
21
- | 'easeInCirc'
22
- | 'easeOutCirc'
23
- | 'easeInOutCirc'
24
- | 'easeInBack'
25
- | 'easeOutBack'
26
- | 'easeInOutBack'
27
- | 'easeInElastic'
28
- | 'easeOutElastic'
29
- | 'easeInOutElastic'
30
- | 'easeInBounce'
31
- | 'easeOutBounce'
32
- | 'easeInOutBounce'
33
- | string // Support format like 'cubic-bezier(x1, y1, x2, y2)'
34
-
35
- export interface AnimationTimingConfig {
36
- __type: 'AnimationTimingConfig'
37
- toValue: number // BRICKS Grid unit
38
- duration: number // ms
39
- easing: Easing
40
- delay: number // ms
41
- isInteraction: boolean
42
- }
43
-
44
- export interface AnimationSpringConfig {
45
- __type: 'AnimationSpringConfig'
46
- toValue: number // BRICKS Grid unit
47
- // Use one spring parameter family: tension/friction, speed/bounciness,
48
- // or stiffness/damping/mass.
49
- friction?: number
50
- tension?: number
51
- speed?: number
52
- bounciness?: number
53
- stiffness?: number
54
- damping?: number
55
- mass?: number
56
- velocity?: number
57
- delay?: number
58
- isInteraction?: boolean
59
- overshootClamping?: boolean
60
- restDisplacementThreshold?: number
61
- restSpeedThreshold?: number
62
- }
63
-
64
- export interface AnimationDecayConfig {
65
- __type: 'AnimationDecayConfig'
66
- toValue: number // BRICKS Grid unit
67
- velocity: number
68
- deceleration: number
69
- isInteraction: boolean
70
- }
71
-
72
- export interface AnimationDef {
73
- __typename: 'Animation'
74
- id: string
75
- alias?: string
76
- title?: string
77
- description?: string
78
- hideShortRef?: boolean
79
- runType?: 'once' | 'loop'
80
- property:
81
- | 'transform.translateX'
82
- | 'transform.translateY'
83
- | 'transform.scale'
84
- | 'transform.scaleX'
85
- | 'transform.scaleY'
86
- | 'transform.rotate'
87
- | 'transform.rotateX'
88
- | 'transform.rotateY'
89
- | 'opacity'
90
- config: AnimationTimingConfig | AnimationSpringConfig | AnimationDecayConfig
91
- }
92
-
93
- export interface AnimationComposeDef {
94
- __typename: 'AnimationCompose'
95
- id: string
96
- alias?: string
97
- title: string
98
- description?: string
99
- hideShortRef?: boolean
100
- runType?: 'once' | 'loop'
101
- composeType: 'parallel' | 'sequence'
102
- items: Array<() => Animation>
103
- }
104
-
105
- export type Animation = AnimationDef | AnimationComposeDef
106
-
107
- // Animation event handlers accept either a direct Animation or a getter that
108
- // returns one. The getter form is useful for lazy/forward references between
109
- // animations defined across files.
110
- export type AnimationOrGetter = Animation | (() => Animation)
111
-
112
- export interface AnimationBasicEvents {
113
- showStart?: AnimationOrGetter
114
- standby?: AnimationOrGetter
115
- breatheStart?: AnimationOrGetter
116
- }