m0saic 0.2.1 → 0.3.0

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 (292) hide show
  1. package/LICENSE +22 -7
  2. package/README.md +23 -5
  3. package/dist/browseE2e.js +1 -1
  4. package/dist/connectionsFromEnv.js +1 -1
  5. package/dist/e2eTemplateVariants.js +1 -1
  6. package/dist/frontDoor.js +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/inlineDsl.js +1 -1
  9. package/dist/mcp/bridgeClient.js +1 -0
  10. package/dist/mcp/server.js +1 -0
  11. package/dist/mcp/tools.js +1 -0
  12. package/dist/mosaicxRunner.js +1 -1
  13. package/dist/outputPattern.js +1 -1
  14. package/dist/showcaseFeed.js +1 -1
  15. package/dist/templateDoctor.js +1 -1
  16. package/dist/utils/checkFeasibility.js +1 -1
  17. package/dist/utils/collectRenderErrors.js +1 -1
  18. package/dist/utils/enrichImageAlphaFromDoc.js +1 -1
  19. package/dist/utils/ensureFfmpeg.js +1 -1
  20. package/dist/utils/extractLayoutMeta.js +1 -1
  21. package/dist/utils/failureHints.js +1 -1
  22. package/dist/utils/ffmpegProbe.js +1 -1
  23. package/dist/utils/formatM0saicForCli.js +1 -1
  24. package/dist/utils/hashFile.js +1 -1
  25. package/dist/utils/helpGroups.js +1 -1
  26. package/dist/utils/installContext.js +1 -0
  27. package/dist/utils/invocation.js +1 -1
  28. package/dist/utils/licenseActivation.js +1 -0
  29. package/dist/utils/licenseActivationWorker.js +1 -0
  30. package/dist/utils/mosaicSpinner.js +1 -1
  31. package/dist/utils/parseDimensions.js +1 -1
  32. package/dist/utils/pathInfo.js +1 -1
  33. package/dist/utils/readJsonArg.js +1 -1
  34. package/dist/utils/renderOutcome.js +1 -1
  35. package/dist/utils/renderReport.js +1 -1
  36. package/dist/utils/renderSignals.js +1 -1
  37. package/dist/utils/renderWorkspace.js +1 -1
  38. package/dist/utils/reportOutputFormat.js +1 -0
  39. package/dist/utils/setupFfmpeg.js +1 -1
  40. package/dist/utils/telemetry.js +1 -1
  41. package/dist/utils/telemetryFlushWorker.js +1 -1
  42. package/dist/utils/telemetryUpstream.js +1 -1
  43. package/dist/utils/telemetryUsage.js +1 -1
  44. package/dist/utils/toolchainConfig.js +1 -1
  45. package/dist/utils/tutorialDuration.js +1 -1
  46. package/dist/utils/updateCheck.js +1 -1
  47. package/dist/utils/updateCheckWorker.js +1 -1
  48. package/dist/utils/validateSourceCounts.js +1 -1
  49. package/dist/utils/versions.js +1 -1
  50. package/dist/webCustomizeLink.js +1 -1
  51. package/node_modules/@m0saic/community-m/NOTICE +9 -0
  52. package/node_modules/@m0saic/community-m/release.json +2 -2
  53. package/node_modules/@m0saic/core/dist/core/buildChildMosaicSource.js +1 -1
  54. package/node_modules/@m0saic/core/dist/core/buildChunkCommands.js +1 -1
  55. package/node_modules/@m0saic/core/dist/core/buildEncodeCommands.js +1 -1
  56. package/node_modules/@m0saic/core/dist/core/buildLavfiSource.js +1 -1
  57. package/node_modules/@m0saic/core/dist/core/buildMediaSource.js +1 -1
  58. package/node_modules/@m0saic/core/dist/core/buildMosaicNode.js +1 -1
  59. package/node_modules/@m0saic/core/dist/core/buildMosaicPlanFromFile.js +1 -1
  60. package/node_modules/@m0saic/core/dist/core/buildSvgTextSource.js +1 -1
  61. package/node_modules/@m0saic/core/dist/core/buildTextRenderCommand.js +1 -1
  62. package/node_modules/@m0saic/core/dist/core/collectRefTargets.js +1 -1
  63. package/node_modules/@m0saic/core/dist/core/index.js +1 -1
  64. package/node_modules/@m0saic/core/dist/core/maskCacheDir.js +1 -1
  65. package/node_modules/@m0saic/core/dist/core/maskWorkerBridge.js +1 -1
  66. package/node_modules/@m0saic/core/dist/core/playbackPlaySpeed.js +1 -1
  67. package/node_modules/@m0saic/core/dist/core/prepareProps.js +1 -1
  68. package/node_modules/@m0saic/core/dist/core/primaryOutput.js +1 -1
  69. package/node_modules/@m0saic/core/dist/core/rasterizeMaskPng.js +1 -1
  70. package/node_modules/@m0saic/core/dist/core/rasterizeSvgMediaPng.js +1 -1
  71. package/node_modules/@m0saic/core/dist/core/refTargetTap.js +1 -1
  72. package/node_modules/@m0saic/core/dist/core/resolveMasks.js +1 -1
  73. package/node_modules/@m0saic/core/dist/core/resolveMasksSync.js +1 -1
  74. package/node_modules/@m0saic/core/dist/core/resolveStrokeRingsSync.js +1 -1
  75. package/node_modules/@m0saic/core/dist/core/roundedRectMask.js +1 -1
  76. package/node_modules/@m0saic/core/dist/core/strokeRing.js +1 -1
  77. package/node_modules/@m0saic/core/dist/core/svgRaster.js +1 -1
  78. package/node_modules/@m0saic/core/dist/defaults.js +1 -1
  79. package/node_modules/@m0saic/core/dist/ffmpeg/backgroundImageInput.js +1 -1
  80. package/node_modules/@m0saic/core/dist/ffmpeg/bufferGuard.js +1 -1
  81. package/node_modules/@m0saic/core/dist/ffmpeg/ffmpegCommands.js +1 -1
  82. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBackdropBlur.js +1 -1
  83. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBlur.js +1 -1
  84. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBorderRadius.js +1 -1
  85. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyCamera.js +1 -1
  86. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyChromaKey.js +1 -1
  87. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyFade.js +1 -1
  88. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyGrade.js +1 -1
  89. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyMask.js +1 -1
  90. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyNoise.js +1 -1
  91. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyPixelize.js +1 -1
  92. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyRotate.js +1 -1
  93. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyStroke.js +1 -1
  94. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyTileEffects.js +1 -1
  95. package/node_modules/@m0saic/core/dist/ffmpeg/filters/coverCrop.js +1 -1
  96. package/node_modules/@m0saic/core/dist/ffmpeg/filters/index.js +1 -1
  97. package/node_modules/@m0saic/core/dist/ffmpeg/filters/sourceRectCrop.js +1 -1
  98. package/node_modules/@m0saic/core/dist/ffmpeg/gridLowering.js +1 -1
  99. package/node_modules/@m0saic/core/dist/ffmpeg/index.js +1 -1
  100. package/node_modules/@m0saic/core/dist/ffmpeg/splitAvMux.js +1 -1
  101. package/node_modules/@m0saic/core/dist/ffmpeg/threadGuard.js +1 -1
  102. package/node_modules/@m0saic/core/dist/flatten/index.js +1 -1
  103. package/node_modules/@m0saic/core/dist/fs/index.js +1 -1
  104. package/node_modules/@m0saic/core/dist/fs/tempWorkspace.js +1 -1
  105. package/node_modules/@m0saic/core/dist/index.js +1 -1
  106. package/node_modules/@m0saic/core/dist/media/buildAssetManifestFromInputs.js +1 -1
  107. package/node_modules/@m0saic/core/dist/media/buildMediaRegistryFromInput.js +1 -1
  108. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromInvocationProps.js +1 -1
  109. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromMosaicRenderableFile.js +1 -1
  110. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromProps.js +1 -1
  111. package/node_modules/@m0saic/core/dist/media/index.js +1 -1
  112. package/node_modules/@m0saic/core/dist/media/internalMintAsset.js +1 -1
  113. package/node_modules/@m0saic/core/dist/media/remoteUrl.js +1 -1
  114. package/node_modules/@m0saic/core/dist/media/stageUnsafeFilenames.js +1 -1
  115. package/node_modules/@m0saic/core/dist/media/types.js +1 -1
  116. package/node_modules/@m0saic/core/dist/output/index.js +1 -1
  117. package/node_modules/@m0saic/core/dist/output/resolveOutputFormat.js +1 -1
  118. package/node_modules/@m0saic/core/dist/output/types.js +1 -1
  119. package/node_modules/@m0saic/core/dist/probe/cellLuminanceProbe.js +1 -1
  120. package/node_modules/@m0saic/core/dist/probe/extractSubtitleCues.js +1 -1
  121. package/node_modules/@m0saic/core/dist/probe/helpers.js +1 -1
  122. package/node_modules/@m0saic/core/dist/probe/index.js +1 -1
  123. package/node_modules/@m0saic/core/dist/probe/luminanceProbe.js +1 -1
  124. package/node_modules/@m0saic/core/dist/probe/probeMedia.js +1 -1
  125. package/node_modules/@m0saic/core/dist/probe/runFFprobe.js +1 -1
  126. package/node_modules/@m0saic/core/dist/probe/types.js +1 -1
  127. package/node_modules/@m0saic/core/dist/runtime/engineRedaction.js +1 -1
  128. package/node_modules/@m0saic/core/dist/runtime/ffmpegFailure.js +1 -1
  129. package/node_modules/@m0saic/core/dist/runtime/filterPreflight.js +1 -1
  130. package/node_modules/@m0saic/core/dist/runtime/hashFile.js +1 -1
  131. package/node_modules/@m0saic/core/dist/runtime/index.js +1 -1
  132. package/node_modules/@m0saic/core/dist/runtime/pathInfo.js +1 -1
  133. package/node_modules/@m0saic/core/dist/runtime/perfHeatmap.js +1 -1
  134. package/node_modules/@m0saic/core/dist/runtime/perfReport.js +1 -1
  135. package/node_modules/@m0saic/core/dist/runtime/pipelineM0p.js +1 -1
  136. package/node_modules/@m0saic/core/dist/runtime/renderCostModel.constants.js +1 -1
  137. package/node_modules/@m0saic/core/dist/runtime/renderCostModel.js +1 -1
  138. package/node_modules/@m0saic/core/dist/runtime/renderReport.js +1 -1
  139. package/node_modules/@m0saic/core/dist/runtime/renderSidecars.js +1 -1
  140. package/node_modules/@m0saic/core/dist/runtime/resolveMosaicx.js +1 -1
  141. package/node_modules/@m0saic/core/dist/runtime/runPlan.js +1 -1
  142. package/node_modules/@m0saic/core/dist/runtime/types.js +1 -1
  143. package/node_modules/@m0saic/core/dist/runtime/upstream.js +1 -1
  144. package/node_modules/@m0saic/core/dist/runtime/writeSidecars.js +1 -1
  145. package/node_modules/@m0saic/core/dist/types/chunk.js +1 -1
  146. package/node_modules/@m0saic/core/dist/types/index.js +1 -1
  147. package/node_modules/@m0saic/core/dist/types/mosaic-plan.js +1 -1
  148. package/node_modules/@m0saic/core/dist/utils/assertTiming.js +1 -1
  149. package/node_modules/@m0saic/core/dist/utils/command.js +1 -1
  150. package/node_modules/@m0saic/core/dist/utils/createEngineContext.js +1 -1
  151. package/node_modules/@m0saic/core/dist/utils/createTemplateRenderCache.js +1 -1
  152. package/node_modules/@m0saic/core/dist/utils/ffmpegColor.js +1 -1
  153. package/node_modules/@m0saic/core/dist/utils/formatDuration.js +1 -1
  154. package/node_modules/@m0saic/core/dist/utils/fsSafeId.js +1 -1
  155. package/node_modules/@m0saic/core/dist/utils/index.js +1 -1
  156. package/node_modules/@m0saic/core/dist/utils/lowerRenderableToMosaicDocument.js +1 -1
  157. package/node_modules/@m0saic/core/dist/utils/movSafeCodec.js +1 -1
  158. package/node_modules/@m0saic/core/dist/utils/resolveBoxFrac.js +1 -1
  159. package/node_modules/@m0saic/core/dist/validate/index.js +1 -1
  160. package/node_modules/@m0saic/core/scripts/mask-rasterize-core.js +1 -1
  161. package/node_modules/@m0saic/core/scripts/mask-rasterize-worker.js +1 -1
  162. package/node_modules/@m0saic/core/scripts/mask-worker-thread.js +1 -1
  163. package/node_modules/@m0saic/dictionary/NOTICE +9 -0
  164. package/node_modules/@m0saic/dictionary/README.md +315 -0
  165. package/node_modules/@m0saic/dictionary/package.json +1 -1
  166. package/node_modules/@m0saic/dsl/package.json +1 -1
  167. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/agent.js +6 -0
  168. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0/parseM0File.js +1 -1
  169. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0/serializeM0File.js +2 -2
  170. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0c/m0cFile.js +3 -3
  171. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/types.js +1 -1
  172. package/node_modules/@m0saic/dsl-file-formats/package.json +1 -1
  173. package/node_modules/@m0saic/dsl-stdlib/dist/cjs/builders/bakeM0cInsets.js +1 -0
  174. package/node_modules/@m0saic/dsl-stdlib/dist/cjs/builders/index.js +1 -1
  175. package/node_modules/@m0saic/dsl-stdlib/package.json +1 -1
  176. package/node_modules/@m0saic/knowledge/LICENSE +21 -0
  177. package/node_modules/@m0saic/knowledge/README.md +71 -0
  178. package/node_modules/@m0saic/knowledge/dist/index.js +1 -0
  179. package/node_modules/@m0saic/knowledge/docs/README.md +60 -0
  180. package/node_modules/@m0saic/knowledge/docs/file-formats/m0-iteration-protocol.md +120 -0
  181. package/node_modules/@m0saic/knowledge/docs/file-formats/m0p-and-custom-field.md +195 -0
  182. package/node_modules/@m0saic/knowledge/docs/handbook/README.md +27 -0
  183. package/node_modules/@m0saic/knowledge/docs/handbook/composition-arithmetic.md +278 -0
  184. package/node_modules/@m0saic/knowledge/docs/handbook/dsl-complexity.md +75 -0
  185. package/node_modules/@m0saic/knowledge/docs/handbook/dsl-rules.md +367 -0
  186. package/node_modules/@m0saic/knowledge/docs/handbook/feasibility-precision-quantization.md +591 -0
  187. package/node_modules/@m0saic/knowledge/docs/handbook/m0-construction-methods.md +201 -0
  188. package/node_modules/@m0saic/knowledge/docs/handbook/precision-tiers.md +84 -0
  189. package/node_modules/@m0saic/knowledge/docs/m0saic-thesis.md +95 -0
  190. package/node_modules/@m0saic/knowledge/docs/runtime/README.md +17 -0
  191. package/node_modules/@m0saic/knowledge/docs/runtime/cli-usage.md +447 -0
  192. package/node_modules/@m0saic/knowledge/docs/runtime/ffmpeg-expression-limits.md +117 -0
  193. package/node_modules/@m0saic/knowledge/docs/runtime/reduce-to-one.md +96 -0
  194. package/node_modules/@m0saic/knowledge/docs/skills/README.md +40 -0
  195. package/node_modules/@m0saic/knowledge/docs/skills/axis-and-geometry.md +103 -0
  196. package/node_modules/@m0saic/knowledge/docs/skills/dsl-stdlib-method-catalog.md +7 -0
  197. package/node_modules/@m0saic/knowledge/docs/skills/identity.md +123 -0
  198. package/node_modules/@m0saic/knowledge/docs/skills/labels-and-masks.md +170 -0
  199. package/node_modules/@m0saic/knowledge/docs/skills/m0saic-string-generation.md +251 -0
  200. package/node_modules/@m0saic/knowledge/docs/skills/more-atoms-not-bigger-atoms.md +77 -0
  201. package/node_modules/@m0saic/knowledge/docs/skills/overlay-semantics.md +194 -0
  202. package/node_modules/@m0saic/knowledge/docs/skills/parse-apis.md +79 -0
  203. package/node_modules/@m0saic/knowledge/docs/skills/passthrough-semantics.md +136 -0
  204. package/node_modules/@m0saic/knowledge/docs/skills/structural-construction.md +86 -0
  205. package/node_modules/@m0saic/knowledge/docs/skills/text-in-templates.md +126 -0
  206. package/node_modules/@m0saic/knowledge/docs/skills/zero-overlay-analysis.md +87 -0
  207. package/node_modules/@m0saic/knowledge/docs/templates/README.md +65 -0
  208. package/node_modules/@m0saic/knowledge/docs/templates/capability-templates.md +72 -0
  209. package/node_modules/@m0saic/knowledge/docs/templates/construction-strategy.md +329 -0
  210. package/node_modules/@m0saic/knowledge/docs/templates/data-pipeline.md +324 -0
  211. package/node_modules/@m0saic/knowledge/docs/templates/emission-patterns.md +130 -0
  212. package/node_modules/@m0saic/knowledge/docs/templates/geometry-recipes.md +248 -0
  213. package/node_modules/@m0saic/knowledge/docs/templates/layout-contract.md +168 -0
  214. package/node_modules/@m0saic/knowledge/docs/templates/output-resolution-tree.md +202 -0
  215. package/node_modules/@m0saic/knowledge/docs/templates/patterns/case-study-lessons.md +69 -0
  216. package/node_modules/@m0saic/knowledge/docs/templates/patterns/perf-authoring-rules.md +100 -0
  217. package/node_modules/@m0saic/knowledge/docs/templates/patterns/primitive-extraction-pattern.md +103 -0
  218. package/node_modules/@m0saic/knowledge/docs/templates/philosophy-and-contract.md +310 -0
  219. package/node_modules/@m0saic/knowledge/docs/templates/recursion-nested-rendering.md +154 -0
  220. package/node_modules/@m0saic/knowledge/docs/templates/reference/grid.md +104 -0
  221. package/node_modules/@m0saic/knowledge/docs/templates/reference/json-prop-type.md +169 -0
  222. package/node_modules/@m0saic/knowledge/docs/templates/reference/mosaic-color.md +81 -0
  223. package/node_modules/@m0saic/knowledge/docs/templates/reference/mosaic-placement-props.md +103 -0
  224. package/node_modules/@m0saic/knowledge/docs/templates/reference/prop-bindings.md +324 -0
  225. package/node_modules/@m0saic/knowledge/docs/templates/reference/template-flags.md +224 -0
  226. package/node_modules/@m0saic/knowledge/docs/templates/render-lifecycle.md +117 -0
  227. package/node_modules/@m0saic/knowledge/docs/templates/rendering-model-contract.md +392 -0
  228. package/node_modules/@m0saic/knowledge/docs/templates/standalone-pack-authoring.md +299 -0
  229. package/node_modules/@m0saic/knowledge/docs/templates/theming.md +81 -0
  230. package/node_modules/@m0saic/knowledge/docs/templates/ui-controls.md +150 -0
  231. package/node_modules/@m0saic/knowledge/package.json +6 -0
  232. package/node_modules/@m0saic/momo-types/README.md +109 -0
  233. package/node_modules/@m0saic/momo-types/dist/agent.js +1 -6
  234. package/node_modules/@m0saic/momo-types/dist/context.js +1 -1
  235. package/node_modules/@m0saic/momo-types/package.json +1 -1
  236. package/node_modules/@m0saic/platform/README.md +3 -0
  237. package/node_modules/@m0saic/platform/dist/cjs/index.js +1 -1
  238. package/node_modules/@m0saic/platform/dist/cjs/mosaic/outputKind/outputKind.js +1 -1
  239. package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/index.js +1 -1
  240. package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/propBindings.js +1 -1
  241. package/node_modules/@m0saic/platform/dist/cjs/mosaic/validate/validateSources.js +1 -1
  242. package/node_modules/@m0saic/platform/dist/cjs/packages/mosaicPackages.js +1 -0
  243. package/node_modules/@m0saic/platform/dist/cjs/paths/m0saicRoot.js +1 -1
  244. package/node_modules/@m0saic/platform/dist/cjs/telemetry/lifecycle.js +1 -1
  245. package/node_modules/@m0saic/platform/dist/cjs/telemetry/preview.js +1 -1
  246. package/node_modules/@m0saic/platform/dist/cjs/telemetry/sender.js +1 -1
  247. package/node_modules/@m0saic/platform/dist/cjs/telemetry/usageRecorder.js +1 -1
  248. package/node_modules/@m0saic/platform/dist/cjs/telemetry/usageStore.js +3 -3
  249. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/baseline.json +25 -97
  250. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/index.js +1 -1
  251. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/install.js +2 -2
  252. package/node_modules/@m0saic/platform/dist/cjs/toolchain/goldenFfmpeg.js +6 -6
  253. package/node_modules/@m0saic/platform/package.json +1 -1
  254. package/node_modules/@m0saic/product/dist/communityRelease.js +1 -1
  255. package/node_modules/@m0saic/product/dist/freeTierLicense.js +1 -1
  256. package/node_modules/@m0saic/product/dist/index.js +1 -1
  257. package/node_modules/@m0saic/product/dist/licenseActivationNotice.js +1 -0
  258. package/node_modules/@m0saic/product/dist/licenseFile.js +1 -0
  259. package/node_modules/@m0saic/product/dist/licenseToken.js +1 -1
  260. package/node_modules/@m0saic/product/dist/makeScratchPath.js +1 -0
  261. package/node_modules/@m0saic/product/dist/postRenderWrapper.js +1 -1
  262. package/node_modules/@m0saic/product/dist/publicKeys.js +1 -1
  263. package/node_modules/@m0saic/product/dist/revokedKeys.js +1 -1
  264. package/node_modules/@m0saic/product/dist/upstreamDefaults.js +1 -1
  265. package/node_modules/@m0saic/product/dist/workspaceScaffold.js +1 -0
  266. package/node_modules/@m0saic/template-utils/dist/brand/hello-world/helloWorld.js +1 -1
  267. package/node_modules/@m0saic/template-utils/dist/geometry-contract/index.js +1 -1
  268. package/node_modules/@m0saic/template-utils/dist/geometry-contract/layoutConstraint.js +2 -2
  269. package/node_modules/@m0saic/template-utils/dist/m0saic/loadSession.js +1 -1
  270. package/node_modules/@m0saic/template-utils/dist/render/createDesignContext.js +1 -1
  271. package/node_modules/@m0saic/template-utils/dist/sources/makeErrorMosaic.js +2 -2
  272. package/node_modules/@m0saic/template-utils/dist/sources/svgTextSource.js +1 -1
  273. package/node_modules/@m0saic/template-utils/dist/template/auditRenderedTemplate.js +5 -5
  274. package/node_modules/@m0saic/template-utils/dist/template/templateConventions.js +3 -3
  275. package/node_modules/@m0saic/template-utils/dist/template/validateTemplateProps.js +1 -1
  276. package/node_modules/@m0saic/template-utils/dist/text/fitText.js +6 -6
  277. package/node_modules/@m0saic/template-utils/package.json +1 -1
  278. package/node_modules/@m0saic/templates/NOTICE +9 -0
  279. package/node_modules/@m0saic/templates/dist/m0saic/media/blur-regions/v1/tutorial.js +1 -1
  280. package/node_modules/@m0saic/templates/package.json +1 -1
  281. package/node_modules/@m0saic/templates-advanced/NOTICE +9 -0
  282. package/node_modules/@m0saic/text/package.json +1 -1
  283. package/node_modules/@m0saic/types/dist/analytics/consent.js +1 -1
  284. package/node_modules/@m0saic/types/dist/analytics/event.js +1 -1
  285. package/node_modules/@m0saic/types/dist/analytics/usageMetrics.js +1 -1
  286. package/node_modules/@m0saic/types/dist/identifiers/identifiers.js +1 -1
  287. package/node_modules/@m0saic/types/dist/telemetry/ffmpegFailure.js +1 -1
  288. package/node_modules/@m0saic/types/package.json +1 -1
  289. package/node_modules/@m0saic/types-internal/dist/analytics/featureKeys.js +1 -1
  290. package/package.json +13 -11
  291. package/resources/starter-base/starter-base.json +10 -0
  292. package/resources/starter-base/starter-base.tgz +0 -0
