@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.
Files changed (160) hide show
  1. package/compile/action-name-map.ts +64 -0
  2. package/compile/index.ts +208 -19
  3. package/package.json +4 -3
  4. package/package.json.bak +4 -3
  5. package/skills/bricks-ctor/SKILL.md +21 -17
  6. package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
  7. package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +12 -0
  8. package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
  9. package/skills/bricks-ctor/references/buttress.md +245 -0
  10. package/skills/bricks-ctor/references/data-calculation.md +239 -0
  11. package/skills/bricks-ctor/references/simulator.md +131 -0
  12. package/skills/bricks-ctor/references/verification-toolchain.md +179 -0
  13. package/skills/bricks-design/SKILL.md +160 -45
  14. package/skills/bricks-design/references/architecture-truths.md +132 -0
  15. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  16. package/skills/bricks-design/references/design-critique.md +195 -0
  17. package/skills/bricks-design/references/design-languages.md +265 -0
  18. package/skills/bricks-design/references/performance.md +116 -0
  19. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  20. package/skills/bricks-design/references/translating-inputs.md +152 -0
  21. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  22. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  23. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  24. package/skills/bricks-design/references/workflow.md +134 -0
  25. package/skills/bricks-ux/SKILL.md +120 -0
  26. package/skills/bricks-ux/references/accessibility.md +162 -0
  27. package/skills/bricks-ux/references/flow-states.md +175 -0
  28. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  29. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  30. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  31. package/skills/bricks-ux/references/user-journey.md +168 -0
  32. package/skills/bricks-ux/references/ux-critique.md +256 -0
  33. package/tools/_git-author.ts +10 -2
  34. package/tools/_last-pushed-commit.ts +28 -0
  35. package/tools/_shell.ts +8 -1
  36. package/tools/deploy.ts +15 -0
  37. package/tools/mcp-tools/compile.ts +19 -9
  38. package/tools/mcp-tools/media.ts +4 -1
  39. package/tools/pull.ts +91 -16
  40. package/tools/push-config.ts +118 -0
  41. package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
  42. package/tools/simulator-preload.cjs +16 -0
  43. package/tools/{preview.ts → simulator.ts} +4 -4
  44. package/types/{animation.ts → animation.d.ts} +24 -8
  45. package/types/{automation.ts → automation.d.ts} +16 -20
  46. package/types/{brick-base.ts → brick-base.d.ts} +1 -1
  47. package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
  48. package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
  49. package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
  50. package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
  51. package/types/bricks/{Image.ts → Image.d.ts} +21 -9
  52. package/types/bricks/{Items.ts → Items.d.ts} +7 -7
  53. package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
  54. package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
  55. package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
  56. package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
  57. package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
  58. package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
  59. package/types/bricks/Scene3D.d.ts +676 -0
  60. package/types/bricks/{Sketch.ts → Sketch.d.ts} +6 -6
  61. package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
  62. package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
  63. package/types/bricks/{Text.ts → Text.d.ts} +9 -9
  64. package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
  65. package/types/bricks/{Video.ts → Video.d.ts} +12 -12
  66. package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
  67. package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
  68. package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
  69. package/types/bricks/{index.ts → index.d.ts} +1 -0
  70. package/types/{common.ts → common.d.ts} +3 -6
  71. package/types/data-calc-command/base.d.ts +57 -0
  72. package/types/data-calc-command/collection.d.ts +418 -0
  73. package/types/data-calc-command/color.d.ts +432 -0
  74. package/types/data-calc-command/constant.d.ts +50 -0
  75. package/types/data-calc-command/datetime.d.ts +147 -0
  76. package/types/data-calc-command/file.d.ts +129 -0
  77. package/types/data-calc-command/index.d.ts +13 -0
  78. package/types/data-calc-command/iteratee.d.ts +23 -0
  79. package/types/data-calc-command/logictype.d.ts +190 -0
  80. package/types/data-calc-command/math.d.ts +275 -0
  81. package/types/data-calc-command/object.d.ts +119 -0
  82. package/types/data-calc-command/sandbox.d.ts +66 -0
  83. package/types/data-calc-command/string.d.ts +407 -0
  84. package/types/{data-calc.ts → data-calc.d.ts} +1 -0
  85. package/types/{data.ts → data.d.ts} +4 -2
  86. package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
  87. package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
  88. package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
  89. package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
  90. package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
  91. package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
  92. package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
  93. package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
  94. package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
  95. package/types/{subspace.ts → subspace.d.ts} +1 -1
  96. package/utils/data.ts +2 -2
  97. package/utils/event-props.ts +17 -0
  98. package/utils/id.ts +78 -27
  99. package/skills/bricks-ctor/rules/buttress.md +0 -156
  100. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  101. package/skills/bricks-design/LICENSE.txt +0 -180
  102. package/types/data-calc-command.ts +0 -7005
  103. /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
  104. /package/skills/bricks-ctor/{rules → references}/media-flow.md +0 -0
  105. /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
  106. /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
  107. /package/types/{canvas.ts → canvas.d.ts} +0 -0
  108. /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
  109. /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
  110. /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
  111. /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
  112. /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
  113. /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
  114. /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
  115. /package/types/generators/{File.ts → File.d.ts} +0 -0
  116. /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
  117. /package/types/generators/{Http.ts → Http.d.ts} +0 -0
  118. /package/types/generators/{HttpServer.ts → HttpServer.d.ts} +0 -0
  119. /package/types/generators/{Information.ts → Information.d.ts} +0 -0
  120. /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
  121. /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
  122. /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
  123. /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
  124. /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
  125. /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
  126. /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
  127. /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
  128. /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
  129. /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
  130. /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
  131. /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
  132. /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
  133. /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
  134. /package/types/generators/{Question.ts → Question.d.ts} +0 -0
  135. /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
  136. /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
  137. /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
  138. /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
  139. /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
  140. /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
  141. /package/types/generators/{Step.ts → Step.d.ts} +0 -0
  142. /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
  143. /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
  144. /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
  145. /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
  146. /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
  147. /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
  148. /package/types/generators/{Tick.ts → Tick.d.ts} +0 -0
  149. /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
  150. /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
  151. /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
  152. /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
  153. /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
  154. /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
  155. /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
  156. /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
  157. /package/types/generators/{index.ts → index.d.ts} +0 -0
  158. /package/types/{index.ts → index.d.ts} +0 -0
  159. /package/types/{switch.ts → switch.d.ts} +0 -0
  160. /package/types/{system.ts → system.d.ts} +0 -0
