@fugood/bricks-ctor 2.25.0-beta.5 → 2.25.0-beta.51
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 +386 -0
- package/compile/__tests__/util.test.js +337 -0
- package/compile/action-name-map.ts +64 -0
- package/compile/config-diff.ts +155 -0
- package/compile/index.ts +278 -34
- package/compile/util.ts +34 -10
- 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/__tests__/_mcp-config.test.ts +67 -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/_mcp-config.ts +42 -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 +95 -38
- 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 } : {}),
|
|
@@ -411,7 +584,7 @@ function compileRunArray(run: unknown[]): unknown[] {
|
|
|
411
584
|
/**
|
|
412
585
|
* Compile typed TestCase to raw format (strips __typename)
|
|
413
586
|
*/
|
|
414
|
-
const compileTestCase = (testCase: TestCase) => ({
|
|
587
|
+
export const compileTestCase = (testCase: TestCase) => ({
|
|
415
588
|
id: testCase.id,
|
|
416
589
|
name: testCase.name,
|
|
417
590
|
hide_short_ref: testCase.hideShortRef,
|
|
@@ -433,7 +606,10 @@ const compileTestCase = (testCase: TestCase) => ({
|
|
|
433
606
|
variable: cond.variable,
|
|
434
607
|
operator: cond.operator,
|
|
435
608
|
value: cond.value,
|
|
436
|
-
jump_to
|
|
609
|
+
// `jump_to` may be a getter (() => TestCase) for dynamic case ids (the project generator
|
|
610
|
+
// emits this form). Resolve it to its id like the `run` array does — otherwise the function
|
|
611
|
+
// is JSON-serialized to nothing and the conditional jump silently vanishes from the config.
|
|
612
|
+
jump_to: compileRunElement(cond.jump_to),
|
|
437
613
|
}
|
|
438
614
|
}),
|
|
439
615
|
})
|
|
@@ -485,6 +661,7 @@ const compileAutomationTest = (
|
|
|
485
661
|
|
|
486
662
|
return {
|
|
487
663
|
id: testId,
|
|
664
|
+
alias: test.alias,
|
|
488
665
|
title: test.title,
|
|
489
666
|
hide_short_ref: test.hideShortRef,
|
|
490
667
|
timeout: test.timeout,
|
|
@@ -538,8 +715,38 @@ const compileAutomation = (automationMap: AutomationMap) =>
|
|
|
538
715
|
}),
|
|
539
716
|
)
|
|
540
717
|
|
|
718
|
+
// Record the minimal compiled-config delta this compile produced to the shared audit
|
|
719
|
+
// log (`.bricks/edits.jsonl`), so editing files directly and running `bun compile`
|
|
720
|
+
// leaves the same trail as the MCP source-editing tools. Maintained only in the
|
|
721
|
+
// editing-tools context (`BRICKS_CTOR_ENABLE_EDITING_TOOLS`); the source-editing tools
|
|
722
|
+
// turn it off for their verify compiles (see _verify.ts) so a tool edit records one
|
|
723
|
+
// richer entry instead of an extra generic compile entry. Also silent when there is no
|
|
724
|
+
// prior build to diff against (fresh projects, package tests, tooling outside a project).
|
|
725
|
+
const recordConfigChange = async (previousConfig: unknown, config: unknown) => {
|
|
726
|
+
if (previousConfig == null) return
|
|
727
|
+
if (!isTruthyEnv(process.env.BRICKS_CTOR_ENABLE_EDITING_TOOLS)) return
|
|
728
|
+
// The baseline was parsed from JSON; `computeConfigChange` applies the same
|
|
729
|
+
// JSON-omitted-field rules lazily so compile avoids cloning the full config.
|
|
730
|
+
const change = computeConfigChange(previousConfig, config)
|
|
731
|
+
if (change.status !== 'ok') return
|
|
732
|
+
await appendEditRecord(process.cwd(), {
|
|
733
|
+
ts: new Date().toISOString(),
|
|
734
|
+
tool: 'compile',
|
|
735
|
+
provenance: editProvenance(),
|
|
736
|
+
outcome: 'ok',
|
|
737
|
+
summary:
|
|
738
|
+
change.opCount === 0
|
|
739
|
+
? 'compile: no config change'
|
|
740
|
+
: `compile: ${change.opCount} config op(s)`,
|
|
741
|
+
configChange: change,
|
|
742
|
+
}).catch(() => undefined)
|
|
743
|
+
}
|
|
744
|
+
|
|
541
745
|
export const compile = async (app: Application) => {
|
|
542
746
|
await new Promise((resolve) => setImmediate(resolve, 0))
|
|
747
|
+
// Snapshot the prior build artifact before the caller's compile.ts overwrites it, so
|
|
748
|
+
// the config change introduced by this compile can be recorded on return.
|
|
749
|
+
const previousConfig = await readBuildConfig(process.cwd())
|
|
543
750
|
const timestamp = Date.now()
|
|
544
751
|
// Pre-index subspace ids so the canvas-item validation below stays O(1).
|
|
545
752
|
const subspaceIdSet = new Set(app.subspaces.map((s) => s.id))
|
|
@@ -559,7 +766,14 @@ export const compile = async (app: Application) => {
|
|
|
559
766
|
// validation (root_canvas_id is required before the conditional
|
|
560
767
|
// schema fix is published).
|
|
561
768
|
if (subspace.module?.link) {
|
|
562
|
-
|
|
769
|
+
// Seed the placeholder id from the (stable) subspace id. `makeId('canvas')` would take
|
|
770
|
+
// the count-fallback branch (a process-global counter that is never reset), so the
|
|
771
|
+
// placeholder id depended on how many prior count-fallback ids had been minted — making
|
|
772
|
+
// it differ between recompiles and breaking compile's byte-stable-output contract
|
|
773
|
+
// (phantom config-change ops). `makeSeededId` keeps no global state, so identical source
|
|
774
|
+
// recompiles to an identical id. (`makeId('canvas', alias)` would instead throw
|
|
775
|
+
// "Duplicate makeId alias" on the second compile in a long-lived process.)
|
|
776
|
+
const placeholderCanvasId = makeSeededId('canvas', `${subspaceId}:module-placeholder`)
|
|
563
777
|
subspaceMap[subspaceId] = {
|
|
564
778
|
title: subspace.title,
|
|
565
779
|
description: subspace.description,
|
|
@@ -629,39 +843,63 @@ export const compile = async (app: Application) => {
|
|
|
629
843
|
`(animation index: ${animationIndex}, subspace: ${subspaceId})`,
|
|
630
844
|
)
|
|
631
845
|
|
|
632
|
-
|
|
846
|
+
const animationTypename = animation.__typename
|
|
847
|
+
if (animationTypename === 'Animation') {
|
|
633
848
|
const animationDef = animation as AnimationDef
|
|
849
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
850
|
+
const animationWarningMetadata = {
|
|
851
|
+
animationIndex,
|
|
852
|
+
animationTitle: animationDef.title,
|
|
853
|
+
animationAlias: animationDef.alias,
|
|
854
|
+
animationProperty: animationDef.property,
|
|
855
|
+
subspaceIndex,
|
|
856
|
+
subspaceTitle: subspace.title,
|
|
857
|
+
}
|
|
858
|
+
const animationType = getAnimationType(animationDef.config, animationErrorReference)
|
|
634
859
|
map[animationId] = {
|
|
635
860
|
alias: animationDef.alias,
|
|
636
861
|
title: animationDef.title,
|
|
637
862
|
description: animationDef.description,
|
|
638
863
|
hide_short_ref: animationDef.hideShortRef,
|
|
639
864
|
animationRunType: animationDef.runType,
|
|
640
|
-
property: animationDef.property,
|
|
641
|
-
type:
|
|
642
|
-
config:
|
|
643
|
-
|
|
644
|
-
|
|
865
|
+
property: assertAnimationProperty(animationDef.property, animationErrorReference),
|
|
866
|
+
type: animationType,
|
|
867
|
+
config: compileAnimationConfig(
|
|
868
|
+
animationType,
|
|
869
|
+
animationDef.config,
|
|
870
|
+
animationErrorReference,
|
|
871
|
+
animationWarningMetadata,
|
|
645
872
|
),
|
|
646
873
|
}
|
|
647
|
-
} else if (
|
|
874
|
+
} else if (animationTypename === 'AnimationCompose') {
|
|
648
875
|
const animationDef = animation as AnimationComposeDef
|
|
876
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
649
877
|
map[animationId] = {
|
|
650
878
|
alias: animationDef.alias,
|
|
651
879
|
title: animationDef.title,
|
|
652
880
|
description: animationDef.description,
|
|
653
881
|
hide_short_ref: animationDef.hideShortRef,
|
|
654
882
|
animationRunType: animationDef.runType,
|
|
655
|
-
compose_type:
|
|
883
|
+
compose_type: assertAnimationComposeType(
|
|
884
|
+
animationDef.composeType,
|
|
885
|
+
animationErrorReference,
|
|
886
|
+
),
|
|
656
887
|
item_list: animationDef.items.map((item, index) => {
|
|
657
888
|
const innerAnimation = item()
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
)
|
|
662
|
-
|
|
889
|
+
const innerAnimationId = assertEntryId(
|
|
890
|
+
innerAnimation?.id,
|
|
891
|
+
'ANIMATION',
|
|
892
|
+
`(animation item index: ${index}, animation: ${animationId}, subspace ${subspaceId})`,
|
|
893
|
+
)
|
|
894
|
+
return { animation_id: innerAnimationId }
|
|
663
895
|
}),
|
|
664
896
|
}
|
|
897
|
+
} else {
|
|
898
|
+
throw new Error(
|
|
899
|
+
`Invalid animation typename (animation: ${animationId}, subspace ${subspaceId}): ${String(
|
|
900
|
+
animationTypename,
|
|
901
|
+
)}`,
|
|
902
|
+
)
|
|
665
903
|
}
|
|
666
904
|
return map
|
|
667
905
|
}, {}),
|
|
@@ -1009,7 +1247,7 @@ export const compile = async (app: Application) => {
|
|
|
1009
1247
|
...compileRemoteUpdate(data.remoteUpdate),
|
|
1010
1248
|
routing: data.routing,
|
|
1011
1249
|
schema: data.schema,
|
|
1012
|
-
type: data.type,
|
|
1250
|
+
type: data.type === 'boolean' ? 'bool' : data.type,
|
|
1013
1251
|
...compileKind(data.kind),
|
|
1014
1252
|
value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
|
|
1015
1253
|
event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
|
|
@@ -1029,6 +1267,7 @@ export const compile = async (app: Application) => {
|
|
|
1029
1267
|
)
|
|
1030
1268
|
|
|
1031
1269
|
const calc: any = {
|
|
1270
|
+
alias: dataCalc.alias,
|
|
1032
1271
|
title: dataCalc.title,
|
|
1033
1272
|
description: dataCalc.description,
|
|
1034
1273
|
hide_short_ref: dataCalc.hideShortRef,
|
|
@@ -1227,12 +1466,7 @@ export const compile = async (app: Application) => {
|
|
|
1227
1466
|
: null,
|
|
1228
1467
|
}
|
|
1229
1468
|
|
|
1230
|
-
Object.assign(
|
|
1231
|
-
calc,
|
|
1232
|
-
generateCalulationMap(calc.script_config, {
|
|
1233
|
-
snapshotMode: process.env.BRICKS_SNAPSHOT_MODE === '1',
|
|
1234
|
-
}),
|
|
1235
|
-
)
|
|
1469
|
+
Object.assign(calc, generateCalulationMap(calc.script_config, dataCalcId))
|
|
1236
1470
|
}
|
|
1237
1471
|
map[dataCalcId] = calc
|
|
1238
1472
|
return map
|
|
@@ -1269,10 +1503,20 @@ export const compile = async (app: Application) => {
|
|
|
1269
1503
|
automation_map: compiledAutomationMap || app.metadata?.TEMP_automation_map || {},
|
|
1270
1504
|
update_timestamp: timestamp,
|
|
1271
1505
|
}
|
|
1506
|
+
await recordConfigChange(previousConfig, config)
|
|
1272
1507
|
return config
|
|
1273
1508
|
}
|
|
1274
1509
|
|
|
1275
1510
|
export const checkConfig = async (configPath: string) => {
|
|
1276
|
-
|
|
1277
|
-
|
|
1511
|
+
// --validate-automation surfaces broken automation_map / test_map refs early,
|
|
1512
|
+
// which catches agent-authored automations that reference deleted bricks.
|
|
1513
|
+
await sh`bricks app check-config --validate-automation ${configPath}`
|
|
1514
|
+
// Doctor adds semantic lint checks after structural validation. Warnings are
|
|
1515
|
+
// surfaced in the compile log, but only errors fail by default. Older published
|
|
1516
|
+
// bricks-cli builds lack `app doctor` — skip rather than fail the compile.
|
|
1517
|
+
const doctor = await sh`bricks app doctor --validate-automation ${configPath}`.nothrow()
|
|
1518
|
+
if (doctor.exitCode !== 0) {
|
|
1519
|
+
if (/unknown command/i.test(doctor.stderr?.toString() ?? '')) return
|
|
1520
|
+
throw new Error(`bricks app doctor failed with exit ${doctor.exitCode}`)
|
|
1521
|
+
}
|
|
1278
1522
|
}
|
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
|
|
@@ -18,30 +18,54 @@ const errorMsg = 'Not allow duplicate set property id between inputs / outputs /
|
|
|
18
18
|
export const validateConfig = (config: ScriptConfig) => {
|
|
19
19
|
// Skip input/output overlap validation in manual mode (allows same data node in both)
|
|
20
20
|
if (config.trigger_mode === 'manual') return
|
|
21
|
-
|
|
21
|
+
// `inputs` is keyed by data-node id with the OBJECT_SET path as the *value*. The overlap
|
|
22
|
+
// check is "is this id also an input?" — a key-existence question — so test key presence,
|
|
23
|
+
// not the path's truthiness. An empty path ('' = OBJECT_SET's default "set whole object")
|
|
24
|
+
// is a legitimate input value that would otherwise slip the overlap guard.
|
|
25
|
+
const isInput = (id: string) => Object.prototype.hasOwnProperty.call(config.inputs, id)
|
|
26
|
+
if (config.error && isInput(config.error)) {
|
|
22
27
|
throw new Error(`${errorMsg}. key: error`)
|
|
23
28
|
}
|
|
24
|
-
if (config.output && config.
|
|
29
|
+
if (config.output && isInput(config.output)) {
|
|
25
30
|
throw new Error(`${errorMsg}. key: output`)
|
|
26
31
|
}
|
|
27
|
-
if (Object.values(config.outputs).some((value) => value.some(
|
|
32
|
+
if (Object.values(config.outputs).some((value) => value.some(isInput))) {
|
|
28
33
|
throw new Error(`${errorMsg}. key: outputs`)
|
|
29
34
|
}
|
|
35
|
+
// The same data-node id reused across the output-side targets (output / error / outputs)
|
|
36
|
+
// also collides: generateCalulationMap spreads their node objects last-wins, so the later
|
|
37
|
+
// one silently overwrites the earlier wiring (e.g. error == output drops the error change
|
|
38
|
+
// link). The checks above only compare against `inputs`, so guard the output-side pairs
|
|
39
|
+
// too. (The same id appearing in multiple `outputs` entries is a supported last-wins case
|
|
40
|
+
// and stays allowed.)
|
|
41
|
+
if (config.error && config.output && config.error === config.output) {
|
|
42
|
+
throw new Error(`${errorMsg}. key: error/output`)
|
|
43
|
+
}
|
|
44
|
+
const outputsIds = Object.values(config.outputs).flat()
|
|
45
|
+
if (config.output && outputsIds.includes(config.output)) {
|
|
46
|
+
throw new Error(`${errorMsg}. key: output/outputs`)
|
|
47
|
+
}
|
|
48
|
+
if (config.error && outputsIds.includes(config.error)) {
|
|
49
|
+
throw new Error(`${errorMsg}. key: error/outputs`)
|
|
50
|
+
}
|
|
30
51
|
}
|
|
31
52
|
|
|
32
53
|
const padding = 15
|
|
33
54
|
const layerXInterval = 300
|
|
34
55
|
const layerYInterval = 150
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
// `calcId` (the owning script calc's stable id) seeds every derived command-node id, so an
|
|
58
|
+
// unchanged calc recompiles to identical ids and editing one calc never shifts another's —
|
|
59
|
+
// keeping the compiled config byte-stable for change detection. See makeSeededId in utils/id.
|
|
60
|
+
export const generateCalulationMap = (config: ScriptConfig, calcId: string) => {
|
|
37
61
|
validateConfig(config)
|
|
38
|
-
const sandboxId =
|
|
39
|
-
const sandboxErrorId =
|
|
40
|
-
const sandboxResultId =
|
|
62
|
+
const sandboxId = makeSeededId('property_bank_command', `${calcId}:sandbox-run`)
|
|
63
|
+
const sandboxErrorId = makeSeededId('property_bank_command', `${calcId}:sandbox-error`)
|
|
64
|
+
const sandboxResultId = makeSeededId('property_bank_command', `${calcId}:sandbox-result`)
|
|
41
65
|
|
|
42
66
|
const inputs = Object.entries(config.inputs).reduce(
|
|
43
67
|
(acc, [key, value], index) => {
|
|
44
|
-
const commandId =
|
|
68
|
+
const commandId = makeSeededId('property_bank_command', `${calcId}:input:${key}`)
|
|
45
69
|
acc.map[key] = {
|
|
46
70
|
type: 'data-node',
|
|
47
71
|
properties: {},
|
|
@@ -123,7 +147,7 @@ export const generateCalulationMap = (config: ScriptConfig, opts?: { snapshotMod
|
|
|
123
147
|
let y = 0
|
|
124
148
|
const outputs = Object.entries(config.outputs).reduce(
|
|
125
149
|
(acc, [key, pbList], index) => {
|
|
126
|
-
const commandId =
|
|
150
|
+
const commandId = makeSeededId('property_bank_command', `${calcId}:output:${key}`)
|
|
127
151
|
acc.commandIdList.push(commandId)
|
|
128
152
|
acc.map[commandId] = {
|
|
129
153
|
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.51",
|
|
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.51",
|
|
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": "9a6ddecabc4a2e11fc6ae9a256de730cd3d744ca"
|
|
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
|
|