@@ -0,0 +1,324 @@
1
+ # Prop bindings — the rect that SHOWS a prop is its handle in Make
2
+
3
+ A template marks the source that DISPLAYS a prop with `source.editor.binding`
4
+ (or `bindings`, several leaves on one rect). Make resolves them per render and
5
+ turns the rect into the prop's canvas handle: double-click / a badge to edit,
6
+ a file dropped on it to fill a media slot. Bindings are re-resolved on every
7
+ render and keyed by the PROP — never by a stableKey, which changes whenever the
8
+ geometry does.
9
+
10
+ One predicate decides what is bindable: `classifyBindableProp` in
11
+ `@m0saic/platform` (`mosaic/propBindings`). Template tests
12
+ (`resolvePropBindings` in `@m0saic/template-utils`) and the Make page both use
13
+ it, so a template can never offer an edit the page refuses, or vice versa.
14
+ `bindProps` / `bindPropPath` THROW at author time for what the platform would
15
+ silently drop — a template test should never reach the silent path.
16
+
17
+ ## The wire shape
18
+
19
+ ```ts
20
+ binding: {
21
+ propKey: string; // dotted path as propsSchema nests it: "label", "titles.title"
22
+ index?: number; // ONE element of string[] / number[] / media[]
23
+ path?: Array<string | number>; // route into a json / list / array value: [2, "title"]
24
+ kind?: "string" | "number" | "color" | "rect" | "media"; // required on structured leaves
25
+ layer?: number; // which text layer shows the leaf (multi-layer text)
26
+ onClear?: "remove-element" | "unset-leaf";
27
+ seedDraft?: string;
28
+ companion?: boolean;
29
+ range?: { start: number; end: number }; // one line of a multi-line string leaf
30
+ focus?: { start: number; end: number }; // the token the rect shows, inside range
31
+ }
32
+ ```
33
+
34
+ Rules:
35
+
36
+ - `propKey` is exactly as `propsSchema` nests it; every segment past the
37
+ first walks a `type: "group"` prop's `fields`.
38
+ - Basic lists (`string[]`, `number[]`, `media[]`) bind ONE element — a
39
+ non-negative integer `index`. The list itself is never bindable.
40
+ - Structured props (`json` / `list` / `array`) bind a LEAF: `path` from the
41
+ prop's value plus an explicit `kind` (the schema has no per-leaf type, and an
42
+ empty leaf must still be an "add" handle). `index` is shorthand for a leading
43
+ numeric segment.
44
+ - `meta.ui.hidden` props, booleans, enum / connection-option strings (closed
45
+ pickers) and whole lists are never bindable. A stray `index` on a scalar is
46
+ ignored.
47
+ - Bind even when the value is empty — the rect is then a handle to ADD.
48
+
49
+ ## Kinds
50
+
51
+ | prop / leaf | binding | kind | Make handle |
52
+ |---|---|---|---|
53
+ | free-text `string` (or one `string[]` element, or a structured leaf) | `bindProp(src, key[, i])` / `bindPropPath(src, key, path, "string")` | `"string"` | T — the inline text form |
54
+ | colour-valued string (`isColor` / `colorPicker`) | same | `"color"` | swatch — a picker |
55
+ | `number` (or one `number[]` element, or a leaf) | same, `"number"` | `"number"` | 123 — the inline form |
56
+ | `json` with `picker: "regions"`, `regions.max: 1` | `bindPropRect(src, key)` — no path | `"rect"` | move glyph — the regions draw session on the cell |
57
+ | `media`, one `media[]` element, or a leaf holding a path | `bindProp(src, key[, i])` / `bindPropPath(…, "media")` | `"media"` | picture glyph — a DROP target + the media chip |
58
+
59
+ Order the entries the way a double-click should read them: `fields[0]` is the
60
+ primary action (a plain double-click); the badge row offers every kind.
61
+
62
+ ## The roll call — `bindingsDeclared` (THROW, 2026-09-25)
63
+
64
+ **Every prop that CAN carry a canvas handle is either bound, or declared.** A new template that does
65
+ neither fails the build. Founder ruling: "if it makes sense to bind, we do — or warn if it's not."
66
+
67
+ ```ts
68
+ bindings: { unbound: { fps: "timing", gap: "geometry", seed: "determinism" } }
69
+ ```
70
+
71
+ One honest word is the point — the reason is for a reviewer. Keys are dotted exactly as `propsSchema`
72
+ nests them (`titles.title`).
73
+
74
+ ### The complete surface — one ruling per control
75
+
76
+ `MUST ACCOUNT` = bind it, or name it in `bindings.unbound`. `NEVER` = not a canvas thing; the
77
+ right-hand panel is its surface, and the rule stays silent.
78
+
79
+ | Prop / flavour | Handle | Convention |
80
+ |---|---|---|
81
+ | `string`, free text | `string` | **MUST ACCOUNT** |
82
+ | `string` + `isColor` / `colorPicker` | `color` | **MUST ACCOUNT** |
83
+ | `number` | `number` | **MUST ACCOUNT** |
84
+ | `media` | `media` (also a drop target) | **MUST ACCOUNT** |
85
+ | `string[]` · `number[]` · `media[]` | per element, by index | **MUST ACCOUNT** (bound anywhere on the prop counts) |
86
+ | `json` · `list` · `array` | per leaf, `path` + `kind` | **MUST ACCOUNT** (any leaf counts) |
87
+ | `json` + `picker: "regions"` | `rect` | **MUST ACCOUNT** |
88
+ | `string` / `number` + closed set (`oneOf`, `options`, `optionsFrom…`) | — | **NEVER** — a picker is a mode, not a value on canvas |
89
+ | `boolean` | — | **NEVER** — a one-way door: `false` renders no rect to click |
90
+ | `group` | — | **NEVER** — a container; its fields are walked individually |
91
+ | `m0` · `m0c` · `m0p` | — | **NEVER** — the value IS the composition of every rect, not one of them |
92
+ | `code` | — | **NEVER** — read-only by contract; a handle implies editing |
93
+ | anything `ui.hidden` or `consumer: "human"` | — | **NEVER** |
94
+
95
+ Why the `m0` family is excluded, since it is the tempting one: a layout prop has no clickable element.
96
+ All 10 shipped layout props (wireframe, dsl-tutorial, screencap-grid v1/v2, watermark, qr-animate,
97
+ camera-debug) drive the ROOT composition — you would have to select the whole grid, which the canvas
98
+ cannot express. If a template ever nests a layout prop into ONE cell, that cell is a real handle and a
99
+ sixth kind (`"layout"`) becomes worth having. None does today.
100
+
101
+ **Bindable ≠ has a rect.** The predicate says a `number` is always inline-editable; whether any rect
102
+ *shows* it is the template's business. `durationMs`, `fps`, `gap`, `seed`, `padding` are all
103
+ accountable and correctly unbound — which is exactly why the rule takes a declaration instead of
104
+ guessing from the pixels.
105
+
106
+ **A colour that IS `document.backgroundColor` needs nothing** — no binding, no declaration. It has no
107
+ source and no rect, so no handle can exist, and it is the way a canvas SHOULD be filled: a full-frame
108
+ base rect becomes a click target that shadows everything behind it whenever the pointer is not on a
109
+ smaller tile (founder direction 2026-09-25). Matched case-insensitively, root or child.
110
+
111
+ **A binding in a nested CHILD counts.** A template that composes its content into a child document
112
+ (the hello-world card puts everything in a `card` child) reaches its props perfectly well — Make
113
+ resolves a binding through children. **But the audit renders at `defaultProps`,** so a rect the
114
+ template only creates when a prop is non-empty reads as unbound. That is the "bind even when the value
115
+ is empty" rule biting: bind it unconditionally, or declare it.
116
+
117
+ **A stale declaration is a violation too:** naming a prop that cannot carry a handle, naming one that
118
+ is in fact bound, or leaving the reason empty. An entry claims a reviewer looked at that prop.
119
+
120
+ **Exempt:** any template hashed in the repo's `frozen.manifest.json`. It shipped before the
121
+ convention, it is never edited in place, and the fix would be a vN+1 nobody will write. A repo with no
122
+ manifest has shipped nothing and holds every template to the rule.
123
+
124
+ ## The in-context line — `bindingHints` (THROW, 2026-09-27)
125
+
126
+ **Every bound rect shows one line in context.** Make puts it under the inline editor when a person
127
+ double-clicks, on the tile card's row, and in the handle's hover tooltip — what changing the value
128
+ does and what it looks like, so nobody has to open the settings pane or a manual to understand a knob.
129
+ Founder ruling: a production-grade template adheres to all the surfaces of the ecosystem, and the
130
+ canvas is now the primary one; "it's better to take this tax now."
131
+
132
+ The line comes from either of two places, and the gate accepts either:
133
+
134
+ | Where | How | Reaches |
135
+ |---|---|---|
136
+ | the prop | `propsSchema.<key>.description = "…"` | every rect bound to that prop |
137
+ | the binding | `bindProp(src, key, i, { hint: "…" })` · a `bindProps` entry's `hint` · `withBindingHint(src, "…")` (composes with every binder) | that rect only — the wording for THIS place |
138
+
139
+ One plain sentence, never markdown, never the value itself ("The month this calendar shows; the grid
140
+ re-flows to its weeks", not "October"). Make trims it and caps it near 200 characters. The tax is
141
+ therefore one honest sentence per bound prop, paid once in the schema; a per-rect `hint` is for the
142
+ cases where the same prop means something different in different places.
143
+
144
+ **Needs none:** a `companion` leaf (filled only by a media drop, never shown in a form), and any prop
145
+ that is not bound (an `unbound` declaration already carries its reason). A binding on an unknown prop
146
+ is `bindingsSound`'s finding, not this one.
147
+
148
+ **Exempt:** any template hashed in the repo's `frozen.manifest.json`, like the roll call — it reports
149
+ in `lagging`, never `findings`. The CLI ignores the field entirely; it is extra data there.
150
+
151
+ ## The `bindingsCover` convention — what the gate checks
152
+
153
+ The roll call above is the gate. `bindingsCover` is the WEAKER SECOND SIGNAL kept beside it
154
+ (posture `record` → a warning): it reads the drawn text, so it is the only rule that can catch a prop
155
+ bound to the WRONG rect, and it still fires on frozen templates where the roll call cannot. Both
156
+ `check-registry.mjs` copies and `m0saic doctor <repo>` run both.
157
+
158
+ - A **free-text `string`** prop — not `meta.ui.hidden`, not `consumer: "human"`, not a closed /
159
+ picked set — whose default is ≥2 characters and appears verbatim in drawn text must be bound.
160
+ - A **`number`** prop, same exclusions, whose default appears in drawn text in one of its honest
161
+ spellings: `String(v)`, `toLocaleString("en-US")`, and `toFixed(1|2)` **only when the value already
162
+ has decimals** (nothing draws an integer count as `"1.0"`, but chrome draws versions that read that
163
+ way). A one-character spelling never counts.
164
+ - Numbers match on a **digit boundary**: `12` is not read out of `2012` or `3.12`, and a drawn
165
+ `1,200` is not read as the prop `200`.
166
+ - Coverage is per PROP, not per rect — a prop bound anywhere on the root document is covered.
167
+
168
+ ### One rect, two props — a composite line
169
+
170
+ `"@qsbuilds · 2026 on GitHub"` is one rect drawing two props, and a single `editor.binding` names one
171
+ of them. A bare `bindProp` there would REPLACE the existing handle, so the rule says so and names the
172
+ fix that keeps both:
173
+
174
+ ```ts
175
+ bindProps(src, [{ propKey: "handle" }, { propKey: "year", kind: "number" }])
176
+ ```
177
+
178
+ — several handles on one rect — or split the line so each prop gets its own rect. (`year-card/v1` is
179
+ the live example; it is frozen, so its fix is v2 and it warns until then.)
180
+
181
+ Colour coverage is not checked: a colour is never drawn as text, so it needs a different detector.
182
+
183
+ ## Authoring helpers (`@m0saic/template-utils`)
184
+
185
+ - `bindProp(src, propKey, index?)` — the basic case.
186
+ - `bindProps(src, entries)` — several leaves on one rect (a multi-layer text,
187
+ a cell that moves AND edits AND takes a drop). Writes `editor.bindings`,
188
+ which wins over the single `binding`.
189
+ - `bindPropPath(src, propKey, path, kind, { onClear?, seedDraft? })` — one
190
+ structured leaf.
191
+ - `bindPropRect(src, propKey)` — a one-region regions picker edited in place.
192
+ - `bindPropRange(src, propKey, index, range, focus?)` — one line of a
193
+ multi-line string prop (a code state, a lyric block); offsets index the RAW
194
+ prop string.
195
+ - `stripPropBindings(doc)` — a host composing ANOTHER template's render drops
196
+ its bindings (they name the inner template's props).
197
+ - `tag(src, label)` composes either way round with all of them.
198
+
199
+ ## `onClear` — what an EMPTY commit means
200
+
201
+ ```ts
202
+ { propKey: "days", path: [rowIndex, "day"], kind: "number", onClear: "remove-element" }
203
+ ```
204
+
205
+ - `"remove-element"` — splice the element at the leading numeric path segment
206
+ (the whole row, or one element of a basic list) out of its array. Needs an
207
+ `index` or a numeric first `path` segment.
208
+ - `"unset-leaf"` — delete the leaf key from its object, or unset a basic prop
209
+ so its default shows again. Not for array-element leaves (no key).
210
+ - Absent — numbers and colours refuse an empty commit; strings write `""`;
211
+ a scalar media clears to `""`; a `media[]` element refuses (a hole in a media
212
+ list is not a value the engine can open).
213
+
214
+ Editor rules: "empty" is blank for numbers / colours, `""` for text. An empty
215
+ commit against an already-empty leaf writes nothing. In a multi-field session a
216
+ row removal supersedes its siblings' writes; rows go highest index first. The
217
+ panel stays the primary place rows are deleted — canvas clear is an accelerator.
218
+
219
+ ## `seedDraft` — prefill an EMPTY leaf from the rect's context
220
+
221
+ ```ts
222
+ { propKey: "days", path: [len, "day"], kind: "number", onClear: "remove-element", seedDraft: String(info.day) }
223
+ ```
224
+
225
+ - Applies only when the bound leaf reads EMPTY / absent; a leaf with a value
226
+ ignores it. A DRAFT: shown pre-selected (Enter alone accepts it), written on
227
+ commit like typed text, never applied by itself. Deleting it and committing
228
+ empty still runs `onClear`.
229
+ - Blur commits too, so an untouched seeded form that loses focus WRITES the
230
+ seed — mind this when seeding.
231
+ - Wire shape is a string; a blank seed, or a non-numeric seed on a number
232
+ leaf, throws at author time; `rect` and `media` leaves take no seed.
233
+ - Focus lands on the FIRST field: order a seeded field first when the seed is
234
+ the whole suggestion.
235
+
236
+ ## `kind: "media"` — a rect that takes a dropped file
237
+
238
+ A media-bound rect is a DROP target on the Make canvas (a file dragged onto it
239
+ writes the path) with a picture-glyph handle that opens the media chip —
240
+ `Choose file…` / `Replace…` · `Remove` (when clearable) · "or drop a file on
241
+ the cell". A file in flight lights every media slot; a slot whose prop refuses
242
+ the kind lights muted.
243
+
244
+ - What may land is the PROP's say: `meta.control.accept` (`["image","video"]`)
245
+ and `meta.control.extensions`. Judged by MIME while in flight, by extension
246
+ at drop.
247
+ - `media[]`: index `< length` replaces, `=== length` appends (an add handle),
248
+ `> length` is refused. A media list is never padded with `""`.
249
+ - A tile pairs handles freely — the drop-calendar's facecam is
250
+ `bindProps(src, [{ propKey: "facecamRegion", kind: "rect" }, { propKey: "facecam" }])`:
251
+ move it, or drop on it. Put the MEDIA entry first when a double-click should
252
+ mean "replace the picture".
253
+
254
+ ### A drop commits the tile's seeded companions
255
+
256
+ When a file lands, every EMPTY sibling leaf on the tile that carries a
257
+ `seedDraft` is written with its seed — the rect's meaning travels with the
258
+ gesture (the clicked date; the slot number). Leaves with a value, or empty
259
+ leaves without a seed, are untouched.
260
+
261
+ `companion: true` marks a seeded leaf ONLY the drop fills:
262
+
263
+ ```ts
264
+ { propKey: "days", path: [row, "teaser"], kind: "number", seedDraft: String(teasers.length + 1), companion: true }
265
+ ```
266
+
267
+ It never shows in the text form (a blur-commit there would write the seed on
268
+ an unrelated edit) and never earns a badge. Needs a kept `seedDraft`; refused
269
+ on media / rect leaves. The drop-calendar date cell, in full — one drop births
270
+ a pictured row:
271
+
272
+ ```ts
273
+ bindProps(cell, [
274
+ { propKey: "days", path: [n, "day"], kind: "number", onClear: "remove-element", seedDraft: String(day) },
275
+ { propKey: "days", path: [n, "title"], kind: "string" },
276
+ { propKey: "teasers", index: teasers.length, kind: "media" },
277
+ { propKey: "days", path: [n, "teaser"], kind: "number", seedDraft: String(teasers.length + 1), companion: true },
278
+ ])
279
+ ```
280
+
281
+ ## Starter media — the slot before the footage
282
+
283
+ A template that wants the slot present (and droppable) before the user has a
284
+ file renders a stand-in while the prop is empty. Two sources:
285
+
286
+ 1. **Bring your own** — media bundled next to the template
287
+ (`assets/*.png|mp4|svg`, mirrored by copy-assets), built with
288
+ `fileAsset(resolve(__dirname, "assets"), name, kind)` from
289
+ `@m0saic/template-utils/dist/m0saic/assetPath` (asar-safe).
290
+ 2. **Included defaults** — `starterMedia(role)` from
291
+ `@m0saic/template-utils/dist/media/defaultMedia` (node-only deep import):
292
+ roles `portrait` `landscape` `square` `facecam` `avatar` `logo`, one small
293
+ viewBox-only SVG each (`assets/starter/`, minted by
294
+ `tools/mint-starter-svg.mjs`). Returns `null` when the file is not on disk —
295
+ degrade to the template's own stand-in, never an error mosaic.
296
+
297
+ An `.svg` file asset is rasterized to a PNG at plan time by the engine, and
298
+ Make's preview still-extractor rasterizes it the same way before ffmpeg sees
299
+ it — ffmpeg itself has no svg decoder, so never hand an svg path to it directly.
300
+ A still stand-in in a video slot keeps a no-footage render a poster (PNG);
301
+ motion arrives with the real clip.
302
+
303
+ Make marks an EXISTING slot with no path (a starter showing) with a persistent
304
+ dashed outline and an "Add …" glyph. An add handle (`teasers[len]`, an unborn
305
+ row) is not a slot and stays quiet — otherwise every empty date cell would light.
306
+
307
+ ## What Make does with them (the host side, for orientation)
308
+
309
+ - Badge row on hover: one glyph per kind (T · 123 · swatch · move · picture),
310
+ collapsing to one dot on a tile under ~60 on-screen px. A click on a glyph is
311
+ a double-click WITH that intent.
312
+ - Text / number / colour → the inline form over the rect (Enter / blur commit,
313
+ Esc cancels, an unchanged draft writes nothing). Rect → the regions draw
314
+ session seeded from the painted cell. Media → the chip; a drop needs no click.
315
+ - Every write goes through the ONE prop writer, so it survives every other
316
+ edit and re-renders the template; the panel field updates because it IS the
317
+ prop.
318
+ - The double-clicked tile is often chrome stacked over the bound rect (gridlines,
319
+ glass) — Make looks THROUGH the stack under the pointer for the topmost bound
320
+ tile, for clicks and drops alike.
321
+
322
+ First implementers: `@m0saic-dev/creator/drop-calendar/v1` (every kind, the
323
+ companion, the facecam slot); `@m0saic/alpine/*` (text / number / colour);
324
+ `@m0saic-dev/music/lyric-video/v1` (ranges).
@@ -0,0 +1,224 @@
1
+ # Template status flags: `internal` / `primitive` / `deprecated`
2
+
3
+ Lookup reference for the three orthogonal discovery/visibility flags on
4
+ `MosaicTemplate`. A template may carry any combination; conflating them is the
5
+ standard mistake. Declared in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts —
6
+ `internal?` (:1385), `primitive?` (:1416), `deprecated?` (:1446), each with its
7
+ truth table in the JSDoc (line numbers verified 2026-07-27; re-verify:
8
+ `grep -n "internal?: boolean\|primitive?: boolean\|deprecated?: {" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts).
9
+
10
+ | Flag | Question | Renderable alone? | Default UI |
11
+ |---|---|---|---|
12
+ | `primitive?: boolean` | "Is this a base others build on?" | **Yes** | Shown + **PRIMITIVE** badge (teal) |
13
+ | `internal?: boolean` | "Is this intended as a **top-level selection**?" (No → hidden) | **Sometimes** — the flag doesn't decide this | **Hidden**; **INTERNAL** badge (violet) when revealed |
14
+ | `deprecated?: {reason?, replacement?, since?}` | "Is this still the recommended pick?" | Yes | **Hidden**; **DEPRECATED** tag + "use X instead" |
15
+
16
+ **`primitive` is informational — it does NOT hide anything.** `internal` and
17
+ `deprecated` are the visibility gates.
18
+
19
+ ## Why `primitive` ≠ `internal`
20
+
21
+ - **`primitive` is a quality** — foundational, widely reused. `charts/donut`,
22
+ `charts/stat-card`, `charts/bar-graph`, `primitives/grid` are primitives *and*
23
+ perfectly good standalone picks.
24
+ - **`internal` is an INTENT/visibility flag, not a capability statement** — it says
25
+ "not intended as a top-level pick": excluded from public listings and template
26
+ pickers by default, but fully available to `renderNestedTemplate()`
27
+ (`internal` JSDoc, `template.ts:1375-1384`). Whether it can render standalone
28
+ varies by template and is NOT what the flag encodes: the ffmpeg-pulse piece
29
+ templates are internal yet render fine on their own; `bar-graph`'s `bar-cell`
30
+ leaf is internal AND genuinely expects parent-injected context. Don't infer
31
+ either way from the flag — read the template.
32
+ - **Both** is a real combination: a shared building block many templates reuse
33
+ that still isn't meant to be picked top-level.
34
+
35
+ (The `primitive` flag's orthogonality table in `template.ts` used to gloss
36
+ `internal` as "can this render on its own?", contradicting the `internal` JSDoc —
37
+ fixed in-code 2026-07-27; both JSDoc now carry the top-level-selection semantics.)
38
+
39
+ ## Where the flags surface
40
+
41
+ - **Electron `templates:get`** (the Mosaic Desktop / Web app source (not published)) serializes all
42
+ three into the meta the desktop Templates page consumes.
43
+ - **Desktop Templates page** — PRIMITIVE / INTERNAL badges with explanatory tooltips.
44
+ - **CLI `list-templates` / `browse-templates`** — annotates
45
+ `(primitive) (internal) (deprecated)` in that **fixed order**
46
+ (the CLI source (not published)-33`).
47
+
48
+ ## Curation is a content decision
49
+
50
+ `primitive` started as 4 seeded entries and is on **29 templates** as of 2026-07-27
51
+ (re-verify: `grep -rn "primitive: true" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic | wc -l`).
52
+ There is no rule that derives it — like `deprecated`, someone decides. If you're
53
+ adding a template that other templates will compose, set it; don't wait for a sweep.
54
+
55
+ ⚠️ **Setting `deprecated` obligates a pruning pass** — repoint the curated registry,
56
+ and remove the id from the CLI E2E cases and the e2e variant list. The full ordered
57
+ procedure is in the maintainers' agent contract §10 (not published) "Deprecating a template"; a deprecated id must
58
+ never linger in those lists. The default e2e sweep auto-excludes deprecated
59
+ templates; opt back in with `M0SAIC_INCLUDE_DEPRECATED=1`.
60
+
61
+ ### Deprecation also silences convention advice (2026-09-09)
62
+
63
+ `M0SAIC_INCLUDE_DEPRECATED=1` governs more than the e2e render sweep: it also
64
+ controls **convention findings** in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/tools/check-registry.mjs,
65
+ which gates `npm run build`.
66
+
67
+ By default a deprecated template's definition-time and render-time convention
68
+ warnings/errors are **suppressed** — the template is defunct, so the advice will
69
+ never be acted on and repeating it forever is noise. This makes the intended
70
+ workflow self-cleaning: for a shipped template that violates a convention, **bump
71
+ to vN+1 and deprecate the old one — the old one goes quiet automatically.** There
72
+ is no exclusion list to maintain.
73
+
74
+ The `outputFormat` convention (2026-09-13: a public template declares
75
+ `outputHints.format`; record posture) treats both visibility gates as
76
+ exemptions at the audit itself, not only through this suppression: an
77
+ `internal` building block renders only nested and a `deprecated` template is
78
+ frozen history, so neither owes a deliverable declaration
79
+ (`auditOutputFormat`, https://github.com/m0saic-project/m0saic-packages/blob/main/packages/template-utils/src/template/auditSchemaConventions.ts).
80
+
81
+ The suppressed count is always printed, never silent:
82
+
83
+ ```
84
+ [check-registry] ℹ 14 finding(s) on 22 deprecated template(s) suppressed
85
+ (advice only — freeze + fingerprints still apply).
86
+ M0SAIC_INCLUDE_DEPRECATED=1 to see them.
87
+ ```
88
+
89
+ ⭐ **Suppression is ADVICE ONLY.** The freeze gate (`frozen.manifest.json`, every
90
+ template shipped in 0.1.0) and the layout fingerprints still cover deprecated
91
+ templates in full, byte for byte, in both modes. Deprecation means "stop
92
+ suggesting improvements" — it NEVER grants permission to change a shipped
93
+ template. See the maintainers' agent contract §10 (not published).
94
+
95
+ Measured when introduced (103 registered templates, 22 deprecated): render-time
96
+ warnings 21 → 12, definition-time warning knobs 208 → 165 across 30 → 25
97
+ templates; freeze (327 files) and fingerprints (89) identical either way.
98
+
99
+ ## The repo's front door — `repo.helloWorld` (2026-09-14)
100
+
101
+ Not a template flag but a **repo-descriptor** field beside them
102
+ (`MosaicTemplateRepoDescriptor.helloWorld?: TemplateId`,
103
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template-repo/template-repo.ts): the id of the template a
104
+ newcomer renders first. Core names `@m0saic/hello-world/v1`; each starter names
105
+ its own `…/basics/hello-world/v1`. It is deliberately NOT a `TemplateRole` — roles
106
+ say what a template *produces*; this says which one is the door.
107
+
108
+ Readers: the CLI alias (`m0saic hello-world --template-repo <path>`), the manifest
109
+ (the descriptor is embedded, so `template-manifest.json` carries it zero-exec; the
110
+ platform loader passes it through), and — follow-up — Make's "Start here" chip.
111
+ The gate's `repoFrontDoor` convention (record posture) warns when the field is
112
+ unset or names an id the repo does not register; `internal` / `deprecated`
113
+ templates are fine to name, just unusual. Authoring recipe: `standalone-pack-authoring.md`
114
+ §1.
115
+
116
+ ## A canvas that is a knob — `resolveOutputHints` (2026-09-15)
117
+
118
+ Also not a flag but a **template field** beside `outputHints`:
119
+ `resolveOutputHints?: (props) => Partial<MosaicTemplateOutputHints>`. The
120
+ static `outputHints` stay what the manifest and the cards show; the resolver
121
+ is what a host asks with the current props before rendering, through the one
122
+ helper `resolveTemplateOutputHints(tmpl, props)` (`@m0saic/template-utils`).
123
+ Readers: CLI `make` (+ wireframe / tutorial paths), Electron preview / cover /
124
+ render-to-file + the `templates:resolveOutputHints` IPC, the web design
125
+ preview, and Make's Device anchor (re-resolved on every prop change). The
126
+ seam's `outputHintsResolve` convention (THROW posture) checks it returns an
127
+ object, is deterministic at `defaultProps`, and agrees with the static hints
128
+ for every field it returns there. Precedence: [`../output-resolution-tree.md`](../output-resolution-tree.md) §size.
129
+
130
+ **Image or video is part of the same answer (2026-09-26).** `format` rides the same
131
+ resolver. A template whose kind depends on its inputs — a `media` prop that accepts
132
+ both kinds — declares it per props, `resolveOutputHints: (props) => ({ format: … })`,
133
+ and its static `outputHints.format` is that function's value at `defaultProps`, which
134
+ for an EMPTY media slot is **image** even when the template is "for" video (the
135
+ `outputHintsResolve` throw holds the two equal). The resolver is pure and prop-only:
136
+ it runs before any probe, so a path's extension is the evidence available. Exactly
137
+ three answers exist — the static hint at defaults, the resolver at the current
138
+ props, and the user's override — and nothing else authors one. Every host asks the
139
+ author through `resolveTemplateOutputHints(tmpl, props).format` and passes it into
140
+ `createDesignContext` / `createEngineContext` as `format`, so the `format` the
141
+ `defineMosaicTemplate` wrapper stamps on the rendered document is a RECORD of the
142
+ decision, never a source of it. `inferOutputKind` (`@m0saic/platform`) is a last
143
+ resort for a bare `.mosaic` opened cold with no template behind it, and since
144
+ 2026-09-26 it counts a `type: "media"` video source as motion (`videoMedia`) — the
145
+ omission that made footage-through-a-mask read as a still. The defect class and the
146
+ seven-surface audit that produced this rule:
147
+ (internal design history).
148
+
149
+ ## The lattice declarations — `lattice` (2026-09-16)
150
+
151
+ **Convention `latticeSmooth` (throw):** every split count above 12 in the rendered
152
+ layout — root document and every nested child, walked as documents (a nested doc
153
+ whose flatten fails below its floor is still measured) — is 5-smooth, `N = 2ᵃ·3ᵇ·5ᶜ`.
154
+ Counts ≤ 12 with a rough factor (7 weekdays, 11 tiles) pass as small-basis ratio
155
+ fill. Measured at the hinted canvas and, under `--sweep`, on the standard canvases.
156
+ Why: all delivery canvases are 5-smooth with family gcd 120, so 5-smooth counts
157
+ divide them (pixel-exact), any two 5-smooth self-lattices LCM to a small number,
158
+ and a child never inherits a rough cell size (handbook
159
+ [`composition-arithmetic.md`](../../handbook/composition-arithmetic.md) §1–3).
160
+ `m0saic doctor <repo>` reports it for external packs at error severity — the
161
+ publish requirement. Plan + numbers: (internal design history).
162
+
163
+ `MosaicTemplate.lattice?` (https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts, beside
164
+ `primitive?` / `deprecated?`) is the declaration surface — every override is typed,
165
+ reasoned, and printed by the gate:
166
+
167
+ | field | when it is honest | effect |
168
+ |---|---|---|
169
+ | `mode: "bitmap"` | the split counts ARE a raster (QR modules, barcode bars, a logo trace, the community M) — handbook §3c BITMAP, baked once, never live-composed | the template is skipped with a note |
170
+ | `allow: [{ count, reason }]` | a count above 12 that is content cardinality (53 ISO weeks; a 335-tile fixture grid) | the count is accepted; the reason is printed and carried in `--json` |
171
+ | `canvas: "physical"` | the hinted size is a millimetre spec at a dpi (business card 1130×678 = 2·5·113 × 2·3·113) | the rough-axis finding is not raised, counts the canvas hands down are charged to it (not to the construction), and the `--sweep` canvases are skipped — a print size cannot move |
172
+
173
+ Per DOCUMENT, `MosaicEngineMeta.lattice?: { mode: "bitmap" }` (`doc.engine.lattice`)
174
+ marks a raster a parent assembles itself (the business card's QR child, built from
175
+ `qrToRenderable` pieces) — that subtree is skipped, the parent is still measured.
176
+ (The layout-fingerprint sidecar cannot carry this marker: the fleet lock
177
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/lattice-audit.test.ts re-measures such a template with
178
+ `auditRenderedTemplate` instead of trusting the flattened string.)
179
+
180
+ **Not honest:** a count that came from your arithmetic — independent rounding to a
181
+ cap (121/119/241/245), a pitch matched to a grid that no longer exists (499),
182
+ `round(side/K)` + `ceil` self-frames (99/101), pixel splits inside an already
183
+ quantized cell. The finding names the nearest 5-smooth neighbours and the LCM cost;
184
+ the neighbour almost always looks identical.
185
+
186
+ ## Reading the dictionary from a template — web vs node (2026-09-16)
187
+
188
+ `@m0saic/dictionary` has two entries: node (`index.ts`, reads `.m0`/`.m0c` from
189
+ disk) and browser (`browser.ts`, what the Mosaic Desktop / Web app source (not published) bundles via the package's
190
+ `browser` field). Every jest suite resolves the NODE entry, so a template that reads
191
+ a brand entry synchronously can be green under test and an error mosaic on
192
+ app.m0saic.io — which is exactly what QR Code's centre M, Brand Marks v3 and
193
+ community-m did until 2026-09-16 (the browser entry shipped every brand `m0` as `""`
194
+ and its `getRankSet` threw). Rules that hold on BOTH entries now:
195
+
196
+ - **`entry.m0` is populated for every entry whose canonical m0 is ≤ 40 KB**
197
+ (`INLINE_M0_MAX_CHARS` in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/tools/validate.js, which emits a
198
+ committed sibling `m0.json` per entry that `browser.ts` imports). Only
199
+ `brand/m-33_bitmap` (149 KB) ships `m0: ""` on web — a template that can take it
200
+ must guard `if (!entry.m0)` and say "Desktop renders this" (Brand Marks v3 does).
201
+ - **`entry.masks`, `entry.rankSets`, `entry.sourceCount` ride in `metadata.json`** →
202
+ identical on web. `registry.getRankSet(id, name)` works on web (same lookup as
203
+ node). Named mask SETS (`getMaskSet`) are node-only.
204
+ - **The guards, in order of reach:** https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/browser.test.ts
205
+ (browser m0 == node m0 for every inlined entry; only the bitmap lazy) ·
206
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/webDictionary.test.ts (renders QR Code / Brand Marks /
207
+ community-m against the browser entry via `jest.mock`) · the Playwright web lane
208
+ the Mosaic Desktop / Web app source (not published) (the shipped bundle).
209
+ **Add your template to `webDictionary.test.ts` if it reads a brand entry.**
210
+
211
+ the Mosaic Desktop / Web app source (not published) copies `*.json`, so `m0.json`
212
+ also lands under `public/dictionary/` — harmless (≤ 10 KB each). The light entries in
213
+ `browser.ts` are still hand-written F-form literals; the drift test compares them
214
+ canonically to the generated `m0.json`.
215
+
216
+ ## Known wrinkle
217
+
218
+ The Templates page's **"Show layout primitives" toggle** (renamed from "Show
219
+ primitives" on 2026-09-16 so the label says what it does) gates a *tag-derived*
220
+ Primitives **category** (tags `grid` / `primitive` — in practice the `primitives/`
221
+ family) — separate from the cross-cutting `primitive` **flag**, which only ranks and
222
+ badges. They coexist cleanly, but they are two mechanisms with one word. Driving the
223
+ toggle off the flag would hide most of the alpine/charts shelf by default, so the
224
+ founder kept the category gate and fixed the label instead.