@@ -350,6 +350,63 @@ export const templateActionNameMap = {
350
350
  strokeWidth: 'BRICK_SKETCH_STROKE_WIDTH',
351
351
  },
352
352
  },
353
+ BRICK_SCENE_3D: {
354
+ BRICK_SCENE_3D_ADD_OBJECT: {
355
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
356
+ objectType: 'BRICK_SCENE_3D_OBJECT_TYPE',
357
+ objectUrl: 'BRICK_SCENE_3D_OBJECT_URL',
358
+ objectMd5: 'BRICK_SCENE_3D_OBJECT_MD5',
359
+ objectPosition: 'BRICK_SCENE_3D_OBJECT_POSITION',
360
+ objectRotation: 'BRICK_SCENE_3D_OBJECT_ROTATION',
361
+ objectScale: 'BRICK_SCENE_3D_OBJECT_SCALE',
362
+ objectColor: 'BRICK_SCENE_3D_OBJECT_COLOR',
363
+ },
364
+ BRICK_SCENE_3D_REMOVE_OBJECT: {
365
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
366
+ },
367
+ BRICK_SCENE_3D_UPDATE_OBJECT: {
368
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
369
+ objectPosition: 'BRICK_SCENE_3D_OBJECT_POSITION',
370
+ objectRotation: 'BRICK_SCENE_3D_OBJECT_ROTATION',
371
+ objectScale: 'BRICK_SCENE_3D_OBJECT_SCALE',
372
+ objectVisible: 'BRICK_SCENE_3D_OBJECT_VISIBLE',
373
+ objectColor: 'BRICK_SCENE_3D_OBJECT_COLOR',
374
+ objectNodes: 'BRICK_SCENE_3D_OBJECT_NODES',
375
+ },
376
+ BRICK_SCENE_3D_SET_CAMERA: {
377
+ cameraPosition: 'BRICK_SCENE_3D_CAMERA_POSITION',
378
+ cameraTarget: 'BRICK_SCENE_3D_CAMERA_TARGET',
379
+ cameraFov: 'BRICK_SCENE_3D_CAMERA_FOV',
380
+ cameraAnimateMs: 'BRICK_SCENE_3D_CAMERA_ANIMATE_MS',
381
+ },
382
+ BRICK_SCENE_3D_LOOK_AT: {
383
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
384
+ },
385
+ BRICK_SCENE_3D_PLAY_ANIMATION: {
386
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
387
+ animationName: 'BRICK_SCENE_3D_ANIMATION_NAME',
388
+ animationLoop: 'BRICK_SCENE_3D_ANIMATION_LOOP',
389
+ animationSpeed: 'BRICK_SCENE_3D_ANIMATION_SPEED',
390
+ },
391
+ BRICK_SCENE_3D_STOP_ANIMATION: {
392
+ objectId: 'BRICK_SCENE_3D_OBJECT_ID',
393
+ animationName: 'BRICK_SCENE_3D_ANIMATION_NAME',
394
+ },
395
+ BRICK_SCENE_3D_SET_BACKGROUND: {
396
+ backgroundColor: 'BRICK_SCENE_3D_BACKGROUND_COLOR',
397
+ backgroundHdrUrl: 'BRICK_SCENE_3D_BACKGROUND_HDR_URL',
398
+ backgroundMd5: 'BRICK_SCENE_3D_BACKGROUND_MD5',
399
+ },
400
+ BRICK_SCENE_3D_SET_CONTROLS: {
401
+ controlsEnabled: 'BRICK_SCENE_3D_CONTROLS_ENABLED',
402
+ controlsAutoRotate: 'BRICK_SCENE_3D_CONTROLS_AUTO_ROTATE',
403
+ controlsAutoRotateSpeed: 'BRICK_SCENE_3D_CONTROLS_AUTO_ROTATE_SPEED',
404
+ },
405
+ BRICK_SCENE_3D_SCREENSHOT: {
406
+ screenshotFormat: 'BRICK_SCENE_3D_SCREENSHOT_FORMAT',
407
+ screenshotQuality: 'BRICK_SCENE_3D_SCREENSHOT_QUALITY',
408
+ },
409
+ },
353
410
 
