@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 +3 -3
- package/tools/preview-main.mjs +129 -2
- package/tools/preview.ts +29 -1
- package/types/bricks/Rect.ts +38 -3
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-project",
|
|
3
|
-
"version": "2.24.0-beta.
|
|
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.
|
|
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": "
|
|
27
|
+
"gitHead": "f74322101339ff5efb796a89fcd8f0a52c2465ce"
|
|
28
28
|
}
|
package/tools/preview-main.mjs
CHANGED
|
@@ -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', (
|
|
132
|
-
|
|
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()
|
package/types/bricks/Rect.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Auto generated by build script
|
|
2
2
|
*
|
|
3
|
-
* Rectangle shape with customizable fill, border, radius, and
|
|
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
|
|
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'
|