@fugood/bricks-ctor 2.25.0-beta.5 → 2.25.0-beta.50
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/__tests__/config-diff.test.js +100 -0
- package/compile/__tests__/index.test.js +365 -0
- package/compile/__tests__/util.test.js +317 -0
- package/compile/action-name-map.ts +64 -0
- package/compile/config-diff.ts +155 -0
- package/compile/index.ts +273 -32
- package/compile/util.ts +26 -7
- package/package.json +7 -3
- package/skills/bricks-ctor/SKILL.md +23 -17
- package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
- package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +18 -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 +132 -0
- package/skills/bricks-ctor/references/source-editing-tools.md +81 -0
- package/skills/bricks-ctor/references/verification-toolchain.md +200 -0
- package/skills/bricks-design/SKILL.md +150 -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 +114 -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/__tests__/_cli-error.test.ts +35 -0
- package/tools/_cli-error.ts +17 -0
- package/tools/_edits-log.ts +41 -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 +17 -6
- package/tools/mcp-env.ts +13 -0
- package/tools/mcp-server.ts +8 -0
- package/tools/mcp-tools/__tests__/data-calc-editing.test.js +516 -0
- package/tools/mcp-tools/__tests__/entry-editing.test.js +866 -0
- package/tools/mcp-tools/__tests__/huggingface.test.ts +49 -0
- package/tools/mcp-tools/__tests__/icons.test.ts +21 -0
- package/tools/mcp-tools/__tests__/mcp-env.test.js +19 -0
- package/tools/mcp-tools/_editing-helpers.ts +58 -0
- package/tools/mcp-tools/_verify.ts +50 -0
- package/tools/mcp-tools/compile.ts +21 -9
- package/tools/mcp-tools/data-calc-editing.ts +1349 -0
- package/tools/mcp-tools/entry-editing.ts +2336 -0
- package/tools/mcp-tools/huggingface.ts +23 -13
- package/tools/mcp-tools/icons.ts +23 -7
- package/tools/mcp-tools/media.ts +4 -1
- package/tools/postinstall.ts +80 -3
- package/tools/pull.ts +93 -22
- package/tools/push-config.ts +114 -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/__tests__/calc.test.js +25 -0
- package/utils/__tests__/id.test.js +154 -0
- package/utils/calc.ts +5 -1
- package/utils/data.ts +5 -7
- package/utils/event-props.ts +17 -0
- package/utils/id.ts +109 -56
- 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
package/compile/index.ts
CHANGED
|
@@ -6,10 +6,14 @@ import omit from 'lodash/omit'
|
|
|
6
6
|
import { parse as parseAST } from 'acorn'
|
|
7
7
|
import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
|
|
8
8
|
import escodegen from 'escodegen'
|
|
9
|
-
import {
|
|
9
|
+
import { makeSeededId } from '../utils/id'
|
|
10
10
|
import { generateCalulationMap } from './util'
|
|
11
11
|
import { templateActionNameMap } from './action-name-map'
|
|
12
12
|
import { templateEventPropsMap } from '../utils/event-props'
|
|
13
|
+
import { sh } from '../tools/_shell'
|
|
14
|
+
import { computeConfigChange, readBuildConfig } from './config-diff'
|
|
15
|
+
import { appendEditRecord, editProvenance } from '../tools/_edits-log'
|
|
16
|
+
import { isTruthyEnv } from '../tools/mcp-env'
|
|
13
17
|
import type {
|
|
14
18
|
Application,
|
|
15
19
|
Data,
|
|
@@ -154,11 +158,13 @@ const basicAnimationEvents = ['show', 'standby', 'breatheStart']
|
|
|
154
158
|
|
|
155
159
|
const compileAnimations = (
|
|
156
160
|
templateKey: string,
|
|
157
|
-
animations: { [key: string]: Animation },
|
|
161
|
+
animations: { [key: string]: Animation | (() => Animation) },
|
|
158
162
|
errorReference: string,
|
|
159
163
|
) =>
|
|
160
164
|
Object.entries(animations).reduce((acc, [key, animation]) => {
|
|
161
|
-
|
|
165
|
+
// Animation events accept either a direct Animation or a getter; unwrap.
|
|
166
|
+
const resolved = typeof animation === 'function' ? animation() : animation
|
|
167
|
+
const animationId = assertEntryId(resolved?.id, 'ANIMATION', errorReference)
|
|
162
168
|
acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
|
|
163
169
|
`ANIMATION#${animationId}`
|
|
164
170
|
return acc
|
|
@@ -179,10 +185,17 @@ const compileEvents = (
|
|
|
179
185
|
|
|
180
186
|
let handlerKey
|
|
181
187
|
let handlerTemplateKey
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
188
|
+
if (typeof handler === 'string') {
|
|
189
|
+
// Only the literal 'system' handler is normalized to the SYSTEM template key.
|
|
190
|
+
// SubspaceID (SUBSPACE_*) and ItemBrickID handlers are kept verbatim: the runtime
|
|
191
|
+
// resolves them case-sensitively (see mapEventMapHandlersWithNewId), so uppercasing
|
|
192
|
+
// a mixed-case ItemBrickID would break handler-to-item event wiring.
|
|
193
|
+
if (handler === 'system') {
|
|
194
|
+
handlerKey = 'SYSTEM'
|
|
195
|
+
handlerTemplateKey = 'SYSTEM'
|
|
196
|
+
} else {
|
|
197
|
+
handlerKey = handler
|
|
198
|
+
}
|
|
186
199
|
} else if (typeof handler === 'function') {
|
|
187
200
|
let instance = handler()
|
|
188
201
|
if (instance?.id) {
|
|
@@ -301,6 +314,162 @@ const animationTypeMap = {
|
|
|
301
314
|
AnimationTimingConfig: 'timing',
|
|
302
315
|
AnimationSpringConfig: 'spring',
|
|
303
316
|
AnimationDecayConfig: 'decay',
|
|
317
|
+
} as const
|
|
318
|
+
|
|
319
|
+
type CompiledAnimationType = (typeof animationTypeMap)[keyof typeof animationTypeMap]
|
|
320
|
+
type WarningMetadata = Record<string, unknown>
|
|
321
|
+
|
|
322
|
+
const animationProperties = new Set([
|
|
323
|
+
'transform.translateX',
|
|
324
|
+
'transform.translateY',
|
|
325
|
+
'transform.scale',
|
|
326
|
+
'transform.scaleX',
|
|
327
|
+
'transform.scaleY',
|
|
328
|
+
'transform.rotate',
|
|
329
|
+
'transform.rotateX',
|
|
330
|
+
'transform.rotateY',
|
|
331
|
+
'opacity',
|
|
332
|
+
])
|
|
333
|
+
|
|
334
|
+
const animationComposeTypes = new Set(['parallel', 'sequence'])
|
|
335
|
+
const springConfigFamilies = [
|
|
336
|
+
['stiffness', 'damping', 'mass'],
|
|
337
|
+
['tension', 'friction'],
|
|
338
|
+
['bounciness', 'speed'],
|
|
339
|
+
]
|
|
340
|
+
const springConfigFamilyKeys = new Set(springConfigFamilies.flat())
|
|
341
|
+
|
|
342
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
343
|
+
Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
344
|
+
|
|
345
|
+
const hasDefinedConfigValue = (config: Record<string, unknown>, key: string) =>
|
|
346
|
+
config[key] !== undefined
|
|
347
|
+
|
|
348
|
+
const assertConfigValue = (
|
|
349
|
+
config: Record<string, unknown>,
|
|
350
|
+
key: string,
|
|
351
|
+
errorReference: string,
|
|
352
|
+
) => {
|
|
353
|
+
if (!hasDefinedConfigValue(config, key)) {
|
|
354
|
+
throw new Error(`Invalid animation config ${errorReference}: missing "${key}"`)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const assertAnimationProperty = (property: unknown, errorReference: string) => {
|
|
359
|
+
if (typeof property !== 'string' || !animationProperties.has(property)) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Invalid animation property${errorReference ? ` ${errorReference}` : ''}: ${String(
|
|
362
|
+
property,
|
|
363
|
+
)}`,
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
return property
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const getAnimationType = (config: unknown, errorReference: string): CompiledAnimationType => {
|
|
370
|
+
if (!isRecord(config)) {
|
|
371
|
+
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const animationType = animationTypeMap[config.__type as keyof typeof animationTypeMap]
|
|
375
|
+
if (!animationType) {
|
|
376
|
+
throw new Error(`Invalid animation config type ${errorReference}: ${String(config.__type)}`)
|
|
377
|
+
}
|
|
378
|
+
return animationType
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const assertAnimationComposeType = (composeType: unknown, errorReference: string) => {
|
|
382
|
+
if (typeof composeType !== 'string' || !animationComposeTypes.has(composeType)) {
|
|
383
|
+
throw new Error(`Invalid animation compose type ${errorReference}: ${String(composeType)}`)
|
|
384
|
+
}
|
|
385
|
+
return composeType
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const pickDefinedConfigValues = (config: Record<string, unknown>, keys: string[]) =>
|
|
389
|
+
keys.reduce((acc, key) => {
|
|
390
|
+
if (hasDefinedConfigValue(config, key)) acc[key] = config[key]
|
|
391
|
+
return acc
|
|
392
|
+
}, {})
|
|
393
|
+
|
|
394
|
+
const getDefinedConfigKeys = (config: Record<string, unknown>, keys: string[]) =>
|
|
395
|
+
keys.filter((key) => hasDefinedConfigValue(config, key))
|
|
396
|
+
|
|
397
|
+
const formatWarningMetadata = (metadata: WarningMetadata = {}) =>
|
|
398
|
+
Object.entries(metadata)
|
|
399
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
400
|
+
.map(([key, value]) => `${key}: ${String(value)}`)
|
|
401
|
+
.join(', ')
|
|
402
|
+
|
|
403
|
+
const formatWarningReference = (metadata?: WarningMetadata) => {
|
|
404
|
+
const metadataText = formatWarningMetadata(metadata)
|
|
405
|
+
return metadataText ? ` [${metadataText}]` : ''
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const normalizeSpringConfig = (
|
|
409
|
+
config: Record<string, unknown>,
|
|
410
|
+
errorReference: string,
|
|
411
|
+
warningMetadata?: WarningMetadata,
|
|
412
|
+
): Record<string, unknown> => {
|
|
413
|
+
assertConfigValue(config, 'toValue', errorReference)
|
|
414
|
+
|
|
415
|
+
const usedFamilies = springConfigFamilies.filter((keys) =>
|
|
416
|
+
keys.some((key) => hasDefinedConfigValue(config, key)),
|
|
417
|
+
)
|
|
418
|
+
if (usedFamilies.length <= 1) return config
|
|
419
|
+
|
|
420
|
+
const configWithoutSpringFamily = Object.entries(config).reduce((acc, [key, value]) => {
|
|
421
|
+
if (!springConfigFamilyKeys.has(key)) acc[key] = value
|
|
422
|
+
return acc
|
|
423
|
+
}, {})
|
|
424
|
+
|
|
425
|
+
// Match runtime normalization: physical spring values are most explicit,
|
|
426
|
+
// otherwise preserve BRICKS' historical tension/friction controls.
|
|
427
|
+
const resolvedFamily =
|
|
428
|
+
usedFamilies.find((keys) => keys.includes('stiffness')) ||
|
|
429
|
+
usedFamilies.find((keys) => keys.includes('tension')) ||
|
|
430
|
+
usedFamilies[0]
|
|
431
|
+
const resolvedFamilyKeys = getDefinedConfigKeys(config, resolvedFamily)
|
|
432
|
+
const droppedFamilyKeys = usedFamilies
|
|
433
|
+
.filter((keys) => keys !== resolvedFamily)
|
|
434
|
+
.flatMap((keys) => getDefinedConfigKeys(config, keys))
|
|
435
|
+
|
|
436
|
+
console.warn(
|
|
437
|
+
`[Warning] Resolved animation spring config${formatWarningReference(
|
|
438
|
+
warningMetadata,
|
|
439
|
+
)}: using ${resolvedFamilyKeys.join('/')}, dropping ${droppedFamilyKeys.join('/')}`,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
...configWithoutSpringFamily,
|
|
444
|
+
...pickDefinedConfigValues(config, resolvedFamily),
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const compileAnimationConfig = (
|
|
449
|
+
animationType: CompiledAnimationType,
|
|
450
|
+
config: unknown,
|
|
451
|
+
errorReference: string,
|
|
452
|
+
warningMetadata?: WarningMetadata,
|
|
453
|
+
) => {
|
|
454
|
+
if (!isRecord(config)) {
|
|
455
|
+
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const compiledConfig = compileProperty(omit(config, '__type'), errorReference)
|
|
459
|
+
|
|
460
|
+
if (!isRecord(compiledConfig)) {
|
|
461
|
+
throw new Error(`Invalid animation config ${errorReference}: config must compile to an object`)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (animationType === 'timing') {
|
|
465
|
+
assertConfigValue(compiledConfig, 'toValue', errorReference)
|
|
466
|
+
} else if (animationType === 'spring') {
|
|
467
|
+
return normalizeSpringConfig(compiledConfig, errorReference, warningMetadata)
|
|
468
|
+
} else if (animationType === 'decay') {
|
|
469
|
+
assertConfigValue(compiledConfig, 'velocity', errorReference)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return compiledConfig
|
|
304
473
|
}
|
|
305
474
|
|
|
306
475
|
const compileFrame = (frame: Canvas['items'][number]['frame']) => ({
|
|
@@ -324,9 +493,12 @@ const preloadTypes = [
|
|
|
324
493
|
'media-resource-audio',
|
|
325
494
|
'media-resource-file',
|
|
326
495
|
'lottie-file-uri',
|
|
496
|
+
'rive-file-uri',
|
|
327
497
|
'ggml-model-asset',
|
|
328
498
|
'gguf-model-asset',
|
|
329
499
|
'binary-asset',
|
|
500
|
+
'mlx-model-asset',
|
|
501
|
+
'scene3d-objects',
|
|
330
502
|
]
|
|
331
503
|
|
|
332
504
|
const compileKind = (kind: Data['kind']) => {
|
|
@@ -353,9 +525,10 @@ const compileKind = (kind: Data['kind']) => {
|
|
|
353
525
|
}
|
|
354
526
|
|
|
355
527
|
const compileRemoteUpdate = (remoteUpdate: Data['remoteUpdate']) => {
|
|
356
|
-
if (!remoteUpdate) return {}
|
|
357
|
-
if (remoteUpdate.type === 'auto') return { enable_remote_update: true }
|
|
528
|
+
if (!remoteUpdate) return { bank_type: 'none' }
|
|
529
|
+
if (remoteUpdate.type === 'auto') return { bank_type: 'create', enable_remote_update: true }
|
|
358
530
|
return {
|
|
531
|
+
bank_type: remoteUpdate.type === 'device-specific' ? 'create-device-specific' : 'global',
|
|
359
532
|
enable_remote_update: true,
|
|
360
533
|
...(remoteUpdate.type === 'device-specific' ? { use_remote_id_prefix: true } : {}),
|
|
361
534
|
...(remoteUpdate.type === 'global-data' ? { global_remote_update_prop: remoteUpdate.id } : {}),
|
|
@@ -485,6 +658,7 @@ const compileAutomationTest = (
|
|
|
485
658
|
|
|
486
659
|
return {
|
|
487
660
|
id: testId,
|
|
661
|
+
alias: test.alias,
|
|
488
662
|
title: test.title,
|
|
489
663
|
hide_short_ref: test.hideShortRef,
|
|
490
664
|
timeout: test.timeout,
|
|
@@ -538,8 +712,38 @@ const compileAutomation = (automationMap: AutomationMap) =>
|
|
|
538
712
|
}),
|
|
539
713
|
)
|
|
540
714
|
|
|
715
|
+
// Record the minimal compiled-config delta this compile produced to the shared audit
|
|
716
|
+
// log (`.bricks/edits.jsonl`), so editing files directly and running `bun compile`
|
|
717
|
+
// leaves the same trail as the MCP source-editing tools. Maintained only in the
|
|
718
|
+
// editing-tools context (`BRICKS_CTOR_ENABLE_EDITING_TOOLS`); the source-editing tools
|
|
719
|
+
// turn it off for their verify compiles (see _verify.ts) so a tool edit records one
|
|
720
|
+
// richer entry instead of an extra generic compile entry. Also silent when there is no
|
|
721
|
+
// prior build to diff against (fresh projects, package tests, tooling outside a project).
|
|
722
|
+
const recordConfigChange = async (previousConfig: unknown, config: unknown) => {
|
|
723
|
+
if (previousConfig == null) return
|
|
724
|
+
if (!isTruthyEnv(process.env.BRICKS_CTOR_ENABLE_EDITING_TOOLS)) return
|
|
725
|
+
// The baseline was parsed from JSON; `computeConfigChange` applies the same
|
|
726
|
+
// JSON-omitted-field rules lazily so compile avoids cloning the full config.
|
|
727
|
+
const change = computeConfigChange(previousConfig, config)
|
|
728
|
+
if (change.status !== 'ok') return
|
|
729
|
+
await appendEditRecord(process.cwd(), {
|
|
730
|
+
ts: new Date().toISOString(),
|
|
731
|
+
tool: 'compile',
|
|
732
|
+
provenance: editProvenance(),
|
|
733
|
+
outcome: 'ok',
|
|
734
|
+
summary:
|
|
735
|
+
change.opCount === 0
|
|
736
|
+
? 'compile: no config change'
|
|
737
|
+
: `compile: ${change.opCount} config op(s)`,
|
|
738
|
+
configChange: change,
|
|
739
|
+
}).catch(() => undefined)
|
|
740
|
+
}
|
|
741
|
+
|
|
541
742
|
export const compile = async (app: Application) => {
|
|
542
743
|
await new Promise((resolve) => setImmediate(resolve, 0))
|
|
744
|
+
// Snapshot the prior build artifact before the caller's compile.ts overwrites it, so
|
|
745
|
+
// the config change introduced by this compile can be recorded on return.
|
|
746
|
+
const previousConfig = await readBuildConfig(process.cwd())
|
|
543
747
|
const timestamp = Date.now()
|
|
544
748
|
// Pre-index subspace ids so the canvas-item validation below stays O(1).
|
|
545
749
|
const subspaceIdSet = new Set(app.subspaces.map((s) => s.id))
|
|
@@ -559,7 +763,14 @@ export const compile = async (app: Application) => {
|
|
|
559
763
|
// validation (root_canvas_id is required before the conditional
|
|
560
764
|
// schema fix is published).
|
|
561
765
|
if (subspace.module?.link) {
|
|
562
|
-
|
|
766
|
+
// Seed the placeholder id from the (stable) subspace id. `makeId('canvas')` would take
|
|
767
|
+
// the count-fallback branch (a process-global counter that is never reset), so the
|
|
768
|
+
// placeholder id depended on how many prior count-fallback ids had been minted — making
|
|
769
|
+
// it differ between recompiles and breaking compile's byte-stable-output contract
|
|
770
|
+
// (phantom config-change ops). `makeSeededId` keeps no global state, so identical source
|
|
771
|
+
// recompiles to an identical id. (`makeId('canvas', alias)` would instead throw
|
|
772
|
+
// "Duplicate makeId alias" on the second compile in a long-lived process.)
|
|
773
|
+
const placeholderCanvasId = makeSeededId('canvas', `${subspaceId}:module-placeholder`)
|
|
563
774
|
subspaceMap[subspaceId] = {
|
|
564
775
|
title: subspace.title,
|
|
565
776
|
description: subspace.description,
|
|
@@ -629,39 +840,63 @@ export const compile = async (app: Application) => {
|
|
|
629
840
|
`(animation index: ${animationIndex}, subspace: ${subspaceId})`,
|
|
630
841
|
)
|
|
631
842
|
|
|
632
|
-
|
|
843
|
+
const animationTypename = animation.__typename
|
|
844
|
+
if (animationTypename === 'Animation') {
|
|
633
845
|
const animationDef = animation as AnimationDef
|
|
846
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
847
|
+
const animationWarningMetadata = {
|
|
848
|
+
animationIndex,
|
|
849
|
+
animationTitle: animationDef.title,
|
|
850
|
+
animationAlias: animationDef.alias,
|
|
851
|
+
animationProperty: animationDef.property,
|
|
852
|
+
subspaceIndex,
|
|
853
|
+
subspaceTitle: subspace.title,
|
|
854
|
+
}
|
|
855
|
+
const animationType = getAnimationType(animationDef.config, animationErrorReference)
|
|
634
856
|
map[animationId] = {
|
|
635
857
|
alias: animationDef.alias,
|
|
636
858
|
title: animationDef.title,
|
|
637
859
|
description: animationDef.description,
|
|
638
860
|
hide_short_ref: animationDef.hideShortRef,
|
|
639
861
|
animationRunType: animationDef.runType,
|
|
640
|
-
property: animationDef.property,
|
|
641
|
-
type:
|
|
642
|
-
config:
|
|
643
|
-
|
|
644
|
-
|
|
862
|
+
property: assertAnimationProperty(animationDef.property, animationErrorReference),
|
|
863
|
+
type: animationType,
|
|
864
|
+
config: compileAnimationConfig(
|
|
865
|
+
animationType,
|
|
866
|
+
animationDef.config,
|
|
867
|
+
animationErrorReference,
|
|
868
|
+
animationWarningMetadata,
|
|
645
869
|
),
|
|
646
870
|
}
|
|
647
|
-
} else if (
|
|
871
|
+
} else if (animationTypename === 'AnimationCompose') {
|
|
648
872
|
const animationDef = animation as AnimationComposeDef
|
|
873
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
649
874
|
map[animationId] = {
|
|
650
875
|
alias: animationDef.alias,
|
|
651
876
|
title: animationDef.title,
|
|
652
877
|
description: animationDef.description,
|
|
653
878
|
hide_short_ref: animationDef.hideShortRef,
|
|
654
879
|
animationRunType: animationDef.runType,
|
|
655
|
-
compose_type:
|
|
880
|
+
compose_type: assertAnimationComposeType(
|
|
881
|
+
animationDef.composeType,
|
|
882
|
+
animationErrorReference,
|
|
883
|
+
),
|
|
656
884
|
item_list: animationDef.items.map((item, index) => {
|
|
657
885
|
const innerAnimation = item()
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
)
|
|
662
|
-
|
|
886
|
+
const innerAnimationId = assertEntryId(
|
|
887
|
+
innerAnimation?.id,
|
|
888
|
+
'ANIMATION',
|
|
889
|
+
`(animation item index: ${index}, animation: ${animationId}, subspace ${subspaceId})`,
|
|
890
|
+
)
|
|
891
|
+
return { animation_id: innerAnimationId }
|
|
663
892
|
}),
|
|
664
893
|
}
|
|
894
|
+
} else {
|
|
895
|
+
throw new Error(
|
|
896
|
+
`Invalid animation typename (animation: ${animationId}, subspace ${subspaceId}): ${String(
|
|
897
|
+
animationTypename,
|
|
898
|
+
)}`,
|
|
899
|
+
)
|
|
665
900
|
}
|
|
666
901
|
return map
|
|
667
902
|
}, {}),
|
|
@@ -1009,7 +1244,7 @@ export const compile = async (app: Application) => {
|
|
|
1009
1244
|
...compileRemoteUpdate(data.remoteUpdate),
|
|
1010
1245
|
routing: data.routing,
|
|
1011
1246
|
schema: data.schema,
|
|
1012
|
-
type: data.type,
|
|
1247
|
+
type: data.type === 'boolean' ? 'bool' : data.type,
|
|
1013
1248
|
...compileKind(data.kind),
|
|
1014
1249
|
value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
|
|
1015
1250
|
event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
|
|
@@ -1029,6 +1264,7 @@ export const compile = async (app: Application) => {
|
|
|
1029
1264
|
)
|
|
1030
1265
|
|
|
1031
1266
|
const calc: any = {
|
|
1267
|
+
alias: dataCalc.alias,
|
|
1032
1268
|
title: dataCalc.title,
|
|
1033
1269
|
description: dataCalc.description,
|
|
1034
1270
|
hide_short_ref: dataCalc.hideShortRef,
|
|
@@ -1227,12 +1463,7 @@ export const compile = async (app: Application) => {
|
|
|
1227
1463
|
: null,
|
|
1228
1464
|
}
|
|
1229
1465
|
|
|
1230
|
-
Object.assign(
|
|
1231
|
-
calc,
|
|
1232
|
-
generateCalulationMap(calc.script_config, {
|
|
1233
|
-
snapshotMode: process.env.BRICKS_SNAPSHOT_MODE === '1',
|
|
1234
|
-
}),
|
|
1235
|
-
)
|
|
1466
|
+
Object.assign(calc, generateCalulationMap(calc.script_config, dataCalcId))
|
|
1236
1467
|
}
|
|
1237
1468
|
map[dataCalcId] = calc
|
|
1238
1469
|
return map
|
|
@@ -1269,10 +1500,20 @@ export const compile = async (app: Application) => {
|
|
|
1269
1500
|
automation_map: compiledAutomationMap || app.metadata?.TEMP_automation_map || {},
|
|
1270
1501
|
update_timestamp: timestamp,
|
|
1271
1502
|
}
|
|
1503
|
+
await recordConfigChange(previousConfig, config)
|
|
1272
1504
|
return config
|
|
1273
1505
|
}
|
|
1274
1506
|
|
|
1275
1507
|
export const checkConfig = async (configPath: string) => {
|
|
1276
|
-
|
|
1277
|
-
|
|
1508
|
+
// --validate-automation surfaces broken automation_map / test_map refs early,
|
|
1509
|
+
// which catches agent-authored automations that reference deleted bricks.
|
|
1510
|
+
await sh`bricks app check-config --validate-automation ${configPath}`
|
|
1511
|
+
// Doctor adds semantic lint checks after structural validation. Warnings are
|
|
1512
|
+
// surfaced in the compile log, but only errors fail by default. Older published
|
|
1513
|
+
// bricks-cli builds lack `app doctor` — skip rather than fail the compile.
|
|
1514
|
+
const doctor = await sh`bricks app doctor --validate-automation ${configPath}`.nothrow()
|
|
1515
|
+
if (doctor.exitCode !== 0) {
|
|
1516
|
+
if (/unknown command/i.test(doctor.stderr?.toString() ?? '')) return
|
|
1517
|
+
throw new Error(`bricks app doctor failed with exit ${doctor.exitCode}`)
|
|
1518
|
+
}
|
|
1278
1519
|
}
|
package/compile/util.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { makeSeededId } from '../utils/id'
|
|
2
2
|
|
|
3
3
|
type ScriptConfig = {
|
|
4
4
|
title?: string
|
|
@@ -27,21 +27,40 @@ export const validateConfig = (config: ScriptConfig) => {
|
|
|
27
27
|
if (Object.values(config.outputs).some((value) => value.some((id) => config.inputs[id]))) {
|
|
28
28
|
throw new Error(`${errorMsg}. key: outputs`)
|
|
29
29
|
}
|
|
30
|
+
// The same data-node id reused across the output-side targets (output / error / outputs)
|
|
31
|
+
// also collides: generateCalulationMap spreads their node objects last-wins, so the later
|
|
32
|
+
// one silently overwrites the earlier wiring (e.g. error == output drops the error change
|
|
33
|
+
// link). The checks above only compare against `inputs`, so guard the output-side pairs
|
|
34
|
+
// too. (The same id appearing in multiple `outputs` entries is a supported last-wins case
|
|
35
|
+
// and stays allowed.)
|
|
36
|
+
if (config.error && config.output && config.error === config.output) {
|
|
37
|
+
throw new Error(`${errorMsg}. key: error/output`)
|
|
38
|
+
}
|
|
39
|
+
const outputsIds = Object.values(config.outputs).flat()
|
|
40
|
+
if (config.output && outputsIds.includes(config.output)) {
|
|
41
|
+
throw new Error(`${errorMsg}. key: output/outputs`)
|
|
42
|
+
}
|
|
43
|
+
if (config.error && outputsIds.includes(config.error)) {
|
|
44
|
+
throw new Error(`${errorMsg}. key: error/outputs`)
|
|
45
|
+
}
|
|
30
46
|
}
|
|
31
47
|
|
|
32
48
|
const padding = 15
|
|
33
49
|
const layerXInterval = 300
|
|
34
50
|
const layerYInterval = 150
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
// `calcId` (the owning script calc's stable id) seeds every derived command-node id, so an
|
|
53
|
+
// unchanged calc recompiles to identical ids and editing one calc never shifts another's —
|
|
54
|
+
// keeping the compiled config byte-stable for change detection. See makeSeededId in utils/id.
|
|
55
|
+
export const generateCalulationMap = (config: ScriptConfig, calcId: string) => {
|
|
37
56
|
validateConfig(config)
|
|
38
|
-
const sandboxId =
|
|
39
|
-
const sandboxErrorId =
|
|
40
|
-
const sandboxResultId =
|
|
57
|
+
const sandboxId = makeSeededId('property_bank_command', `${calcId}:sandbox-run`)
|
|
58
|
+
const sandboxErrorId = makeSeededId('property_bank_command', `${calcId}:sandbox-error`)
|
|
59
|
+
const sandboxResultId = makeSeededId('property_bank_command', `${calcId}:sandbox-result`)
|
|
41
60
|
|
|
42
61
|
const inputs = Object.entries(config.inputs).reduce(
|
|
43
62
|
(acc, [key, value], index) => {
|
|
44
|
-
const commandId =
|
|
63
|
+
const commandId = makeSeededId('property_bank_command', `${calcId}:input:${key}`)
|
|
45
64
|
acc.map[key] = {
|
|
46
65
|
type: 'data-node',
|
|
47
66
|
properties: {},
|
|
@@ -123,7 +142,7 @@ export const generateCalulationMap = (config: ScriptConfig, opts?: { snapshotMod
|
|
|
123
142
|
let y = 0
|
|
124
143
|
const outputs = Object.entries(config.outputs).reduce(
|
|
125
144
|
(acc, [key, pbList], index) => {
|
|
126
|
-
const commandId =
|
|
145
|
+
const commandId = makeSeededId('property_bank_command', `${calcId}:output:${key}`)
|
|
127
146
|
acc.commandIdList.push(commandId)
|
|
128
147
|
acc.map[commandId] = {
|
|
129
148
|
type: 'command-node-object',
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-ctor",
|
|
3
|
-
"version": "2.25.0-beta.
|
|
3
|
+
"version": "2.25.0-beta.50",
|
|
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
|
-
"@
|
|
10
|
+
"@babel/generator": "7.28.5",
|
|
11
|
+
"@babel/parser": "7.28.5",
|
|
12
|
+
"@babel/traverse": "7.28.5",
|
|
13
|
+
"@babel/types": "7.28.5",
|
|
14
|
+
"@fugood/bricks-cli": "^2.25.0-beta.50",
|
|
11
15
|
"@huggingface/gguf": "^0.3.2",
|
|
12
16
|
"@iarna/toml": "^3.0.0",
|
|
13
17
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
@@ -25,5 +29,5 @@
|
|
|
25
29
|
"peerDependencies": {
|
|
26
30
|
"oxfmt": "^0.36.0"
|
|
27
31
|
},
|
|
28
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "3614445cd0166e40634f69c6238299bb63f4f597"
|
|
29
33
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bricks-ctor
|
|
3
|
-
description: Advanced BRICKS configuration knowledge. Covers Standby Transition, Automations (E2E testing), Data Calculation (JS sandbox libraries), Local Sync, Remote Data Bank, Media Flow,
|
|
3
|
+
description: Advanced BRICKS configuration knowledge. Covers Standby Transition, Automations (E2E testing), Data Calculation (JS sandbox libraries), Local Sync, Remote Data Bank, Media Flow, Buttress (remote inference), and the verification toolchain (compile, harness-specific preview tool, on-device DevTools, definition-of-done gate). Triggers on multi-device sync, cloud data, media assets, AI offloading, E2E testing, canvas transitions, or verifying project work before declaring done.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# BRICKS Ctor - Advanced Features
|
|
@@ -11,22 +11,28 @@ This skill covers advanced BRICKS features not in the main project instructions.
|
|
|
11
11
|
|
|
12
12
|
| Rule | Description |
|
|
13
13
|
|------|-------------|
|
|
14
|
-
| [Architecture Patterns](
|
|
15
|
-
| [
|
|
16
|
-
| [
|
|
17
|
-
| [
|
|
18
|
-
| [
|
|
19
|
-
| [
|
|
20
|
-
| [
|
|
21
|
-
| [
|
|
22
|
-
| [
|
|
14
|
+
| [Architecture Patterns](references/architecture-patterns.md) | **Read first** — decompose flows and select patterns |
|
|
15
|
+
| [Source-Editing Tools](references/source-editing-tools.md) | MCP tools for editing entries and data-calcs by AST (new/edit/remove, value grammar, verify) |
|
|
16
|
+
| [Animation](references/animation.md) | Animation system for brick transforms and opacity |
|
|
17
|
+
| [Standby Transition](references/standby-transition.md) | Canvas enter/exit animations |
|
|
18
|
+
| [Automations](references/automations.md) | E2E testing and scheduled tasks |
|
|
19
|
+
| [Data Calculation](references/data-calculation.md) | Wiring contract, trigger semantics, JS sandbox (25+ libraries) |
|
|
20
|
+
| [Local Sync](references/local-sync.md) | LAN device synchronization |
|
|
21
|
+
| [Remote Data Bank](references/remote-data-bank.md) | Cloud data sync and API access |
|
|
22
|
+
| [Media Flow](references/media-flow.md) | Media asset management |
|
|
23
|
+
| [Buttress](references/buttress.md) | Remote inference for AI generators |
|
|
24
|
+
| [Verification Toolchain](references/verification-toolchain.md) | Definition of done, compile, preview tool selection, on-device DevTools, Path 1/2/3 decision rule |
|
|
25
|
+
| [Simulator](references/simulator.md) | Path 1 fidelity — Simulator feature support, fallbacks (Camera/LLM/STT/Vector Store, Buttress disabled), when a green run is enough vs. Path 2 |
|
|
23
26
|
|
|
24
27
|
## Quick Reference
|
|
25
28
|
|
|
26
|
-
- **Complex flows**: See [Architecture Patterns](
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
29
|
+
- **Complex flows**: See [Architecture Patterns](references/architecture-patterns.md) for decomposing multi-step workflows
|
|
30
|
+
- **Editing entries/data-calcs**: See [Source-Editing Tools](references/source-editing-tools.md) — prefer the MCP editing tools over hand-editing `subspaces/**`
|
|
31
|
+
- **Multi-device**: See [Local Sync](references/local-sync.md) for LAN coordination
|
|
32
|
+
- **Cloud data**: See [Remote Data Bank](references/remote-data-bank.md) for sync and API access
|
|
33
|
+
- **Media assets**: See [Media Flow](references/media-flow.md) for centralized asset management
|
|
34
|
+
- **AI offloading**: See [Buttress](references/buttress.md) for GPU server delegation
|
|
35
|
+
- **E2E testing**: See [Automations](references/automations.md) for test automation
|
|
36
|
+
- **Enter animations**: See [Standby Transition](references/standby-transition.md) for canvas transitions
|
|
37
|
+
- **Verification before done**: See [Verification Toolchain](references/verification-toolchain.md) for the definition-of-done gate and Path 1/2/3 decision rule
|
|
38
|
+
- **Simulator fidelity**: See [Simulator](references/simulator.md) for what the Simulator fakes (Camera, AI generators, Buttress) and when to escalate to a real device
|
|
@@ -41,12 +41,13 @@ const bounce: AnimationDef = {
|
|
|
41
41
|
toValue: 1,
|
|
42
42
|
friction: 7,
|
|
43
43
|
tension: 40,
|
|
44
|
-
speed: 12,
|
|
45
|
-
bounciness: 8,
|
|
46
44
|
},
|
|
47
45
|
}
|
|
48
46
|
```
|
|
49
47
|
|
|
48
|
+
Use one spring parameter family per config: `tension/friction`, `speed/bounciness`, or
|
|
49
|
+
`stiffness/damping/mass`.
|
|
50
|
+
|
|
50
51
|
### Decay Animation
|
|
51
52
|
Velocity-based deceleration animation.
|
|
52
53
|
|
|
@@ -26,10 +26,18 @@ The primary way to orchestrate multi-step flows. A single event can contain an a
|
|
|
26
26
|
- Use `dataParams` + `mapping` to pass event data downstream
|
|
27
27
|
- This is the "glue" that wires generators, state, and UI together
|
|
28
28
|
|
|
29
|
+
Sequential `PROPERTY_BANK` / `PROPERTY_BANK_EXPRESSION` actions in one chain read the data values that existed when the chain started. If a later action needs to read what an earlier action wrote, set `waitAsync: true` on the earlier action.
|
|
30
|
+
|
|
29
31
|
### System Actions (Priority 3)
|
|
30
32
|
Built-in commands for direct state and UI changes.
|
|
31
33
|
- **PROPERTY_BANK**: set data value
|
|
32
34
|
- **PROPERTY_BANK_EXPRESSION**: inline JS expression for simple compute
|
|
35
|
+
- The expression engine folds statements into a single expression: only expression
|
|
36
|
+
statements, simple `const`/`let` declarations, and a final return/expression are
|
|
37
|
+
supported — **no `if`/`for`/`while`/`switch`** (use ternaries). The same limit
|
|
38
|
+
applies inside a zero-arg IIFE body. Unsupported statements fail at runtime with
|
|
39
|
+
the error visible only in a DevTools session, so prefer ternary chains or move the
|
|
40
|
+
logic to a DataCalculationScript.
|
|
33
41
|
- **CHANGE_CANVAS**: navigate to another canvas
|
|
34
42
|
- **DYNAMIC_ANIMATION**: trigger animation
|
|
35
43
|
- **ALERT / MESSAGE**: system feedback
|
|
@@ -67,3 +75,13 @@ Only actual data derivation maps to Data Calculation:
|
|
|
67
75
|
|
|
68
76
|
### Step 4: Wire with Event Action Chains
|
|
69
77
|
Connect the pieces through events on generators and bricks.
|
|
78
|
+
|
|
79
|
+
## Recipe: user-driven state machine (calculator, form wizard, picker)
|
|
80
|
+
|
|
81
|
+
A brief like "state vars X, Y, Z; button A updates X from X+Y; button B resets" is a state machine. The shape:
|
|
82
|
+
|
|
83
|
+
- Each state variable is its own `Data` entity. Displays read it via `linkData(() => data.dFoo)`.
|
|
84
|
+
- Each button's `onPress` is an Event Action Chain. For every state var that changes, append one `PROPERTY_BANK_EXPRESSION` whose `expression` reads the current state and returns the new value.
|
|
85
|
+
- Use `Data Calculation` only for reusable pure derivations referenced as inputs to those expressions.
|
|
86
|
+
|
|
87
|
+
A 10-button calculator with 4 state vars is ~10 small chains of 1–4 inline expressions. If you find yourself writing a single auto-mode `DataCalculationScript` that consumes all state vars and emits all-new state through mirror `dFooResult` data — or pinging a `dLastInput` field to force an auto calc to re-run — the chain shape was the right answer.
|
|
@@ -118,6 +118,16 @@ const testLoginFlow: AutomationTest = {
|
|
|
118
118
|
| `match_screenshot` | `[name, threshold?, maxRetry?]` | Screenshot compare |
|
|
119
119
|
| `delay` | `[subspace?, property?, defaultValue?]` | Delay execution |
|
|
120
120
|
|
|
121
|
+
In project TypeScript source, pass entity getters for BRICKS entities:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
run: ['brick_press', () => mainSubspace, () => bricks.bSubmitButton]
|
|
125
|
+
run: ['wait_until_canvas_change', () => mainSubspace, () => canvases.cDone, 5000]
|
|
126
|
+
run: ['assert_property', () => mainSubspace, () => data.dStep, 'done']
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The compiler resolves these getters to the current generated IDs.
|
|
130
|
+
|
|
121
131
|
### execute_action Params
|
|
122
132
|
|
|
123
133
|
The `params` object in `execute_action` uses **runtime event property keys** from `event-props.ts`, NOT the action config `input` names from type definitions.
|
|
@@ -208,6 +218,7 @@ Automations work with Modules. Use Manual Run in Preview mode for module testing
|
|
|
208
218
|
|
|
209
219
|
- **Automation map key**: Always use `'AUTOMATION_MAP_DEFAULT'` as the automation map ID (not `makeId()`). The preview test runner reads from `automationMap['AUTOMATION_MAP_DEFAULT']?.map`.
|
|
210
220
|
- **Valid makeId types**: Use `'test'` for AutomationTest, `'test_case'` for TestCase, `'test_var'` for TestVariable. Do NOT use `'automation_test'` or `'automation_test_map'`.
|
|
221
|
+
- **Entity references in run arrays**: Use getter references (`() => subspace`, `() => bricks.bButton`, `() => data.dValue`) in TypeScript source so compile resolves fresh IDs.
|
|
211
222
|
- **handler in execute_action**: Pass the entity's `.id` string (e.g., `bricks.bInput.id`), not a getter function.
|
|
212
223
|
|
|
213
224
|
## Best Practices
|