354
411
  GENERATOR_FILE: {
355
412
  GENERATOR_FILE_READ_CONTENT: {
@@ -782,6 +839,11 @@ export const templateActionNameMap = {
782
839
  seed: 'GENERATOR_LLM_SEED',
783
840
  typicalP: 'GENERATOR_LLM_TYPICAL_P',
784
841
  ignoreEos: 'GENERATOR_LLM_IGNORE_EOS',
842
+ mtpSpeculativeDecoding: 'GENERATOR_LLM_MTP_SPECULATIVE_DECODING',
843
+ mtpDraftTokens: 'GENERATOR_LLM_MTP_DRAFT_TOKENS',
844
+ mtpDraftMinTokens: 'GENERATOR_LLM_MTP_DRAFT_MIN_TOKENS',
845
+ mtpDraftMinProbability: 'GENERATOR_LLM_MTP_DRAFT_MIN_PROBABILITY',
846
+ mtpDraftSplitProbability: 'GENERATOR_LLM_MTP_DRAFT_SPLIT_PROBABILITY',
785
847
  functionCallEnabled: 'GENERATOR_LLM_FUNCTION_CALL_ENABLED',
786
848
  functionCallSchema: 'GENERATOR_LLM_FUNCTION_CALL_SCHEMA',
787
849
  },
@@ -894,10 +956,12 @@ export const templateActionNameMap = {
894
956
  GENERATOR_APPLE_STT_TRANSCRIBE_FILE: {
895
957
  fileUrl: 'GENERATOR_APPLE_STT_FILE_URL',
896
958
  language: 'GENERATOR_APPLE_STT_LANGUAGE',
959
+ contextualStrings: 'GENERATOR_APPLE_STT_CONTEXTUAL_STRINGS',
897
960
  },
898
961
  GENERATOR_APPLE_STT_TRANSCRIBE_DATA: {
899
962
  data: 'GENERATOR_APPLE_STT_DATA',
900
963
  language: 'GENERATOR_APPLE_STT_LANGUAGE',
964
+ contextualStrings: 'GENERATOR_APPLE_STT_CONTEXTUAL_STRINGS',
901
965
  },
902
966
  },
903
967
  GENERATOR_APPLE_TTS: {
package/compile/index.ts CHANGED
@@ -154,11 +154,13 @@ const basicAnimationEvents = ['show', 'standby', 'breatheStart']
154
154
 
155
155
  const compileAnimations = (
156
156
  templateKey: string,
157
- animations: { [key: string]: Animation },
157
+ animations: { [key: string]: Animation | (() => Animation) },
158
158
  errorReference: string,
159
159
  ) =>
160
160
  Object.entries(animations).reduce((acc, [key, animation]) => {
161
- const animationId = assertEntryId(animation?.id, 'ANIMATION', errorReference)
161
+ // Animation events accept either a direct Animation or a getter; unwrap.
162
+ const resolved = typeof animation === 'function' ? animation() : animation
163
+ const animationId = assertEntryId(resolved?.id, 'ANIMATION', errorReference)
162
164
  acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
163
165
  `ANIMATION#${animationId}`
164
166
  return acc
@@ -301,6 +303,162 @@ const animationTypeMap = {
301
303
  AnimationTimingConfig: 'timing',
302
304
  AnimationSpringConfig: 'spring',
303
305
  AnimationDecayConfig: 'decay',
306
+ } as const
307
+
308
+ type CompiledAnimationType = (typeof animationTypeMap)[keyof typeof animationTypeMap]
309
+ type WarningMetadata = Record<string, unknown>
310
+
311
+ const animationProperties = new Set([
312
+ 'transform.translateX',
313
+ 'transform.translateY',
314
+ 'transform.scale',
315
+ 'transform.scaleX',
316
+ 'transform.scaleY',
317
+ 'transform.rotate',
318
+ 'transform.rotateX',
319
+ 'transform.rotateY',
320
+ 'opacity',
321
+ ])
322
+
323
+ const animationComposeTypes = new Set(['parallel', 'sequence'])
324
+ const springConfigFamilies = [
325
+ ['stiffness', 'damping', 'mass'],
326
+ ['tension', 'friction'],
327
+ ['bounciness', 'speed'],
328
+ ]
329
+ const springConfigFamilyKeys = new Set(springConfigFamilies.flat())
330
+
331
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
332
+ Boolean(value) && typeof value === 'object' && !Array.isArray(value)
333
+
334
+ const hasDefinedConfigValue = (config: Record<string, unknown>, key: string) =>
335
+ config[key] !== undefined
336
+
337
+ const assertConfigValue = (
338
+ config: Record<string, unknown>,
339
+ key: string,
340
+ errorReference: string,
341
+ ) => {
342
+ if (!hasDefinedConfigValue(config, key)) {
343
+ throw new Error(`Invalid animation config ${errorReference}: missing "${key}"`)
344
+ }
345
+ }
346
+
347
+ const assertAnimationProperty = (property: unknown, errorReference: string) => {
348
+ if (typeof property !== 'string' || !animationProperties.has(property)) {
349
+ throw new Error(
350
+ `Invalid animation property${errorReference ? ` ${errorReference}` : ''}: ${String(
351
+ property,
352
+ )}`,
353
+ )
354
+ }
355
+ return property
356
+ }
357
+
358
+ const getAnimationType = (config: unknown, errorReference: string): CompiledAnimationType => {
359
+ if (!isRecord(config)) {
360
+ throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
361
+ }
362
+
363
+ const animationType = animationTypeMap[config.__type as keyof typeof animationTypeMap]
364
+ if (!animationType) {
365
+ throw new Error(`Invalid animation config type ${errorReference}: ${String(config.__type)}`)
366
+ }
367
+ return animationType
368
+ }
369
+
370
+ const assertAnimationComposeType = (composeType: unknown, errorReference: string) => {
371
+ if (typeof composeType !== 'string' || !animationComposeTypes.has(composeType)) {
372
+ throw new Error(`Invalid animation compose type ${errorReference}: ${String(composeType)}`)
373
+ }
374
+ return composeType
375
+ }
376
+
377
+ const pickDefinedConfigValues = (config: Record<string, unknown>, keys: string[]) =>
378
+ keys.reduce((acc, key) => {
379
+ if (hasDefinedConfigValue(config, key)) acc[key] = config[key]
380
+ return acc
381
+ }, {})
382
+
383
+ const getDefinedConfigKeys = (config: Record<string, unknown>, keys: string[]) =>
384
+ keys.filter((key) => hasDefinedConfigValue(config, key))
385
+
386
+ const formatWarningMetadata = (metadata: WarningMetadata = {}) =>
387
+ Object.entries(metadata)
388
+ .filter(([, value]) => value !== undefined && value !== null && value !== '')
389
+ .map(([key, value]) => `${key}: ${String(value)}`)
390
+ .join(', ')
391
+
392
+ const formatWarningReference = (metadata?: WarningMetadata) => {
393
+ const metadataText = formatWarningMetadata(metadata)
394
+ return metadataText ? ` [${metadataText}]` : ''
395
+ }
396
+
397
+ const normalizeSpringConfig = (
398
+ config: Record<string, unknown>,
399
+ errorReference: string,
400
+ warningMetadata?: WarningMetadata,
401
+ ): Record<string, unknown> => {
402
+ assertConfigValue(config, 'toValue', errorReference)
403
+
404
+ const usedFamilies = springConfigFamilies.filter((keys) =>
405
+ keys.some((key) => hasDefinedConfigValue(config, key)),
406
+ )
407
+ if (usedFamilies.length <= 1) return config
408
+
409
+ const configWithoutSpringFamily = Object.entries(config).reduce((acc, [key, value]) => {
410
+ if (!springConfigFamilyKeys.has(key)) acc[key] = value
411
+ return acc
412
+ }, {})
413
+
414
+ // Match runtime normalization: physical spring values are most explicit,
415
+ // otherwise preserve BRICKS' historical tension/friction controls.
416
+ const resolvedFamily =
417
+ usedFamilies.find((keys) => keys.includes('stiffness')) ||
418
+ usedFamilies.find((keys) => keys.includes('tension')) ||
419
+ usedFamilies[0]
420
+ const resolvedFamilyKeys = getDefinedConfigKeys(config, resolvedFamily)
421
+ const droppedFamilyKeys = usedFamilies
422
+ .filter((keys) => keys !== resolvedFamily)
423
+ .flatMap((keys) => getDefinedConfigKeys(config, keys))
424
+
425
+ console.warn(
426
+ `[Warning] Resolved animation spring config${formatWarningReference(
427
+ warningMetadata,
428
+ )}: using ${resolvedFamilyKeys.join('/')}, dropping ${droppedFamilyKeys.join('/')}`,
429
+ )
430
+
431
+ return {
432
+ ...configWithoutSpringFamily,
433
+ ...pickDefinedConfigValues(config, resolvedFamily),
434
+ }
435
+ }
436
+
437
+ const compileAnimationConfig = (
438
+ animationType: CompiledAnimationType,
439
+ config: unknown,
440
+ errorReference: string,
441
+ warningMetadata?: WarningMetadata,
442
+ ) => {
443
+ if (!isRecord(config)) {
444
+ throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
445
+ }
446
+
447
+ const compiledConfig = compileProperty(omit(config, '__type'), errorReference)
448
+
449
+ if (!isRecord(compiledConfig)) {
450
+ throw new Error(`Invalid animation config ${errorReference}: config must compile to an object`)
451
+ }
452
+
453
+ if (animationType === 'timing') {
454
+ assertConfigValue(compiledConfig, 'toValue', errorReference)
455
+ } else if (animationType === 'spring') {
456
+ return normalizeSpringConfig(compiledConfig, errorReference, warningMetadata)
457
+ } else if (animationType === 'decay') {
458
+ assertConfigValue(compiledConfig, 'velocity', errorReference)
459
+ }
460
+
461
+ return compiledConfig
304
462
  }
305
463
 
306
464
  const compileFrame = (frame: Canvas['items'][number]['frame']) => ({
@@ -327,6 +485,8 @@ const preloadTypes = [
327
485
  'ggml-model-asset',
328
486
  'gguf-model-asset',
329
487
  'binary-asset',
488
+ 'mlx-model-asset',
489
+ 'scene3d-objects',
330
490
  ]
331
491
 
332
492
  const compileKind = (kind: Data['kind']) => {
@@ -353,9 +513,10 @@ const compileKind = (kind: Data['kind']) => {
353
513
  }
354
514
 
355
515
  const compileRemoteUpdate = (remoteUpdate: Data['remoteUpdate']) => {
356
- if (!remoteUpdate) return {}
357
- if (remoteUpdate.type === 'auto') return { enable_remote_update: true }
516
+ if (!remoteUpdate) return { bank_type: 'none' }
517
+ if (remoteUpdate.type === 'auto') return { bank_type: 'create', enable_remote_update: true }
358
518
  return {
519
+ bank_type: remoteUpdate.type === 'device-specific' ? 'create-device-specific' : 'global',
359
520
  enable_remote_update: true,
360
521
  ...(remoteUpdate.type === 'device-specific' ? { use_remote_id_prefix: true } : {}),
361
522
  ...(remoteUpdate.type === 'global-data' ? { global_remote_update_prop: remoteUpdate.id } : {}),
@@ -485,6 +646,7 @@ const compileAutomationTest = (
485
646
 
486
647
  return {
487
648
  id: testId,
649
+ alias: test.alias,
488
650
  title: test.title,
489
651
  hide_short_ref: test.hideShortRef,
490
652
  timeout: test.timeout,
@@ -629,39 +791,63 @@ export const compile = async (app: Application) => {
629
791
  `(animation index: ${animationIndex}, subspace: ${subspaceId})`,
630
792
  )
631
793
 
632
- if (animation.__typename === 'Animation') {
794
+ const animationTypename = animation.__typename
795
+ if (animationTypename === 'Animation') {
633
796
  const animationDef = animation as AnimationDef
797
+ const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
798
+ const animationWarningMetadata = {
799
+ animationIndex,
800
+ animationTitle: animationDef.title,
801
+ animationAlias: animationDef.alias,
802
+ animationProperty: animationDef.property,
803
+ subspaceIndex,
804
+ subspaceTitle: subspace.title,
805
+ }
806
+ const animationType = getAnimationType(animationDef.config, animationErrorReference)
634
807
  map[animationId] = {
635
808
  alias: animationDef.alias,
636
809
  title: animationDef.title,
637
810
  description: animationDef.description,
638
811
  hide_short_ref: animationDef.hideShortRef,
639
812
  animationRunType: animationDef.runType,
640
- property: animationDef.property,
641
- type: animationTypeMap[animationDef.config.__type],
642
- config: compileProperty(
643
- omit(animationDef.config, '__type'),
644
- `(animation: ${animationId}, subspace ${subspaceId})`,
813
+ property: assertAnimationProperty(animationDef.property, animationErrorReference),
814
+ type: animationType,
815
+ config: compileAnimationConfig(
816
+ animationType,
817
+ animationDef.config,
818
+ animationErrorReference,
819
+ animationWarningMetadata,
645
820
  ),
646
821
  }
647
- } else if (animation.__typename === 'AnimationCompose') {
822
+ } else if (animationTypename === 'AnimationCompose') {
648
823
  const animationDef = animation as AnimationComposeDef
824
+ const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
649
825
  map[animationId] = {
650
826
  alias: animationDef.alias,
651
827
  title: animationDef.title,
652
828
  description: animationDef.description,
653
829
  hide_short_ref: animationDef.hideShortRef,
654
830
  animationRunType: animationDef.runType,
655
- compose_type: animationDef.composeType,
831
+ compose_type: assertAnimationComposeType(
832
+ animationDef.composeType,
833
+ animationErrorReference,
834
+ ),
656
835
  item_list: animationDef.items.map((item, index) => {
657
836
  const innerAnimation = item()
658
- if (!innerAnimation?.id)
659
- throw new Error(
660
- `Invalid animation index: ${index} (animation: ${innerAnimation.id}, subspace ${subspaceId})`,
661
- )
662
- return { animation_id: innerAnimation.id }
837
+ const innerAnimationId = assertEntryId(
838
+ innerAnimation?.id,
839
+ 'ANIMATION',
840
+ `(animation item index: ${index}, animation: ${animationId}, subspace ${subspaceId})`,
841
+ )
842
+ return { animation_id: innerAnimationId }
663
843
  }),
664
844
  }
845
+ } else {
846
+ throw new Error(
847
+ `Invalid animation typename (animation: ${animationId}, subspace ${subspaceId}): ${String(
848
+ animationTypename,
849
+ )}`,
850
+ )
665
851
  }
666
852
  return map
667
853
  }, {}),
@@ -1009,7 +1195,7 @@ export const compile = async (app: Application) => {
1009
1195
  ...compileRemoteUpdate(data.remoteUpdate),
1010
1196
  routing: data.routing,
1011
1197
  schema: data.schema,
1012
- type: data.type,
1198
+ type: data.type === 'boolean' ? 'bool' : data.type,
1013
1199
  ...compileKind(data.kind),
1014
1200
  value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
1015
1201
  event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
@@ -1029,6 +1215,7 @@ export const compile = async (app: Application) => {
1029
1215
  )
1030
1216
 
1031
1217
  const calc: any = {
1218
+ alias: dataCalc.alias,
1032
1219
  title: dataCalc.title,
1033
1220
  description: dataCalc.description,
1034
1221
  hide_short_ref: dataCalc.hideShortRef,
@@ -1274,5 +1461,7 @@ export const compile = async (app: Application) => {
1274
1461
 
1275
1462
  export const checkConfig = async (configPath: string) => {
1276
1463
  const { sh } = await import('../tools/_shell')
1277
- await sh`bricks app check-config ${configPath}`
1464
+ // --validate-automation surfaces broken automation_map / test_map refs early,
1465
+ // which catches agent-authored automations that reference deleted bricks.
1466
+ await sh`bricks app check-config --validate-automation ${configPath}`
1278
1467
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fugood/bricks-project",
3
- "version": "2.25.0-beta.4",
3
+ "version": "2.25.0-beta.41",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "typecheck": "tsc --noEmit",
7
7
  "build": "bun scripts/build.js"
8
8
  },
9
9
  "dependencies": {
10
- "@fugood/bricks-cli": "^2.25.0-beta.4",
10
+ "@fugood/bricks-cli": "^2.25.0-beta.41",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -24,5 +24,6 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "oxfmt": "^0.36.0"
27
- }
27
+ },
28
+ "gitHead": "4ad8588eed6f0161b0aa66d863a36e6c623c4a83"
28
29
  }
package/package.json.bak CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fugood/bricks-ctor",
3
- "version": "2.25.0-beta.4",
3
+ "version": "2.25.0-beta.41",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "typecheck": "tsc --noEmit",
7
7
  "build": "bun scripts/build.js"
8
8
  },
9
9
  "dependencies": {
10
- "@fugood/bricks-cli": "^2.25.0-beta.4",
10
+ "@fugood/bricks-cli": "^2.25.0-beta.41",
11
11
  "@huggingface/gguf": "^0.3.2",
12
12
  "@iarna/toml": "^3.0.0",
13
13
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -24,5 +24,6 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "oxfmt": "^0.36.0"
27
- }
27
+ },
28
+ "gitHead": "4ad8588eed6f0161b0aa66d863a36e6c623c4a83"
28
29
  }
@@ -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, and Buttress (remote inference). Triggers on multi-device sync, cloud data, media assets, AI offloading, E2E testing, or canvas transitions.
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,26 @@ This skill covers advanced BRICKS features not in the main project instructions.
11
11
 
12
12
  | Rule | Description |
13
13
  |------|-------------|
14
- | [Architecture Patterns](rules/architecture-patterns.md) | **Read first** — decompose flows and select patterns |
15
- | [Animation](rules/animation.md) | Animation system for brick transforms and opacity |
16
- | [Standby Transition](rules/standby-transition.md) | Canvas enter/exit animations |
17
- | [Automations](rules/automations.md) | E2E testing and scheduled tasks |
18
- | [Data Calculation](rules/data-calculation.md) | JS sandbox libraries (25+ available) |
19
- | [Local Sync](rules/local-sync.md) | LAN device synchronization |
20
- | [Remote Data Bank](rules/remote-data-bank.md) | Cloud data sync and API access |
21
- | [Media Flow](rules/media-flow.md) | Media asset management |
22
- | [Buttress](rules/buttress.md) | Remote inference for AI generators |
14
+ | [Architecture Patterns](references/architecture-patterns.md) | **Read first** — decompose flows and select patterns |
15
+ | [Animation](references/animation.md) | Animation system for brick transforms and opacity |
16
+ | [Standby Transition](references/standby-transition.md) | Canvas enter/exit animations |
17
+ | [Automations](references/automations.md) | E2E testing and scheduled tasks |
18
+ | [Data Calculation](references/data-calculation.md) | Wiring contract, trigger semantics, JS sandbox (25+ libraries) |
19
+ | [Local Sync](references/local-sync.md) | LAN device synchronization |
20
+ | [Remote Data Bank](references/remote-data-bank.md) | Cloud data sync and API access |
21
+ | [Media Flow](references/media-flow.md) | Media asset management |
22
+ | [Buttress](references/buttress.md) | Remote inference for AI generators |
23
+ | [Verification Toolchain](references/verification-toolchain.md) | Definition of done, compile, preview tool selection, on-device DevTools, Path 1/2/3 decision rule |
24
+ | [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
25
 
24
26
  ## Quick Reference
25
27
 
26
- - **Complex flows**: See [Architecture Patterns](rules/architecture-patterns.md) for decomposing multi-step workflows
27
- - **Multi-device**: See [Local Sync](rules/local-sync.md) for LAN coordination
28
- - **Cloud data**: See [Remote Data Bank](rules/remote-data-bank.md) for sync and API access
29
- - **Media assets**: See [Media Flow](rules/media-flow.md) for centralized asset management
30
- - **AI offloading**: See [Buttress](rules/buttress.md) for GPU server delegation
31
- - **E2E testing**: See [Automations](rules/automations.md) for test automation
32
- - **Enter animations**: See [Standby Transition](rules/standby-transition.md) for canvas transitions
28
+ - **Complex flows**: See [Architecture Patterns](references/architecture-patterns.md) for decomposing multi-step workflows
29
+ - **Multi-device**: See [Local Sync](references/local-sync.md) for LAN coordination
30
+ - **Cloud data**: See [Remote Data Bank](references/remote-data-bank.md) for sync and API access
31
+ - **Media assets**: See [Media Flow](references/media-flow.md) for centralized asset management
32
+ - **AI offloading**: See [Buttress](references/buttress.md) for GPU server delegation
33
+ - **E2E testing**: See [Automations](references/automations.md) for test automation
34
+ - **Enter animations**: See [Standby Transition](references/standby-transition.md) for canvas transitions
35
+ - **Verification before done**: See [Verification Toolchain](references/verification-toolchain.md) for the definition-of-done gate and Path 1/2/3 decision rule
36
+ - **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,6 +26,8 @@ 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
@@ -67,3 +69,13 @@ Only actual data derivation maps to Data Calculation:
67
69
 
68
70
  ### Step 4: Wire with Event Action Chains
69
71
  Connect the pieces through events on generators and bricks.
72
+
73
+ ## Recipe: user-driven state machine (calculator, form wizard, picker)
74
+
75
+ A brief like "state vars X, Y, Z; button A updates X from X+Y; button B resets" is a state machine. The shape:
76
+
77
+ - Each state variable is its own `Data` entity. Displays read it via `linkData(() => data.dFoo)`.
78
+ - 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.
79
+ - Use `Data Calculation` only for reusable pure derivations referenced as inputs to those expressions.
80
+
81
+ 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