@fugood/bricks-project 2.25.0-beta.4 → 2.25.0-beta.41
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/compile/action-name-map.ts +64 -0
- package/compile/index.ts +208 -19
- package/package.json +4 -3
- package/package.json.bak +4 -3
- package/skills/bricks-ctor/SKILL.md +21 -17
- package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
- package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +12 -0
- package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
- package/skills/bricks-ctor/references/buttress.md +245 -0
- package/skills/bricks-ctor/references/data-calculation.md +239 -0
- package/skills/bricks-ctor/references/simulator.md +131 -0
- package/skills/bricks-ctor/references/verification-toolchain.md +179 -0
- package/skills/bricks-design/SKILL.md +160 -45
- package/skills/bricks-design/references/architecture-truths.md +132 -0
- package/skills/bricks-design/references/avoiding-complexity.md +91 -0
- package/skills/bricks-design/references/design-critique.md +195 -0
- package/skills/bricks-design/references/design-languages.md +265 -0
- package/skills/bricks-design/references/performance.md +116 -0
- package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
- package/skills/bricks-design/references/translating-inputs.md +152 -0
- package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
- package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
- package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
- package/skills/bricks-design/references/workflow.md +134 -0
- package/skills/bricks-ux/SKILL.md +120 -0
- package/skills/bricks-ux/references/accessibility.md +162 -0
- package/skills/bricks-ux/references/flow-states.md +175 -0
- package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
- package/skills/bricks-ux/references/monitoring-screens.md +153 -0
- package/skills/bricks-ux/references/pressable-composition.md +126 -0
- package/skills/bricks-ux/references/user-journey.md +168 -0
- package/skills/bricks-ux/references/ux-critique.md +256 -0
- package/tools/_git-author.ts +10 -2
- package/tools/_last-pushed-commit.ts +28 -0
- package/tools/_shell.ts +8 -1
- package/tools/deploy.ts +15 -0
- package/tools/mcp-tools/compile.ts +19 -9
- package/tools/mcp-tools/media.ts +4 -1
- package/tools/pull.ts +91 -16
- package/tools/push-config.ts +118 -0
- package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
- package/tools/simulator-preload.cjs +16 -0
- package/tools/{preview.ts → simulator.ts} +4 -4
- package/types/{animation.ts → animation.d.ts} +24 -8
- package/types/{automation.ts → automation.d.ts} +16 -20
- package/types/{brick-base.ts → brick-base.d.ts} +1 -1
- package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
- package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
- package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
- package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
- package/types/bricks/{Image.ts → Image.d.ts} +21 -9
- package/types/bricks/{Items.ts → Items.d.ts} +7 -7
- package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
- package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
- package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
- package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
- package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
- package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
- package/types/bricks/Scene3D.d.ts +676 -0
- package/types/bricks/{Sketch.ts → Sketch.d.ts} +6 -6
- package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
- package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
- package/types/bricks/{Text.ts → Text.d.ts} +9 -9
- package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
- package/types/bricks/{Video.ts → Video.d.ts} +12 -12
- package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
- package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
- package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
- package/types/bricks/{index.ts → index.d.ts} +1 -0
- package/types/{common.ts → common.d.ts} +3 -6
- package/types/data-calc-command/base.d.ts +57 -0
- package/types/data-calc-command/collection.d.ts +418 -0
- package/types/data-calc-command/color.d.ts +432 -0
- package/types/data-calc-command/constant.d.ts +50 -0
- package/types/data-calc-command/datetime.d.ts +147 -0
- package/types/data-calc-command/file.d.ts +129 -0
- package/types/data-calc-command/index.d.ts +13 -0
- package/types/data-calc-command/iteratee.d.ts +23 -0
- package/types/data-calc-command/logictype.d.ts +190 -0
- package/types/data-calc-command/math.d.ts +275 -0
- package/types/data-calc-command/object.d.ts +119 -0
- package/types/data-calc-command/sandbox.d.ts +66 -0
- package/types/data-calc-command/string.d.ts +407 -0
- package/types/{data-calc.ts → data-calc.d.ts} +1 -0
- package/types/{data.ts → data.d.ts} +4 -2
- package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
- package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
- package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
- package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
- package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
- package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
- package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
- package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
- package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
- package/types/{subspace.ts → subspace.d.ts} +1 -1
- package/utils/data.ts +2 -2
- package/utils/event-props.ts +17 -0
- package/utils/id.ts +78 -27
- package/skills/bricks-ctor/rules/buttress.md +0 -156
- package/skills/bricks-ctor/rules/data-calculation.md +0 -209
- package/skills/bricks-design/LICENSE.txt +0 -180
- package/types/data-calc-command.ts +0 -7005
- /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/media-flow.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
- /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
- /package/types/{canvas.ts → canvas.d.ts} +0 -0
- /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
- /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
- /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
- /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
- /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
- /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
- /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
- /package/types/generators/{File.ts → File.d.ts} +0 -0
- /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
- /package/types/generators/{Http.ts → Http.d.ts} +0 -0
- /package/types/generators/{HttpServer.ts → HttpServer.d.ts} +0 -0
- /package/types/generators/{Information.ts → Information.d.ts} +0 -0
- /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
- /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
- /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
- /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
- /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
- /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
- /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
- /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
- /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
- /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
- /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
- /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
- /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
- /package/types/generators/{Question.ts → Question.d.ts} +0 -0
- /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
- /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
- /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
- /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
- /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
- /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
- /package/types/generators/{Step.ts → Step.d.ts} +0 -0
- /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
- /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
- /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
- /package/types/generators/{Tick.ts → Tick.d.ts} +0 -0
- /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
- /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
- /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
- /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
- /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
- /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
- /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
- /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
- /package/types/generators/{index.ts → index.d.ts} +0 -0
- /package/types/{index.ts → index.d.ts} +0 -0
- /package/types/{switch.ts → switch.d.ts} +0 -0
- /package/types/{system.ts → system.d.ts} +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { parseArgs } from 'util'
|
|
3
|
+
import { sh } from './_shell'
|
|
4
|
+
import { buildCommitArgs } from './_git-author'
|
|
5
|
+
import { writeLastPushedCommit } from './_last-pushed-commit'
|
|
6
|
+
|
|
7
|
+
const cwd = process.cwd()
|
|
8
|
+
|
|
9
|
+
const readJson = async (p: string) => JSON.parse(await readFile(p, 'utf8'))
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
values: { 'auto-commit': autoCommit, 'no-check': noCheck, 'no-validate': noValidate, yes, help },
|
|
13
|
+
} = parseArgs({
|
|
14
|
+
args: process.argv.slice(2),
|
|
15
|
+
options: {
|
|
16
|
+
'auto-commit': { type: 'boolean' },
|
|
17
|
+
'no-check': { type: 'boolean' },
|
|
18
|
+
'no-validate': { type: 'boolean' },
|
|
19
|
+
yes: { type: 'boolean', short: 'y' },
|
|
20
|
+
help: { type: 'boolean', short: 'h' },
|
|
21
|
+
},
|
|
22
|
+
allowPositionals: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
if (help) {
|
|
26
|
+
console.log(`Push compiled config to BRICKS without creating a release.
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--auto-commit Auto-commit unstaged changes before pushing
|
|
30
|
+
--no-check Skip the conflict guard (don't pass --last-commit-id)
|
|
31
|
+
--no-validate Skip server-side config schema validation
|
|
32
|
+
-y, --yes Skip all prompts
|
|
33
|
+
-h, --help Show this help message`)
|
|
34
|
+
process.exit(0)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Detect git repo (mirrors deploy.ts)
|
|
38
|
+
const { exitCode } = await sh`cd ${cwd} && git status`.quiet().nothrow()
|
|
39
|
+
const isGitRepo = exitCode === 0
|
|
40
|
+
|
|
41
|
+
if (!isGitRepo && !yes) {
|
|
42
|
+
const confirmContinue = prompt('No git repository found, continue? (y/n)')
|
|
43
|
+
if (confirmContinue !== 'y') throw new Error('Update cancelled')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Read application.json + compiled config
|
|
47
|
+
const app = await readJson(`${cwd}/application.json`)
|
|
48
|
+
const config = await readJson(`${cwd}/.bricks/build/application-config.json`)
|
|
49
|
+
|
|
50
|
+
// Handle unstaged changes the same way deploy.ts does.
|
|
51
|
+
let commitId = ''
|
|
52
|
+
let parentCommitId = ''
|
|
53
|
+
if (isGitRepo) {
|
|
54
|
+
const unstagedChanges = await sh`cd ${cwd} && git diff --name-only --diff-filter=ACMR`.text()
|
|
55
|
+
if (unstagedChanges) {
|
|
56
|
+
if (autoCommit) {
|
|
57
|
+
// Capture the pre-commit HEAD so we can use it as the conflict-check
|
|
58
|
+
// baseline (the server should still hold it from the prior deploy).
|
|
59
|
+
parentCommitId = (await sh`cd ${cwd} && git rev-parse HEAD`.nothrow().text()).trim()
|
|
60
|
+
await sh`cd ${cwd} && git add -A`
|
|
61
|
+
const commitArgs = await buildCommitArgs(cwd, ['chore: update bricks config'])
|
|
62
|
+
await sh`cd ${cwd} && git ${commitArgs}`
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error('Unstaged changes found, please commit or stash your changes before updating')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
commitId = (await sh`cd ${cwd} && git rev-parse HEAD`.text()).trim()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Auto-derive --last-commit-id for the server-side conflict guard.
|
|
71
|
+
// - parent of the auto-commit (server still holds it from the prior deploy)
|
|
72
|
+
// - current HEAD (clean tree — server should be at the same commit too)
|
|
73
|
+
let lastCommitId: string | undefined
|
|
74
|
+
if (!noCheck) {
|
|
75
|
+
if (parentCommitId) lastCommitId = parentCommitId
|
|
76
|
+
else if (commitId) lastCommitId = commitId
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!yes) {
|
|
80
|
+
const confirm = prompt('Are you sure you want to push the new config? (y/n)')
|
|
81
|
+
if (confirm !== 'y') throw new Error('Update cancelled')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const isModule = app.type === 'module'
|
|
85
|
+
const command = isModule ? 'module' : 'app'
|
|
86
|
+
|
|
87
|
+
const updateConfig = {
|
|
88
|
+
...config,
|
|
89
|
+
bricks_project_last_commit_id: commitId || undefined,
|
|
90
|
+
}
|
|
91
|
+
const configPath = `${cwd}/.bricks/build/push-config.json`
|
|
92
|
+
await writeFile(configPath, JSON.stringify(updateConfig))
|
|
93
|
+
|
|
94
|
+
const args = ['bricks', command, 'update', app.id, '-f', configPath, '--json']
|
|
95
|
+
if (noValidate) args.push('--no-validate')
|
|
96
|
+
if (lastCommitId) args.push('--last-commit-id', lastCommitId)
|
|
97
|
+
|
|
98
|
+
const result = await sh`${args}`.quiet().nothrow()
|
|
99
|
+
|
|
100
|
+
if (result.exitCode !== 0) {
|
|
101
|
+
const output = result.stderr.toString() || result.stdout.toString()
|
|
102
|
+
try {
|
|
103
|
+
const json = JSON.parse(output)
|
|
104
|
+
throw new Error(json.error?.message || json.error || 'Update failed')
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(output || 'Update failed')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const output = JSON.parse(result.stdout.toString())
|
|
111
|
+
|
|
112
|
+
// Record the commit we just pushed from so a later pull can use it as the
|
|
113
|
+
// merge base regardless of what the server stores in
|
|
114
|
+
// bricks_project_last_commit_id (which may be an opaque nanoid for
|
|
115
|
+
// content-only edits).
|
|
116
|
+
if (commitId) await writeLastPushedCommit(cwd, commitId)
|
|
117
|
+
|
|
118
|
+
console.log(`${isModule ? 'Module' : 'App'} config updated: ${output.target?.name || app.name}`)
|
|
@@ -1,11 +1,19 @@
|
|
|
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
|
|
|
11
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
12
|
+
stream.on('error', (err) => {
|
|
13
|
+
if (err.code !== 'EPIPE') throw err
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
const { values } = parseArgs({
|
|
10
18
|
args: process.argv,
|
|
11
19
|
options: {
|
|
@@ -22,6 +30,139 @@ const { values } = parseArgs({
|
|
|
22
30
|
})
|
|
23
31
|
|
|
24
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
|
+
}
|
|
25
166
|
|
|
26
167
|
let takeScreenshotConfig = null
|
|
27
168
|
try {
|
|
@@ -39,6 +180,7 @@ try {
|
|
|
39
180
|
}
|
|
40
181
|
|
|
41
182
|
let config = JSON.parse(await readFile(`${cwd}/.bricks/build/application-config.json`))
|
|
183
|
+
let simulatorSettings = await readSimulatorProjectSettings()
|
|
42
184
|
|
|
43
185
|
// Resolve testId from testTitleLike
|
|
44
186
|
let testId = values['test-id'] || null
|
|
@@ -71,6 +213,13 @@ const previewUrlMap = {
|
|
|
71
213
|
|
|
72
214
|
const previewUrl = previewUrlMap[stage]
|
|
73
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
|
+
})
|
|
74
223
|
|
|
75
224
|
// --- CDP WebSocket Server ---
|
|
76
225
|
// Bridges external CDP clients to the preview's postMessage-based CDP bridge.
|
|
@@ -136,9 +285,9 @@ const startCdpServer = async (mainWindow, port) => {
|
|
|
136
285
|
res.end(
|
|
137
286
|
JSON.stringify([
|
|
138
287
|
{
|
|
139
|
-
description: 'BRICKS
|
|
140
|
-
id: 'bricks-
|
|
141
|
-
title: 'BRICKS
|
|
288
|
+
description: 'BRICKS Simulator',
|
|
289
|
+
id: 'bricks-simulator',
|
|
290
|
+
title: 'BRICKS Simulator',
|
|
142
291
|
type: 'page',
|
|
143
292
|
url: previewUrl,
|
|
144
293
|
webSocketDebuggerUrl: `ws://localhost:${actualPort}/ws`,
|
|
@@ -149,7 +298,7 @@ const startCdpServer = async (mainWindow, port) => {
|
|
|
149
298
|
}
|
|
150
299
|
if (req.url === '/json/version') {
|
|
151
300
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
152
|
-
res.end(JSON.stringify({ Browser: 'BRICKS
|
|
301
|
+
res.end(JSON.stringify({ Browser: 'BRICKS Simulator', 'Protocol-Version': '1.3' }))
|
|
153
302
|
return
|
|
154
303
|
}
|
|
155
304
|
// bricks-cli discovery endpoint
|
|
@@ -157,7 +306,7 @@ const startCdpServer = async (mainWindow, port) => {
|
|
|
157
306
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
158
307
|
res.end(
|
|
159
308
|
JSON.stringify({
|
|
160
|
-
name: 'BRICKS
|
|
309
|
+
name: 'BRICKS Simulator',
|
|
161
310
|
port: actualPort,
|
|
162
311
|
protocols: ['cdp'],
|
|
163
312
|
hasPasscode: false,
|
|
@@ -200,6 +349,7 @@ const startCdpServer = async (mainWindow, port) => {
|
|
|
200
349
|
}
|
|
201
350
|
|
|
202
351
|
app.on('ready', () => {
|
|
352
|
+
registerAutomationScreenshotIpc()
|
|
203
353
|
let show = true
|
|
204
354
|
if (takeScreenshotConfig && !takeScreenshotConfig.noHeadless) show = false
|
|
205
355
|
const mainWindow = new BrowserWindow({
|
|
@@ -207,8 +357,19 @@ app.on('ready', () => {
|
|
|
207
357
|
height: takeScreenshotConfig?.height || 768,
|
|
208
358
|
frame: !takeScreenshotConfig,
|
|
209
359
|
show,
|
|
360
|
+
webPreferences: {
|
|
361
|
+
preload: path.join(__dirname, 'simulator-preload.cjs'),
|
|
362
|
+
contextIsolation: true,
|
|
363
|
+
nodeIntegration: false,
|
|
364
|
+
},
|
|
210
365
|
})
|
|
211
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
|
+
)
|
|
212
373
|
mainWindow.loadURL(previewUrl)
|
|
213
374
|
|
|
214
375
|
const sendConfig = () => {
|
|
@@ -224,16 +385,23 @@ app.on('ready', () => {
|
|
|
224
385
|
)
|
|
225
386
|
if (takeScreenshotConfig) {
|
|
226
387
|
const { delay, width, height, path } = takeScreenshotConfig
|
|
227
|
-
setTimeout(() => {
|
|
388
|
+
setTimeout(async () => {
|
|
389
|
+
let screenshotFailed = false
|
|
228
390
|
console.log('Taking screenshot')
|
|
229
|
-
|
|
391
|
+
try {
|
|
392
|
+
const image = await mainWindow.webContents.capturePage()
|
|
230
393
|
console.log('Writing screenshot to', path)
|
|
231
|
-
writeFile(path, image.resize({ width, height }).toJPEG(75))
|
|
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 {
|
|
232
399
|
if (noKeepOpen) {
|
|
233
400
|
console.log('Closing app')
|
|
234
|
-
app.
|
|
401
|
+
if (screenshotFailed) app.exit(1)
|
|
402
|
+
else app.quit()
|
|
235
403
|
}
|
|
236
|
-
}
|
|
404
|
+
}
|
|
237
405
|
}, delay)
|
|
238
406
|
}
|
|
239
407
|
}
|
|
@@ -287,7 +455,34 @@ app.on('ready', () => {
|
|
|
287
455
|
async () => {
|
|
288
456
|
console.log('Detected config changed')
|
|
289
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
|
+
})
|
|
290
466
|
sendConfig()
|
|
291
467
|
},
|
|
292
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
|
+
)
|
|
293
488
|
})
|
|
@@ -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
|
+
})
|
|
@@ -98,7 +98,7 @@ const cleanupDevtoolsInfo = () => {
|
|
|
98
98
|
} catch {}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
// Kill existing
|
|
101
|
+
// Kill existing simulator process if devtools.json contains a stale pid
|
|
102
102
|
try {
|
|
103
103
|
const devtoolsInfo = JSON.parse(await readFile(devtoolsInfoPath, 'utf8'))
|
|
104
104
|
if (devtoolsInfo.pid) {
|
|
@@ -111,7 +111,7 @@ try {
|
|
|
111
111
|
|
|
112
112
|
const proc = spawn(
|
|
113
113
|
'bunx',
|
|
114
|
-
['--bun', 'electron', `${import.meta.dirname}/
|
|
114
|
+
['--bun', 'electron', `${import.meta.dirname}/simulator-main.mjs`, ...args],
|
|
115
115
|
{
|
|
116
116
|
env: { ...process.env, BRICKS_STAGE: app.stage || 'production' },
|
|
117
117
|
stdio: ['inherit', 'pipe', 'inherit'],
|
|
@@ -127,14 +127,14 @@ rl.on('line', (line) => {
|
|
|
127
127
|
return
|
|
128
128
|
}
|
|
129
129
|
if (!line) return
|
|
130
|
-
// Detect CDP server startup from
|
|
130
|
+
// Detect CDP server startup from simulator-main output
|
|
131
131
|
const cdpMatch = line.match(/^CDP server: ws:\/\/localhost:(\d+)/)
|
|
132
132
|
if (cdpMatch) {
|
|
133
133
|
const info = {
|
|
134
134
|
port: parseInt(cdpMatch[1], 10),
|
|
135
135
|
pid: proc.pid,
|
|
136
136
|
address: 'localhost',
|
|
137
|
-
name: `${app.name || 'Unnamed'} (CTOR
|
|
137
|
+
name: `${app.name || 'Unnamed'} (CTOR Simulator)`,
|
|
138
138
|
startedAt: new Date().toISOString(),
|
|
139
139
|
}
|
|
140
140
|
writeFile(devtoolsInfoPath, JSON.stringify(info, null, 2))
|
|
@@ -44,10 +44,21 @@ export interface AnimationTimingConfig {
|
|
|
44
44
|
export interface AnimationSpringConfig {
|
|
45
45
|
__type: 'AnimationSpringConfig'
|
|
46
46
|
toValue: number // BRICKS Grid unit
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
51
62
|
}
|
|
52
63
|
|
|
53
64
|
export interface AnimationDecayConfig {
|
|
@@ -62,7 +73,7 @@ export interface AnimationDef {
|
|
|
62
73
|
__typename: 'Animation'
|
|
63
74
|
id: string
|
|
64
75
|
alias?: string
|
|
65
|
-
title
|
|
76
|
+
title?: string
|
|
66
77
|
description?: string
|
|
67
78
|
hideShortRef?: boolean
|
|
68
79
|
runType?: 'once' | 'loop'
|
|
@@ -93,8 +104,13 @@ export interface AnimationComposeDef {
|
|
|
93
104
|
|
|
94
105
|
export type Animation = AnimationDef | AnimationComposeDef
|
|
95
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
|
+
|
|
96
112
|
export interface AnimationBasicEvents {
|
|
97
|
-
showStart?:
|
|
98
|
-
standby?:
|
|
99
|
-
breatheStart?:
|
|
113
|
+
showStart?: AnimationOrGetter
|
|
114
|
+
standby?: AnimationOrGetter
|
|
115
|
+
breatheStart?: AnimationOrGetter
|
|
100
116
|
}
|
|
@@ -83,9 +83,22 @@ export type TestMethodName =
|
|
|
83
83
|
| 'delay'
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
87
|
-
* Each method has a specific signature: [methodName, ...args]
|
|
86
|
+
* Union type for all test method run arrays
|
|
88
87
|
*/
|
|
88
|
+
export type TestMethodRun =
|
|
89
|
+
| TestMethodRunBrickPress
|
|
90
|
+
| TestMethodRunBrickExists
|
|
91
|
+
| TestMethodRunWaitUntilBrickExists
|
|
92
|
+
| TestMethodRunWaitUntilEventTrigger
|
|
93
|
+
| TestMethodRunWaitUntilCanvasChange
|
|
94
|
+
| TestMethodRunKeydown
|
|
95
|
+
| TestMethodRunKeyup
|
|
96
|
+
| TestMethodRunHttpRequest
|
|
97
|
+
| TestMethodRunAssertProperty
|
|
98
|
+
| TestMethodRunWaitUntilPropertyChange
|
|
99
|
+
| TestMethodRunExecuteAction
|
|
100
|
+
| TestMethodRunMatchScreenshot
|
|
101
|
+
| TestMethodRunDelay
|
|
89
102
|
|
|
90
103
|
// [methodName, subspaceId, brickId, options?]
|
|
91
104
|
export type TestMethodRunBrickPress = ['brick_press', SubspaceRef, BrickRef, Record<string, any>?]
|
|
@@ -157,24 +170,6 @@ export type TestMethodRunMatchScreenshot = ['match_screenshot', string, number?,
|
|
|
157
170
|
// [methodName, subspaceId?, propertyId?, defaultValue?]
|
|
158
171
|
export type TestMethodRunDelay = ['delay', SubspaceRef?, DataRef?, number?]
|
|
159
172
|
|
|
160
|
-
/**
|
|
161
|
-
* Union type for all test method run arrays
|
|
162
|
-
*/
|
|
163
|
-
export type TestMethodRun =
|
|
164
|
-
| TestMethodRunBrickPress
|
|
165
|
-
| TestMethodRunBrickExists
|
|
166
|
-
| TestMethodRunWaitUntilBrickExists
|
|
167
|
-
| TestMethodRunWaitUntilEventTrigger
|
|
168
|
-
| TestMethodRunWaitUntilCanvasChange
|
|
169
|
-
| TestMethodRunKeydown
|
|
170
|
-
| TestMethodRunKeyup
|
|
171
|
-
| TestMethodRunHttpRequest
|
|
172
|
-
| TestMethodRunAssertProperty
|
|
173
|
-
| TestMethodRunWaitUntilPropertyChange
|
|
174
|
-
| TestMethodRunExecuteAction
|
|
175
|
-
| TestMethodRunMatchScreenshot
|
|
176
|
-
| TestMethodRunDelay
|
|
177
|
-
|
|
178
173
|
/**
|
|
179
174
|
* Test case definition
|
|
180
175
|
*/
|
|
@@ -203,6 +198,7 @@ export type LocalSyncMode = 'main-only' | 'minor-only'
|
|
|
203
198
|
export interface AutomationTest {
|
|
204
199
|
__typename: 'AutomationTest'
|
|
205
200
|
id: string
|
|
201
|
+
alias?: string
|
|
206
202
|
title: string
|
|
207
203
|
hideShortRef?: boolean
|
|
208
204
|
timeout: number
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Auto generated by build script */
|
|
2
2
|
import type { SwitchCondInnerStateCurrentCanvas, SwitchCondData, SwitchDef } from './switch'
|
|
3
3
|
import type { Data, DataLink } from './data'
|
|
4
|
-
import type { Animation, AnimationBasicEvents } from './animation'
|
|
4
|
+
import type { Animation, AnimationBasicEvents, AnimationOrGetter } from './animation'
|
|
5
5
|
import type {
|
|
6
6
|
Brick,
|
|
7
7
|
EventAction,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { SwitchCondInnerStateCurrentCanvas, SwitchCondData, SwitchDef } from '../switch'
|
|
6
6
|
import type { Data, DataLink } from '../data'
|
|
7
|
-
import type { Animation, AnimationBasicEvents } from '../animation'
|
|
7
|
+
import type { Animation, AnimationBasicEvents, AnimationOrGetter } from '../animation'
|
|
8
8
|
import type {
|
|
9
9
|
Brick,
|
|
10
10
|
EventAction,
|
|
@@ -213,13 +213,13 @@ Default property:
|
|
|
213
213
|
faceDetected?: () => Data<Array<{ [key: string]: any }>>
|
|
214
214
|
}
|
|
215
215
|
animation?: AnimationBasicEvents & {
|
|
216
|
-
stateChange?:
|
|
217
|
-
recordStart?:
|
|
218
|
-
recordEnd?:
|
|
219
|
-
barcodeRead?:
|
|
220
|
-
pictureTaken?:
|
|
221
|
-
recordFinish?:
|
|
222
|
-
mountError?:
|
|
216
|
+
stateChange?: AnimationOrGetter
|
|
217
|
+
recordStart?: AnimationOrGetter
|
|
218
|
+
recordEnd?: AnimationOrGetter
|
|
219
|
+
barcodeRead?: AnimationOrGetter
|
|
220
|
+
pictureTaken?: AnimationOrGetter
|
|
221
|
+
recordFinish?: AnimationOrGetter
|
|
222
|
+
mountError?: AnimationOrGetter
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { SwitchCondInnerStateCurrentCanvas, SwitchCondData, SwitchDef } from '../switch'
|
|
6
6
|
import type { Data, DataLink } from '../data'
|
|
7
|
-
import type { Animation, AnimationBasicEvents } from '../animation'
|
|
7
|
+
import type { Animation, AnimationBasicEvents, AnimationOrGetter } from '../animation'
|
|
8
8
|
import type {
|
|
9
9
|
Brick,
|
|
10
10
|
EventAction,
|
|
@@ -343,9 +343,9 @@ Default property:
|
|
|
343
343
|
>
|
|
344
344
|
}
|
|
345
345
|
animation?: AnimationBasicEvents & {
|
|
346
|
-
onRender?:
|
|
347
|
-
onPress?:
|
|
348
|
-
onLegendSelectChanged?:
|
|
346
|
+
onRender?: AnimationOrGetter
|
|
347
|
+
onPress?: AnimationOrGetter
|
|
348
|
+
onLegendSelectChanged?: AnimationOrGetter
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
|