@fugood/bricks-project 2.25.0-beta.39 → 2.25.0-beta.40

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.25.0-beta.39",
3
+ "version": "2.25.0-beta.40",
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.25.0-beta.38",
10
+ "@fugood/bricks-cli": "^2.25.0-beta.40",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -24,5 +24,6 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "oxfmt": "^0.36.0"
27
- }
27
+ },
28
+ "gitHead": "4ad8588eed6f0161b0aa66d863a36e6c623c4a83"
28
29
  }
package/package.json.bak CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fugood/bricks-ctor",
3
- "version": "2.25.0-beta.39",
3
+ "version": "2.25.0-beta.40",
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.25.0-beta.38",
10
+ "@fugood/bricks-cli": "^2.25.0-beta.40",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -24,5 +24,6 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "oxfmt": "^0.36.0"
27
- }
27
+ },
28
+ "gitHead": "4ad8588eed6f0161b0aa66d863a36e6c623c4a83"
28
29
  }
@@ -9,6 +9,23 @@ What the Simulator can and can't faithfully reproduce — so a passing Simulator
9
9
 
10
10
  A Simulator screenshot proves **layout, navigation, Standby Transitions, Data wiring, and generator/Automation wiring**. It does *not* prove anything that depends on real hardware, real models, or capabilities the preview runtime lacks.
11
11
 
12
+ ## Automation screenshots
13
+
14
+ `match_screenshot` stores Simulator automation artifacts at the project level:
15
+
16
+ ```text
17
+ .bricks/automation_screenshots/
18
+ last/
19
+ failed/
20
+ diff/
21
+ ```
22
+
23
+ There is no application-id subdirectory; in CTOR the project is the application. Generated project `.gitignore` files exclude this directory.
24
+
25
+ When a `match_screenshot` baseline is missing or updated, the baseline goes under `last/`. When a screenshot mismatch occurs, the current capture goes under `failed/` and the pixel diff goes under `diff/`. File names follow `<screenshotName>+local+<timestamp>.png`.
26
+
27
+ In CTOR Desktop, `simulator_invoke` reports any automation screenshot files created or changed by the invocation in an `Automation screenshots:` section. It still separately reports the ordinary preview capture at `.bricks/simulator-screenshot.jpg`.
28
+
12
29
  ## What the Simulator can't reproduce
13
30
 
14
31
  Because it runs in a browser-class preview rather than on device hardware, the Simulator cannot exercise these — a green run says nothing about them. Verify on a real device (Path 2):
@@ -1,8 +1,10 @@
1
1
  // eslint-disable-next-line import/no-extraneous-dependencies
2
- import { app, BrowserWindow } from 'electron'
3
- import { readFile, writeFile } from 'fs/promises'
2
+ import { app, BrowserWindow, ipcMain } from 'electron'
3
+ import { mkdir, readdir, readFile, rm, writeFile } from 'fs/promises'
4
4
  import { watchFile } from 'fs'
5
5
  import { createServer } from 'http'
6
+ import path from 'node:path'
7
+ import { fileURLToPath } from 'node:url'
6
8
  import { parseArgs } from 'util'
7
9
  import * as TOON from '@toon-format/toon'
8
10
 
@@ -28,12 +30,70 @@ const { values } = parseArgs({
28
30
  })
29
31
 
30
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')
31
35
  const DEFAULT_SIMULATOR_SECURITY_SETTINGS = Object.freeze({
32
36
  allowedInsecureHosts: [],
33
37
  })
34
38
 
35
39
  const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value)
36
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
+
37
97
  const normalizeSimulatorSecuritySettings = (settings = {}) => {
38
98
  const source = isObject(settings) ? settings : {}
39
99
  const allowedHosts = Array.isArray(source.allowedInsecureHosts)
@@ -289,6 +349,7 @@ const startCdpServer = async (mainWindow, port) => {
289
349
  }
290
350
 
291
351
  app.on('ready', () => {
352
+ registerAutomationScreenshotIpc()
292
353
  let show = true
293
354
  if (takeScreenshotConfig && !takeScreenshotConfig.noHeadless) show = false
294
355
  const mainWindow = new BrowserWindow({
@@ -296,6 +357,11 @@ app.on('ready', () => {
296
357
  height: takeScreenshotConfig?.height || 768,
297
358
  frame: !takeScreenshotConfig,
298
359
  show,
360
+ webPreferences: {
361
+ preload: path.join(__dirname, 'simulator-preload.cjs'),
362
+ contextIsolation: true,
363
+ nodeIntegration: false,
364
+ },
299
365
  })
300
366
  mainWindow.setBackgroundColor('#333')
301
367
  mainWindow.webContents.session.webRequest.onBeforeRequest(
@@ -0,0 +1,16 @@
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
+ })