@fugood/bricks-project 2.24.0-beta.37 → 2.24.0-beta.39

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.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fugood/bricks-project",
3
- "version": "2.24.0-beta.37",
3
+ "version": "2.24.0-beta.39",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "typecheck": "tsc --noEmit",
7
7
  "build": "bun scripts/build.js"
8
8
  },
9
9
  "dependencies": {
10
- "@fugood/bricks-cli": "^2.24.0-beta.35",
10
+ "@fugood/bricks-cli": "^2.24.0-beta.39",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -24,5 +24,5 @@
24
24
  "peerDependencies": {
25
25
  "oxfmt": "^0.36.0"
26
26
  },
27
- "gitHead": "5b91fb31b3ef42efe74e15f8c2e04973905b6cba"
27
+ "gitHead": "f74322101339ff5efb796a89fcd8f0a52c2465ce"
28
28
  }
@@ -2,6 +2,7 @@
2
2
  import { app, BrowserWindow } from 'electron'
3
3
  import { readFile, writeFile } from 'fs/promises'
4
4
  import { watchFile } from 'fs'
5
+ import { createServer } from 'http'
5
6
  import { parseArgs } from 'util'
6
7
  import * as TOON from '@toon-format/toon'
7
8
 
@@ -14,6 +15,7 @@ const { values } = parseArgs({
14
15
  'test-id': { type: 'string' },
15
16
  'test-title-like': { type: 'string' },
16
17
  'no-keep-open': { type: 'boolean' },
18
+ 'cdp-port': { type: 'string' },
17
19
  },
18
20
  strict: true,
19
21
  allowPositionals: true,
@@ -70,6 +72,127 @@ const previewUrlMap = {
70
72
  const previewUrl = previewUrlMap[stage]
71
73
  if (!previewUrl) throw new Error(`Invalid BRICKS_STAGE: ${stage}`)
72
74
 
75
+ // --- CDP WebSocket Server ---
76
+ // Bridges external CDP clients to the preview's postMessage-based CDP bridge.
77
+ // Usage: --cdp-port 9222
78
+
79
+ const startCdpServer = async (mainWindow, port) => {
80
+ const { WebSocketServer } = await import('ws')
81
+ const clients = new Set()
82
+
83
+ // Inject a listener in the preview that forwards CDP responses/events via console
84
+ const injectCdpRelay = () =>
85
+ mainWindow.webContents.executeJavaScript(`
86
+ if (!window.__cdpRelayInstalled) {
87
+ window.__cdpRelayInstalled = true
88
+ window.addEventListener('message', (evt) => {
89
+ try {
90
+ const d = typeof evt.data === 'string' ? JSON.parse(evt.data) : evt.data
91
+ if (d && (d.type === 'cdp-response' || d.type === 'cdp-event'))
92
+ console.log('[CDP]' + JSON.stringify(d))
93
+ } catch {}
94
+ })
95
+ }
96
+ `)
97
+
98
+ // Capture CDP messages from the preview's console output
99
+ mainWindow.webContents.on('console-message', (_event, ...rest) => {
100
+ const message = typeof rest[0] === 'object' ? rest[0].message : rest[1]
101
+ if (!message?.startsWith('[CDP]')) return
102
+ try {
103
+ const data = JSON.parse(message.slice(5))
104
+ // Translate to standard CDP wire format (strip internal type field)
105
+ const wireMsg =
106
+ data.type === 'cdp-response'
107
+ ? { id: data.id, result: data.result, error: data.error }
108
+ : { method: data.method, params: data.params }
109
+ if (data.sessionId) wireMsg.sessionId = data.sessionId
110
+ const payload = JSON.stringify(wireMsg)
111
+ for (const c of clients) if (c.readyState === 1) c.send(payload)
112
+ } catch {}
113
+ })
114
+
115
+ // HTTP discovery endpoints (chrome://inspect, Playwright, etc.)
116
+ const httpServer = createServer((req, res) => {
117
+ if (req.url === '/json/list' || req.url === '/json') {
118
+ res.writeHead(200, { 'Content-Type': 'application/json' })
119
+ res.end(
120
+ JSON.stringify([
121
+ {
122
+ description: 'BRICKS Preview',
123
+ id: 'bricks-preview',
124
+ title: 'BRICKS Preview',
125
+ type: 'page',
126
+ url: previewUrl,
127
+ webSocketDebuggerUrl: `ws://localhost:${port}/ws`,
128
+ },
129
+ ]),
130
+ )
131
+ return
132
+ }
133
+ if (req.url === '/json/version') {
134
+ res.writeHead(200, { 'Content-Type': 'application/json' })
135
+ res.end(JSON.stringify({ Browser: 'BRICKS Preview', 'Protocol-Version': '1.3' }))
136
+ return
137
+ }
138
+ // bricks-cli discovery endpoint
139
+ if (req.url === '/devtools/info') {
140
+ res.writeHead(200, { 'Content-Type': 'application/json' })
141
+ res.end(
142
+ JSON.stringify({
143
+ name: 'BRICKS Preview',
144
+ port,
145
+ protocols: ['cdp'],
146
+ hasPasscode: false,
147
+ }),
148
+ )
149
+ return
150
+ }
151
+ res.writeHead(404)
152
+ res.end()
153
+ })
154
+
155
+ const wss = new WebSocketServer({ server: httpServer, path: '/ws' })
156
+ wss.on('connection', (ws) => {
157
+ clients.add(ws)
158
+ injectCdpRelay()
159
+
160
+ ws.on('message', (raw) => {
161
+ try {
162
+ const req = JSON.parse(raw.toString())
163
+ const cdpReq = JSON.stringify({
164
+ type: 'cdp-request',
165
+ id: req.id,
166
+ method: req.method,
167
+ params: req.params || {},
168
+ sessionId: req.sessionId,
169
+ })
170
+ mainWindow.webContents.executeJavaScript(`window.postMessage(${JSON.stringify(cdpReq)})`)
171
+ } catch {}
172
+ })
173
+
174
+ ws.on('close', () => clients.delete(ws))
175
+ })
176
+
177
+ // Try ports starting from the requested one until one is available
178
+ const listen = (p) =>
179
+ new Promise((resolve, reject) => {
180
+ httpServer.once('error', (err) => {
181
+ if (err.code === 'EADDRINUSE' && p < port + 100) {
182
+ resolve(listen(p + 1))
183
+ } else {
184
+ reject(err)
185
+ }
186
+ })
187
+ httpServer.listen(p, () => {
188
+ console.log(`CDP server: ws://localhost:${p}/ws`)
189
+ console.log(`Inspect: devtools://devtools/bundled/inspector.html?ws=localhost:${p}/ws`)
190
+ resolve(p)
191
+ })
192
+ })
193
+ await listen(port)
194
+ }
195
+
73
196
  app.on('ready', () => {
74
197
  let show = true
75
198
  if (takeScreenshotConfig && !takeScreenshotConfig.noHeadless) show = false
@@ -124,12 +247,16 @@ app.on('ready', () => {
124
247
  })
125
248
  `)
126
249
  }
250
+ // Start CDP WebSocket server if requested
251
+ const cdpPort = values['cdp-port'] ? parseInt(values['cdp-port'], 10) : null
252
+ if (cdpPort) startCdpServer(mainWindow, cdpPort)
127
253
  })
128
254
 
129
255
  // Capture console messages from the preview
130
256
  if (testId) {
131
- mainWindow.webContents.on('console-message', (_, { message }) => {
132
- if (message.startsWith('[TEST_RESULT]')) {
257
+ mainWindow.webContents.on('console-message', (_event, ...rest) => {
258
+ const message = typeof rest[0] === 'object' ? rest[0].message : rest[1]
259
+ if (message?.startsWith('[TEST_RESULT]')) {
133
260
  const data = JSON.parse(message.replace('[TEST_RESULT]', ''))
134
261
  console.log(`[TEST_RESULT_TOON]${TOON.encode(data.result)}`)
135
262
  if (!takeScreenshotConfig && noKeepOpen) app.quit()
package/tools/preview.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { $ } from 'bun'
2
- import { watch } from 'fs'
2
+ import { watch, unlinkSync } from 'fs'
3
3
  import type { FSWatcher } from 'fs'
4
4
  import { parseArgs } from 'util'
5
5
  import debounce from 'lodash/debounce'
@@ -19,6 +19,8 @@ const { values } = parseArgs({
19
19
  'test-id': { type: 'string' },
20
20
  'test-title-like': { type: 'string' },
21
21
  'no-keep-open': { type: 'boolean' },
22
+ 'cdp-port': { type: 'string' },
23
+ 'no-cdp': { type: 'boolean' },
22
24
  },
23
25
  strict: true,
24
26
  allowPositionals: true,
@@ -62,6 +64,11 @@ if (values['no-keep-open']) {
62
64
  args.push('--no-keep-open')
63
65
  }
64
66
 
67
+ if (!values['no-cdp']) {
68
+ const cdpPort = parseInt(values['cdp-port'] || '', 10) || 19852
69
+ args.push('--cdp-port', String(cdpPort))
70
+ }
71
+
65
72
  const useTypecheck = !values['skip-typecheck']
66
73
 
67
74
  const compile = async () => {
@@ -80,6 +87,14 @@ if (needWatcher) {
80
87
  })
81
88
  }
82
89
 
90
+ const devtoolsInfoPath = `${cwd}/.bricks/devtools.json`
91
+
92
+ const cleanupDevtoolsInfo = () => {
93
+ try {
94
+ unlinkSync(devtoolsInfoPath)
95
+ } catch {}
96
+ }
97
+
83
98
  const proc = Bun.spawn(['bunx', '--bun', 'electron', `${__dirname}/preview-main.mjs`, ...args], {
84
99
  env: { ...process.env, BRICKS_STAGE: app.stage || 'production' },
85
100
  stdout: 'pipe',
@@ -102,6 +117,18 @@ const processOutput = async () => {
102
117
  const toonData = line.replace('[TEST_RESULT_TOON]', '')
103
118
  console.log(toonData)
104
119
  } else if (line) {
120
+ // Detect CDP server startup from preview-main output
121
+ const cdpMatch = line.match(/^CDP server: ws:\/\/localhost:(\d+)/)
122
+ if (cdpMatch) {
123
+ const info = {
124
+ port: parseInt(cdpMatch[1], 10),
125
+ pid: proc.pid,
126
+ address: 'localhost',
127
+ name: app.name || 'BRICKS Preview',
128
+ startedAt: new Date().toISOString(),
129
+ }
130
+ Bun.write(devtoolsInfoPath, JSON.stringify(info, null, 2))
131
+ }
105
132
  console.log(line)
106
133
  }
107
134
  })
@@ -112,4 +139,5 @@ const processOutput = async () => {
112
139
  await processOutput()
113
140
  await proc.exited
114
141
 
142
+ cleanupDevtoolsInfo()
115
143
  if (watcher) watcher.close()
@@ -1,6 +1,6 @@
1
1
  /* Auto generated by build script
2
2
  *
3
- * Rectangle shape with customizable fill, border, radius, and shadow
3
+ * Rectangle shape with customizable fill, border, radius, shadow, and blur
4
4
  */
5
5
  import type { SwitchCondInnerStateCurrentCanvas, SwitchCondData, SwitchDef } from '../switch'
6
6
  import type { Data, DataLink } from '../data'
@@ -37,7 +37,16 @@ Default property:
37
37
  "linearGradientLocations": [
38
38
  0,
39
39
  1
40
- ]
40
+ ],
41
+ "blurEnabled": false,
42
+ "blurVariant": "blur",
43
+ "blurType": "light",
44
+ "blurAmount": 10,
45
+ "blurDirection": "blurredTopClearBottom",
46
+ "blurStartOffset": 0,
47
+ "blurGlassType": "regular",
48
+ "blurGlassOpacity": 0.5,
49
+ "blurGlassInteractive": false
41
50
  }
42
51
  */
43
52
  property?: BrickBasicProperty & {
@@ -61,6 +70,32 @@ Default property:
61
70
  }
62
71
  /* An optional array of numbers defining the location of each gradient color stop */
63
72
  linearGradientLocations?: Array<number | DataLink> | DataLink
73
+ /* Enable blur effect.
74
+ Not supported on Desktop (Electron) and Web.
75
+ Avoid using opacity prop with blur — use alpha backgroundColor (e.g. rgba) instead. */
76
+ blurEnabled?: boolean | DataLink
77
+ /* Blur variant */
78
+ blurVariant?: 'blur' | 'progressiveBlur' | 'liquidGlass' | DataLink
79
+ /* Blur type (blur variant) */
80
+ blurType?: 'xlight' | 'light' | 'dark' | 'regular' | 'prominent' | DataLink
81
+ /* Blur amount (0 ~ 100, blur variant) */
82
+ blurAmount?: number | DataLink
83
+ /* Progressive blur direction (progressiveBlur variant) */
84
+ blurDirection?:
85
+ | 'blurredTopClearBottom'
86
+ | 'blurredBottomClearTop'
87
+ | 'blurredCenterClearTopAndBottom'
88
+ | DataLink
89
+ /* Progressive blur start offset (0 ~ 1, progressiveBlur variant) */
90
+ blurStartOffset?: number | DataLink
91
+ /* Glass type (liquidGlass variant, iOS 26+) */
92
+ blurGlassType?: 'clear' | 'regular' | DataLink
93
+ /* Glass tint color (liquidGlass variant) */
94
+ blurGlassTintColor?: string | DataLink
95
+ /* Glass opacity (0 ~ 1, liquidGlass variant) */
96
+ blurGlassOpacity?: number | DataLink
97
+ /* Enable glass interaction (liquidGlass variant) */
98
+ blurGlassInteractive?: boolean | DataLink
64
99
  }
65
100
  events?: BrickBasicEvents & {
66
101
  /* Event of the brick press */
@@ -92,7 +127,7 @@ Default property:
92
127
  }
93
128
  }
94
129
 
95
- /* Rectangle shape with customizable fill, border, radius, and shadow */
130
+ /* Rectangle shape with customizable fill, border, radius, shadow, and blur */
96
131
  export type BrickRect = Brick &
97
132
  BrickRectDef & {
98
133
  templateKey: 'BRICK_RECT'