m0saic 0.3.1 → 0.4.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.
- package/README.md +23 -16
- package/dist/browseE2e.js +1 -1
- package/dist/connectionsFromEnv.js +1 -1
- package/dist/e2eTemplateVariants.js +1 -1
- package/dist/frontDoor.js +1 -1
- package/dist/index.js +1 -1
- package/dist/inlineDsl.js +1 -1
- package/dist/mcp/bridgeClient.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tools.js +1 -1
- package/dist/mosaicxRunner.js +1 -1
- package/dist/outputPattern.js +1 -1
- package/dist/showcaseFeed.js +1 -1
- package/dist/templateDoctor.js +1 -1
- package/dist/utils/checkFeasibility.js +1 -1
- package/dist/utils/collectRenderErrors.js +1 -1
- package/dist/utils/enrichImageAlphaFromDoc.js +1 -1
- package/dist/utils/ensureFfmpeg.js +1 -1
- package/dist/utils/extractLayoutMeta.js +1 -1
- package/dist/utils/failureHints.js +1 -1
- package/dist/utils/ffmpegProbe.js +1 -1
- package/dist/utils/formatM0saicForCli.js +1 -1
- package/dist/utils/hashFile.js +1 -1
- package/dist/utils/helpGroups.js +1 -1
- package/dist/utils/installContext.js +1 -1
- package/dist/utils/invocation.js +1 -1
- package/dist/utils/licenseActivation.js +1 -1
- package/dist/utils/licenseActivationWorker.js +1 -1
- package/dist/utils/mosaicSpinner.js +1 -1
- package/dist/utils/parseDimensions.js +1 -1
- package/dist/utils/pathInfo.js +1 -1
- package/dist/utils/readJsonArg.js +1 -1
- package/dist/utils/renderOutcome.js +1 -1
- package/dist/utils/renderReport.js +1 -1
- package/dist/utils/renderSignals.js +1 -1
- package/dist/utils/renderWorkspace.js +1 -1
- package/dist/utils/reportOutputFormat.js +1 -1
- package/dist/utils/resolveLayoutFileProps.js +1 -0
- package/dist/utils/setupFfmpeg.js +1 -1
- package/dist/utils/telemetry.js +1 -1
- package/dist/utils/telemetryFlushWorker.js +1 -1
- package/dist/utils/telemetryUpstream.js +1 -1
- package/dist/utils/telemetryUsage.js +1 -1
- package/dist/utils/templateInstall.js +1 -0
- package/dist/utils/templateRepoKeys.js +1 -0
- package/dist/utils/templateSources.js +1 -0
- package/dist/utils/toolchainConfig.js +1 -1
- package/dist/utils/tutorialDuration.js +1 -1
- package/dist/utils/updateCheck.js +1 -1
- package/dist/utils/updateCheckWorker.js +1 -1
- package/dist/utils/validateSourceCounts.js +1 -1
- package/dist/utils/versions.js +1 -1
- package/dist/webCustomizeLink.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildChildMosaicSource.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildChunkCommands.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildEncodeCommands.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildLavfiSource.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildMediaSource.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildMosaicNode.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildMosaicPlanFromFile.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildSvgTextSource.js +1 -1
- package/node_modules/@m0saic/core/dist/core/buildTextRenderCommand.js +1 -1
- package/node_modules/@m0saic/core/dist/core/collectRefTargets.js +1 -1
- package/node_modules/@m0saic/core/dist/core/index.js +1 -1
- package/node_modules/@m0saic/core/dist/core/maskCacheDir.js +1 -1
- package/node_modules/@m0saic/core/dist/core/maskWorkerBridge.js +1 -1
- package/node_modules/@m0saic/core/dist/core/playbackPlaySpeed.js +1 -1
- package/node_modules/@m0saic/core/dist/core/prepareProps.js +1 -1
- package/node_modules/@m0saic/core/dist/core/primaryOutput.js +1 -1
- package/node_modules/@m0saic/core/dist/core/rasterizeMaskPng.js +1 -1
- package/node_modules/@m0saic/core/dist/core/rasterizeSvgMediaPng.js +1 -1
- package/node_modules/@m0saic/core/dist/core/refTargetTap.js +1 -1
- package/node_modules/@m0saic/core/dist/core/resolveMasks.js +1 -1
- package/node_modules/@m0saic/core/dist/core/resolveMasksSync.js +1 -1
- package/node_modules/@m0saic/core/dist/core/resolveStrokeRingsSync.js +1 -1
- package/node_modules/@m0saic/core/dist/core/roundedRectMask.js +1 -1
- package/node_modules/@m0saic/core/dist/core/strokeRing.js +1 -1
- package/node_modules/@m0saic/core/dist/core/svgRaster.js +1 -1
- package/node_modules/@m0saic/core/dist/defaults.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/backgroundImageInput.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/bufferGuard.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/ffmpegCommands.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBackdropBlur.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBlur.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBorderRadius.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyCamera.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyChromaKey.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyFade.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyGrade.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyMask.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyNoise.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyPixelize.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyRotate.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyStroke.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyTileEffects.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/coverCrop.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/index.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/filters/sourceRectCrop.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/gridLowering.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/index.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/splitAvMux.js +1 -1
- package/node_modules/@m0saic/core/dist/ffmpeg/stillOutput.js +1 -0
- package/node_modules/@m0saic/core/dist/ffmpeg/threadGuard.js +1 -1
- package/node_modules/@m0saic/core/dist/flatten/index.js +1 -1
- package/node_modules/@m0saic/core/dist/fs/index.js +1 -1
- package/node_modules/@m0saic/core/dist/fs/tempWorkspace.js +1 -1
- package/node_modules/@m0saic/core/dist/index.js +1 -1
- package/node_modules/@m0saic/core/dist/media/buildAssetManifestFromInputs.js +1 -1
- package/node_modules/@m0saic/core/dist/media/buildMediaRegistryFromInput.js +1 -1
- package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromInvocationProps.js +1 -1
- package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromMosaicRenderableFile.js +1 -1
- package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromProps.js +1 -1
- package/node_modules/@m0saic/core/dist/media/index.js +1 -1
- package/node_modules/@m0saic/core/dist/media/internalMintAsset.js +1 -1
- package/node_modules/@m0saic/core/dist/media/remoteUrl.js +1 -1
- package/node_modules/@m0saic/core/dist/media/stageUnsafeFilenames.js +1 -1
- package/node_modules/@m0saic/core/dist/media/types.js +1 -1
- package/node_modules/@m0saic/core/dist/output/index.js +1 -1
- package/node_modules/@m0saic/core/dist/output/resolveOutputFormat.js +1 -1
- package/node_modules/@m0saic/core/dist/output/types.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/cellLuminanceProbe.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/extractSubtitleCues.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/helpers.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/index.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/luminanceProbe.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/probeMedia.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/runFFprobe.js +1 -1
- package/node_modules/@m0saic/core/dist/probe/types.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/engineRedaction.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/ffmpegFailure.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/filterPreflight.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/hashFile.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/index.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/pathInfo.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/perfHeatmap.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/perfReport.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/pipelineM0p.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/renderCostModel.constants.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/renderCostModel.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/renderReport.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/renderSidecars.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/resolveMosaicx.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/runPlan.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/types.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/upstream.js +1 -1
- package/node_modules/@m0saic/core/dist/runtime/writeSidecars.js +1 -1
- package/node_modules/@m0saic/core/dist/types/chunk.js +1 -1
- package/node_modules/@m0saic/core/dist/types/index.js +1 -1
- package/node_modules/@m0saic/core/dist/types/mosaic-plan.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/assertTiming.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/command.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/createEngineContext.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/createTemplateRenderCache.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/ffmpegColor.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/formatDuration.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/fsSafeId.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/index.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/lowerRenderableToMosaicDocument.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/movSafeCodec.js +1 -1
- package/node_modules/@m0saic/core/dist/utils/resolveBoxFrac.js +1 -1
- package/node_modules/@m0saic/core/dist/validate/index.js +1 -1
- package/node_modules/@m0saic/core/package.json +1 -1
- package/node_modules/@m0saic/core/scripts/mask-rasterize-core.js +1 -1
- package/node_modules/@m0saic/core/scripts/mask-rasterize-worker.js +1 -1
- package/node_modules/@m0saic/core/scripts/mask-worker-thread.js +1 -1
- package/node_modules/@m0saic/dictionary/package.json +1 -1
- package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0v/m0vFile.js +2 -2
- package/node_modules/@m0saic/dsl-file-formats/package.json +1 -1
- package/node_modules/@m0saic/knowledge/docs/README.md +1 -0
- package/node_modules/@m0saic/knowledge/docs/file-formats/m0p-and-custom-field.md +1 -1
- package/node_modules/@m0saic/knowledge/docs/runtime/cli-usage.md +186 -46
- package/node_modules/@m0saic/knowledge/docs/runtime/ffmpeg-expression-limits.md +7 -0
- package/node_modules/@m0saic/knowledge/docs/templates/README.md +5 -1
- package/node_modules/@m0saic/knowledge/docs/templates/patterns/print-authoring.md +97 -0
- package/node_modules/@m0saic/knowledge/docs/templates/reference/brand-schema.md +73 -0
- package/node_modules/@m0saic/knowledge/docs/templates/reference/prop-bindings.md +31 -1
- package/node_modules/@m0saic/knowledge/docs/templates/render-lifecycle.md +26 -1
- package/node_modules/@m0saic/knowledge/docs/templates/ui-controls.md +42 -0
- package/node_modules/@m0saic/knowledge/package.json +1 -1
- package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/index.js +1 -1
- package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/propBindings.js +1 -1
- package/node_modules/@m0saic/platform/dist/cjs/share/shareQuery.js +1 -1
- package/node_modules/@m0saic/platform/package.json +1 -1
- package/node_modules/@m0saic/product/dist/__fixtures__/tarGz.js +1 -0
- package/node_modules/@m0saic/product/dist/communityRelease.js +1 -1
- package/node_modules/@m0saic/product/dist/freeTierLicense.js +1 -1
- package/node_modules/@m0saic/product/dist/index.js +1 -1
- package/node_modules/@m0saic/product/dist/installedTemplateRepos.js +1 -0
- package/node_modules/@m0saic/product/dist/licenseActivationNotice.js +1 -1
- package/node_modules/@m0saic/product/dist/licenseFile.js +1 -1
- package/node_modules/@m0saic/product/dist/licenseToken.js +1 -1
- package/node_modules/@m0saic/product/dist/makeScratchPath.js +1 -1
- package/node_modules/@m0saic/product/dist/postRenderWrapper.js +1 -1
- package/node_modules/@m0saic/product/dist/publicKeys.js +1 -1
- package/node_modules/@m0saic/product/dist/revokedKeys.js +1 -1
- package/node_modules/@m0saic/product/dist/templateBundle.js +1 -0
- package/node_modules/@m0saic/product/dist/upstreamDefaults.js +1 -1
- package/node_modules/@m0saic/product/dist/workspaceScaffold.js +1 -1
- package/node_modules/@m0saic/product/package.json +1 -1
- package/node_modules/@m0saic/template-utils/dist/media/defaultMedia.browser.js +1 -0
- package/node_modules/@m0saic/template-utils/dist/media/starterSvgInline.js +1 -0
- package/node_modules/@m0saic/template-utils/package.json +1 -1
- package/node_modules/@m0saic/templates/dist/m0saic/alpine/bar-graph/v3/bar-graph.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/alpine/bar-graph/v3/index.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/alpine/index.js +1 -1
- package/node_modules/@m0saic/templates/dist/m0saic/alpine/line-chart/v4/index.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/alpine/line-chart/v4/line-chart.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/brand/business-card/v3/business-card.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/brand/business-card/v3/index.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/brand/business-card/v3/layout.js +1 -0
- package/node_modules/@m0saic/templates/dist/m0saic/brand/index.js +1 -1
- package/node_modules/@m0saic/templates/dist/template-catalog.json +486 -6
- package/node_modules/@m0saic/templates/dist/template-registry.js +1 -1
- package/node_modules/@m0saic/templates/dist/web.js +1 -1
- package/node_modules/@m0saic/templates/package.json +1 -1
- package/node_modules/@m0saic/templates/template-catalog.json +486 -6
- package/node_modules/@m0saic/templates/template-manifest.json +69 -0
- package/node_modules/@m0saic/templates/web-template-ids.json +3 -0
- package/node_modules/@m0saic/templates-advanced/package.json +1 -1
- package/node_modules/@m0saic/text/package.json +1 -1
- package/node_modules/@m0saic/types/package.json +1 -1
- package/node_modules/@m0saic/types-internal/dist/analytics/featureKeys.js +1 -1
- package/node_modules/@m0saic/types-internal/package.json +1 -1
- package/package.json +13 -13
- package/resources/starter-base/starter-base.json +4 -4
- package/resources/starter-base/starter-base.tgz +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`}
|
|
1
|
+
"use strict";var d=Object.defineProperty;var a=(e,t)=>d(e,"name",{value:t,configurable:!0});Object.defineProperty(exports,"__esModule",{value:!0}),exports.serializeM0vFile=serializeM0vFile,exports.parseM0vFile=parseM0vFile;const serializeM0File_1=require("../m0/serializeM0File");function serializeM0vFile(e){const t=e.created??new Date;if(e.outputs!==void 0&&(e.outputs===null||typeof e.outputs!="object"||Array.isArray(e.outputs)))throw new Error("serializeM0vFile: outputs must be an object map when provided");if(e.assets!==void 0&&(e.assets===null||typeof e.assets!="object"||Array.isArray(e.assets)))throw new Error("serializeM0vFile: assets must be an object map when provided");if(e.brand!==void 0&&(e.brand===null||typeof e.brand!="object"||Array.isArray(e.brand)))throw new Error("serializeM0vFile: brand must be an object map when provided");const r=e.app??null,i=e.appVersion??null,s=normalizeMeta(e.meta??null),n={format:"m0v",version:1,created:(0,serializeM0File_1.formatISO)(t),app:r&&r.trim()!==""?r.trim():null,appVersion:i&&i.trim()!==""?i.trim():null,meta:s,custom:e.custom??null};return e.outputs&&Object.keys(e.outputs).length>0&&(n.outputs=e.outputs),e.assets&&Object.keys(e.assets).length>0&&(n.assets=e.assets),e.brand&&Object.keys(e.brand).length>0&&(n.brand=e.brand),JSON.stringify(n,null,2)+`
|
|
2
|
+
`}a(serializeM0vFile,"serializeM0vFile");function normalizeMeta(e){if(!e)return null;const t={},r=["title","author","source","note"];for(const i of r){const s=e[i];if(s!=null){const n=s.trim();n!==""&&(t[i]=n)}}return Object.keys(t).length?t:null}a(normalizeMeta,"normalizeMeta");function parseM0vFile(e){if(typeof e!="string")throw new Error("parseM0vFile: input must be a string");let t;try{t=JSON.parse(e)}catch{throw new Error("parseM0vFile: invalid JSON")}if(!isPlainObject(t))throw new Error("parseM0vFile: expected JSON object");const r=t;if(r.format!=="m0v")throw new Error(`parseM0vFile: expected format "m0v", got "${String(r.format)}"`);if(r.version!==1)throw new Error(`parseM0vFile: expected version 1, got ${String(r.version)}`);const i=typeof r.created=="string"?r.created.trim():"";if(!i)throw new Error("parseM0vFile: missing created timestamp");const s=parseOptionalMap(r.outputs,"outputs"),n=parseOptionalMap(r.assets,"assets"),u=parseOptionalMap(r.brand,"brand"),l=parseStringOrNull(r.app),c=parseStringOrNull(r.appVersion),f=parseMetaOrNull(r.meta),o={format:"m0v",version:1,created:i,app:l,appVersion:c,meta:f,custom:r.custom===void 0?null:r.custom};return s!==void 0&&(o.outputs=s),n!==void 0&&(o.assets=n),u!==void 0&&(o.brand=u),o}a(parseM0vFile,"parseM0vFile");function parseOptionalMap(e,t){if(e!==void 0){if(e===null)throw new Error(`parseM0vFile: invalid ${t} (expected object map or omit, got null)`);if(!isPlainObject(e))throw new Error(`parseM0vFile: invalid ${t} (expected object map, got `+(Array.isArray(e)?"array":typeof e)+")");return e}}a(parseOptionalMap,"parseOptionalMap");function parseStringOrNull(e){if(e==null||typeof e!="string")return null;const t=e.trim();return t||null}a(parseStringOrNull,"parseStringOrNull");function parseMetaOrNull(e){if(e==null)return null;if(!isPlainObject(e))throw new Error("parseM0vFile: invalid meta (expected object or null)");const t=e,r={};for(const i of["title","author","source","note"]){const s=t[i];if(typeof s=="string"){const n=s.trim();n&&(r[i]=n)}}return Object.keys(r).length?r:null}a(parseMetaOrNull,"parseMetaOrNull");function isPlainObject(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}a(isPlainObject,"isPlainObject");
|
|
@@ -45,6 +45,7 @@ disposes).
|
|
|
45
45
|
| Overlays, passthroughs, identity | `skills/overlay-semantics.md`, `skills/passthrough-semantics.md`, `skills/identity.md` |
|
|
46
46
|
| Build a template | `templates/philosophy-and-contract.md` → `templates/construction-strategy.md` → `templates/geometry-recipes.md` |
|
|
47
47
|
| Template perf | `templates/patterns/perf-authoring-rules.md` |
|
|
48
|
+
| A template that goes to paper (cards, banners, posters) | `templates/patterns/print-authoring.md` |
|
|
48
49
|
| Data-driven templates | `templates/data-pipeline.md` |
|
|
49
50
|
| Read/write `.m0` / `.m0c` / `.m0p` / `.m0v` | `file-formats/m0p-and-custom-field.md` |
|
|
50
51
|
| Render via the CLI | `runtime/cli-usage.md` |
|
|
@@ -23,7 +23,7 @@ These features shipped together in April 2026. They are foundational to v2 work
|
|
|
23
23
|
| `.m0c` | JSON: DSL + StableKey-keyed labels + derive image + meta | One layout, with labels / overlays / derive thumbnail / `custom.*` |
|
|
24
24
|
| `.m0g` | `.m0c`-shaped JSON with `format: "m0g"` + the `M0cGold` `gold` block | A **m0saic golden** ("mogging") — a tried-and-true, signed-off layout; `#mogging` comment-line convention (`types.ts`) |
|
|
25
25
|
| `.m0p` | JSON: pack of `.m0c`-shaped variants under shared identity | Multiple coordinated variants (cross-canvas, brand pack, content set, themed collection) |
|
|
26
|
-
| `.m0v` | JSON: `outputs` / `assets` / `custom` (`src/m0v/m0vFile.ts`) | **Mosaic Vocabulary** — user-tier shared brand language: output presets,
|
|
26
|
+
| `.m0v` | JSON: `outputs` / `assets` / `brand` / `custom` (`src/m0v/m0vFile.ts`) | **Mosaic Vocabulary** — user-tier shared brand language: output presets, asset paths, and since 2026-10-19 the `brand` slot (the shop's name, logo, accent, site, … as strings — Mosaic Desktop's "Your brand" profile, `~/m0saic/solutions/<id>/brand.m0v`; see `templates/reference/brand-schema.md`). Every slot optional; palette, typography still ride in `custom`. CLI `--m0v <path>` reads `outputs`; `m0saic open` accepts it |
|
|
27
27
|
|
|
28
28
|
`.m0` and `.m0c` are unchanged by the later formats' arrival — each is purely additive.
|
|
29
29
|
This doc covers `.m0`/`.m0c`/`.m0p` in depth; `.m0g` and `.m0v` have their own type
|
|
@@ -44,7 +44,11 @@ m0saic layout.m0 # .m0 / .m0c paths also accepted (index.ts:~7184-7270
|
|
|
44
44
|
## Command table
|
|
45
45
|
|
|
46
46
|
18 `.command(` registrations in `index.ts` + `telemetry` registered by
|
|
47
|
-
`registerTelemetryCommand(program)` (index.ts:~7138 → `utils/telemetry.ts:~372`)
|
|
47
|
+
`registerTelemetryCommand(program)` (index.ts:~7138 → `utils/telemetry.ts:~372`) +
|
|
48
|
+
`community` (`fetch`), `install`, `uninstall` registered by
|
|
49
|
+
`registerTemplateInstallCommands(program, …)` (`utils/templateInstall.ts`, since
|
|
50
|
+
0.4.0; also listed in `inlineDsl.ts` `KNOWN_SUBCOMMANDS` so they are never read as
|
|
51
|
+
an inline m0 string).
|
|
48
52
|
Two of the 18 sit inside `if (DEV_MODE) { … }` and are absent from the public
|
|
49
53
|
help — see the DEV-GATED rows below.
|
|
50
54
|
|
|
@@ -58,12 +62,15 @@ help — see the DEV-GATED rows below.
|
|
|
58
62
|
| Command (index.ts line) | What it does |
|
|
59
63
|
|---|---|
|
|
60
64
|
| `make <input>` (~5095) | Primary render: template id / `.mosaic` / `.mosaicx` → video or image. Full flag surface below. |
|
|
61
|
-
| `hello-world` | The first render — a thin alias for `make @m0saic/hello-world/v1` (the brand card; output defaults to `hello-world.mp4`). With `--template-repo <path...>`
|
|
65
|
+
| `hello-world` | The first render — a thin alias for `make @m0saic/hello-world/v1` (the brand card; output defaults to `hello-world.mp4`). With `--template-repo <path...>` it renders the loaded folder's **front door** instead: the first flagged repo (load order) whose `repo.helloWorld` names a template that registered (installed repos never are the front door; the community one is `m0saic make @m0saic-community/hello-world/v1` after `m0saic community fetch`), announced as `Front door: <id> — <repo>'s hello-world`; no repos, or none that name one → the core card with a one-line why. Takes `-o -w -h --fps --durationMs --props --quiet --verbose` (no engine surface). Picker: the CLI source (not published). |
|
|
62
66
|
| `make-wireframe` (~5462) | Static wireframe PNG from `--m0 <string>` or `--mfile <path>`. |
|
|
63
67
|
| `make-wireframe-animated` (~5568) | Animated wireframe MP4 from `--m0`/`--mfile`; extra `--disable-ui`. |
|
|
64
68
|
| `flatten <input>` (~5680) | Inline all `type:mosaic` children of a `.mosaic`/template into one flat JSON doc — no render. |
|
|
65
69
|
| `resolve <input>` (~5800) | `.mosaicx` recipe → resolved `.mosaic` provenance doc, no ffmpeg; `--flatten` also inlines children. |
|
|
66
|
-
| `list-templates` (~
|
|
70
|
+
| `list-templates` (~6103) | Print registered template ids, grouped by source (since 0.4.0): `Built-in: N templates`, then `Installed <repo id> (<name>): <tag>, signed|unsigned, N templates` (community first), then `Folder <path>, <repo id> (<name>): N templates` for `--template-repo`. A heading is one line that never starts with `@`; ids stay one per line, flush left. `--source <name>` lists one group: `builtin`, `community`, or a repo id with or without `@` (any case); an unknown name exits 1 and lists the sources. `--json` = a flat sorted id array, filtered by `--source`. Annotates primitive/internal/deprecated. Grouping: the CLI source (not published). |
|
|
71
|
+
| `community fetch` | **Since 0.4.0.** Install or update the official community templates from their latest signed GitHub release bundle, or report them current (`--force` reinstalls, `--quiet`). A valid signature is required. See "Installed template repos" below. Impl: the CLI source (not published). |
|
|
72
|
+
| `install [source]` | **Since 0.4.0.** Install a template repo from its release bundle: `owner/repo[@tag]`, a GitHub repo or release page, a `bundle.json` / `.tgz` URL, or a bundle on disk. No source: list what is installed (`--json`). A third-party repo: once `bundle.json` is read, the third-party code notice, then a question (repo, tag, templates, size; default No) before the runtime downloads; `--yes` skips it and is required with no terminal (CI, pipes: exit 1, nothing installed). A repo signed by m0saic (second-party) gets one line instead, no question. Given the official community repo in any GitHub form, it runs `community fetch` instead. A private repo needs its access key: `--key-env <name>`, then `M0SAIC_REPO_KEY`, then the key saved for it; in a terminal it asks once and saves it. `--force`, `--quiet`. |
|
|
73
|
+
| `uninstall <repoId>` | **Since 0.4.0.** Remove an installed template repo (files + entry) and its saved access key, if any; exit 1 with the installed ids when it is not installed. |
|
|
67
74
|
| `doctor <repoDir>` | **Public since 0.2.0** (dev-gated at 0.1.0; founder ruling 2026-09-16, BURN-DOWN D5 → publish). Run the template conventions over a template repo folder — the same checks its build gate (`tools/check-registry.mjs`) runs, from outside (agents, reviewers, forks, CI on a fork): definition-time + render-time conventions (the 13 throw-posture ones incl. `latticeSmooth`) and, when the repo commits `layout-fingerprints/` or `.layout.m0` sidecars, the fingerprint diff. `--json` (one object: `ok, repo, mode, loadDiagnostics, rendered, skipped, errors, warnings, notes, fingerprints, conventions`), `--sweep` (also render on the standard 1080p canvases → `canvasEnvelope`), `--entry <path>`, `--src <dir>`. Loads through `loadTemplateRepoFromPath` under the external origin scope, so conventions RECORD rather than throw and one bad template never hides the rest. **Runs the repo's template module bodies in-process** — prints a trust warning to stderr (not under `--json`). Exit 1 on any error-severity finding or a fatal load diagnostic. It is the pack-publishing contract for community/starter authors ("the publish requirement for packs"). Impl: the CLI source (not published); locked public by `__tests__/cli.surface.test.js`. |
|
|
68
75
|
| `browse-templates` (~6033) | **DEV-GATED** (whole command inside `if (DEV_MODE)`, 4792): interactive TTY picker (template → variant → confirm → delegates to `make`). It drives the E2E variant matrix and writes to `test-output/` — a development harness, not a user surface. |
|
|
69
76
|
| `momo <message…>` (~6282) | **DEV-GATED** (whole command inside `if (DEV_MODE)`, 5040): relay English geometry instruction to Momo in a running Mosaic Desktop over the loopback bridge; exit 0 applied / 2 not-applied / 1 couldn't-run. |
|
|
@@ -177,8 +184,9 @@ mosaic; prints `Validation DEGRADED`), `--report` (`.output.json` /
|
|
|
177
184
|
`--keep-temp`, `--quiet`, `--verbose`, `--prefer-pretty-m0` /
|
|
178
185
|
`--prefer-canonical-m0` (printed-m0 form only; note: NOT `--prefer-*-m0saic`),
|
|
179
186
|
`--background-color <color>`, `--template-repo <path...>` +
|
|
180
|
-
`--template-repo-entry <file>` (
|
|
181
|
-
|
|
187
|
+
`--template-repo-entry <file>` (a template repo folder for one run). Installed
|
|
188
|
+
repos load with no flag; `--community-repo` was removed in 0.4.0 — see
|
|
189
|
+
"Installed template repos" and the community gate below.
|
|
182
190
|
|
|
183
191
|
**Encode surface** (index.ts:~4231-4338, mirrors the Make page's Advanced panel;
|
|
184
192
|
precedence: flags > `.m0v` preset > doc-on-disk > engine default):
|
|
@@ -235,34 +243,140 @@ and neither is `doctor` (public since 0.2.0).
|
|
|
235
243
|
> `momo` / `browse-templates` finds hits; that is the router table, not the
|
|
236
244
|
> gated implementation.
|
|
237
245
|
|
|
238
|
-
## External template repos and the community gate (2026-09-16)
|
|
239
|
-
|
|
240
|
-
**Three reserved
|
|
241
|
-
namespaces `@m0saic
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
`
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
246
|
+
## External template repos and the community gate (2026-09-16; installs 2026-10-13)
|
|
247
|
+
|
|
248
|
+
**Three reserved namespaces and one reserved repo id, exact-match, nothing else
|
|
249
|
+
is checked:** template namespaces `@m0saic/`, `@m0saic-dev/`, `@m0saic-community/`
|
|
250
|
+
and repo id `@m0saic-community` (`RESERVED_TEMPLATE_NAMESPACES` /
|
|
251
|
+
`RESERVED_COMMUNITY_REPO_ID` in `index.ts`; registry compare is case-insensitive,
|
|
252
|
+
`templateRegistry.ts`). Every other namespace / repo id is open. A `--template-repo`
|
|
253
|
+
folder never gets a grant in a shipped build.
|
|
254
|
+
|
|
255
|
+
**Installed template repos (CLI 0.4.0).** `m0saic community fetch` and
|
|
256
|
+
`m0saic install <source>` put release bundles (`bundle.json` + `runtime.tgz`,
|
|
257
|
+
written by https://github.com/m0saic-project/m0saic-community-templates/blob/main/tools/pack-bundle.mjs — the starter's
|
|
258
|
+
`npm run bundle` — and attached to a GitHub release; previews and samples are
|
|
259
|
+
never downloaded) into `~/m0saic/template-repos/` (`M0SAIC_ROOT` moves it):
|
|
260
|
+
`installed.json` + one unpacked runtime per repo at `<slug>/<tag or sha-…>/`.
|
|
261
|
+
The store, download and checks are `@m0saic/product` `templateBundle.ts` (index
|
|
262
|
+
parse, source resolution, download with a 30 s idle timeout + HTTP Range resume +
|
|
263
|
+
size/sha256 checks, an extractor that writes only regular files and folders —
|
|
264
|
+
links, devices, `..`, absolute paths, backslashes, `node_modules`,
|
|
265
|
+
Windows-illegal names and duplicates are refused) and `installedTemplateRepos.ts`
|
|
266
|
+
(lock, staging + swap, the signature and reserved-namespace checks). `community
|
|
267
|
+
fetch` reads `github.com/m0saic-project/m0saic-community-templates/releases/latest/
|
|
268
|
+
download/bundle.json` (`M0SAIC_COMMUNITY_GITHUB_REPO` overrides; the bundle must
|
|
269
|
+
still verify) and installs, updates, or reports current; a signed install that no
|
|
270
|
+
longer verifies is reinstalled. Install refusals: `not-signed` (any
|
|
271
|
+
`@m0saic-community` bundle that does not verify — dev builds also take an
|
|
272
|
+
unsigned one), `reserved-namespace` (manifest ids in a reserved namespace,
|
|
273
|
+
unless the verified community repo), `check-failed` (the CLI loads the staged
|
|
274
|
+
copy once, `checkStagedTemplateRepo`, before it replaces anything),
|
|
275
|
+
`wrong-repo`, size / hash / tree mismatches, `busy` (another install holds
|
|
276
|
+
`.lock`), `declined` (the question below answered no, cancelled, or no terminal
|
|
277
|
+
and no `--yes`).
|
|
278
|
+
|
|
279
|
+
**Asking first (2026-10-13).** `install` reads `bundle.json`, then decides
|
|
280
|
+
(product `installTemplateBundle({ confirm })`: the hook runs after the index and
|
|
281
|
+
the "current" check, before the runtime download, and the store lock is taken
|
|
282
|
+
after it, so an open question never holds `.lock`; false throws `declined`). A
|
|
283
|
+
copy that is already current is reported with no question. A third-party repo
|
|
284
|
+
(a bare `.tgz` too) gets the notice, then `Install @acme (Acme Templates)
|
|
285
|
+
v2026.10.1 (12 templates, 640 KB)?` (`Update @acme v2026.10.0 → v2026.10.1
|
|
286
|
+
(…)?`, `Reinstall …?` for `--force` over the same tag, `Install the template
|
|
287
|
+
repo at <source>?` with no `bundle.json`), default No: no prints `Nothing
|
|
288
|
+
installed.`, exit 1. With no terminal (stdin or stdout not a TTY, or `CI` set)
|
|
289
|
+
and no `--yes`: `✗ Not installed: m0saic asks before it installs a template
|
|
290
|
+
repo, and there is no terminal to ask on.` + `Approve it explicitly: m0saic
|
|
291
|
+
install <source> --yes`, exit 1, the runtime never downloaded (the `setup`
|
|
292
|
+
rule). `--yes` never asks. The notice prints once per install, never at load.
|
|
293
|
+
|
|
294
|
+
**One way in for the community templates.** `community fetch` never asks.
|
|
295
|
+
`install` given the community repo's GitHub releases in any form
|
|
296
|
+
(`owner/repo[@tag]`, the repo or a release page, a release file's URL; product
|
|
297
|
+
`isCommunityBundleSource`) prints `The community templates install with m0saic
|
|
298
|
+
community fetch; running that.` and runs it: the same pins (signed only, repo id
|
|
299
|
+
`@m0saic-community` only), recorded as `community`, a tag kept
|
|
300
|
+
(`fetchCommunityTemplates({ source })`). A `@m0saic-community` bundle from
|
|
301
|
+
anywhere else (one packed on disk, tested before it is published) installs
|
|
302
|
+
through `install` with no notice and no question, printing
|
|
303
|
+
`@m0saic-community is m0saic's official community repo: it installs only if
|
|
304
|
+
m0saic's signature verifies.`: safe on an unverified claim, since a bundle naming
|
|
305
|
+
that repo id installs only if it verifies and an index that disagrees with the
|
|
306
|
+
manifest inside is refused. A community not-found says `No community templates
|
|
307
|
+
release there.` (a third-party one points at m0saic.io/developers/bundles).
|
|
308
|
+
|
|
309
|
+
**Three tiers (2026-10-13).** First-party: the built-in templates and the community
|
|
310
|
+
repo (signed; the only holder of the reserved namespaces). Second-party: a repo
|
|
311
|
+
signed by m0saic in its own namespace (one m0saic built for a customer, signed with
|
|
312
|
+
`scripts/sign-template-repo.mjs` like the starters; the starters land here too).
|
|
313
|
+
Third-party: unsigned. `install` acts on `bundle.json`'s `signed` before the
|
|
314
|
+
download: a second-party bundle prints `<repo id> (<name>) is signed by m0saic: it
|
|
315
|
+
installs only if that signature verifies.`, with no third-party notice, no question
|
|
316
|
+
and no `--yes` needed. The product refuses any bundle whose `bundle.json` says signed
|
|
317
|
+
and whose signature does not verify (`not-signed`: `bundle.json says it is signed by
|
|
318
|
+
m0saic, but the signature does not verify (<reason>)`), before the CLI's load check
|
|
319
|
+
runs any code; it is never installed unsigned. A signature is not a namespace grant:
|
|
320
|
+
a signed repo other than the community repo using reserved ids is still
|
|
321
|
+
`reserved-namespace`.
|
|
322
|
+
|
|
323
|
+
**Private repos (an access key, 2026-10-13).** A private GitHub repo that publishes
|
|
324
|
+
bundles installs like any other, plus an access key: today a fine-grained GitHub
|
|
325
|
+
token its owner hands out (that repo, Contents read-only). The key comes from
|
|
326
|
+
`--key-env <NAME>` (the variable must be set; a source that is not a GitHub release
|
|
327
|
+
exits 1), then `M0SAIC_REPO_KEY`, then the key saved for that owner/repo in
|
|
328
|
+
`~/m0saic/template-repo-keys.json` (mode 0600, by lowercase owner/repo). There is no
|
|
329
|
+
`--key <value>` flag (shell history, process lists). In a terminal, a GitHub source
|
|
330
|
+
that answers not-found with no key, or refuses the key (`auth-failed`, `no-access`),
|
|
331
|
+
prints why and asks `Access key for owner/repo (Enter to stop)` (masked), retries,
|
|
332
|
+
and saves a key that worked (`Saved the access key for owner/repo (<file>); updates
|
|
333
|
+
use it.`); keys from the environment are never saved. With no terminal, a GitHub
|
|
334
|
+
not-found adds `A private repo needs its access key: run this in a terminal to enter
|
|
335
|
+
it, or set M0SAIC_REPO_KEY.` Transport: `GET api.github.com/repos/<o>/<r>/releases/
|
|
336
|
+
latest` (or `/tags/<tag>`) lists the assets, `bundle.json` is the index, and each
|
|
337
|
+
file is `GET …/releases/assets/<id>` with `Accept: application/octet-stream` and
|
|
338
|
+
`redirect: manual`; GitHub's redirect is followed without the key, which is only ever
|
|
339
|
+
sent to `https://api.github.com` (`downloadToFile` refuses any other host). Every
|
|
340
|
+
GitHub release form carries `githubRelease` (owner/repo[@tag], the repo and release
|
|
341
|
+
pages, release file URLs; product `resolveKeyedGitHubSource`). Errors: `auth-failed`
|
|
342
|
+
(401), `no-access` (404/403 with a key; a used-up rate limit is `http-error`).
|
|
343
|
+
Records gain `github` ("owner/repo", lowercase, on any GitHub install) and
|
|
344
|
+
`access: "key"`, never the key; `m0saic install` lists keyed repos `private`;
|
|
345
|
+
`uninstall` deletes the saved key (`✓ removed … and its saved access key`). Access
|
|
346
|
+
ending changes nothing installed (it keeps working offline); updates stop.
|
|
347
|
+
|
|
348
|
+
**Loading.** `make`, `hello-world`, `list-templates` and `mcp` load the installed
|
|
349
|
+
repos with no flag, once per process (the MCP server sees a new install after a
|
|
350
|
+
restart), after any `--template-repo` folders — a folder with the same repo id
|
|
351
|
+
wins (the developer's clone over its installed release) — and quietly (no notice,
|
|
352
|
+
no "Loaded" line). An installed repo that fails to load is skipped with one
|
|
353
|
+
warning instead of stopping the command. Installed repos never become
|
|
354
|
+
`hello-world`'s front door. A not-registered `@m0saic-dev/…` /
|
|
355
|
+
`@m0saic-community/…` id adds the hint `m0saic community fetch` when the
|
|
356
|
+
community templates are not installed.
|
|
357
|
+
|
|
358
|
+
**The grant.** The reserved namespaces go only to the installed
|
|
359
|
+
`@m0saic-community` whose `release.json` passes `verifyCommunityRelease()`
|
|
360
|
+
(`@m0saic/product` `communityRelease.ts`) against `LICENSE_PUBLIC_KEYS`
|
|
361
|
+
(`publicKeys.ts` — the licence k1 family; rotation = append `k2`, ship, sign with
|
|
362
|
+
`M0SAIC_SIGNING_KID=k2`; older CLIs report `unknown-kid`). It is checked at install
|
|
363
|
+
and again on EVERY load (`gateCommunityRepo`, before the entry module is
|
|
364
|
+
imported): pass → silent; fail → the copy is skipped, never imported (not even as
|
|
365
|
+
an ordinary repo), with one stderr line: `⚠ [template-repo] the installed
|
|
366
|
+
community templates (<tag>) no longer verify (<why>); skipped. Reinstall: m0saic
|
|
367
|
+
community fetch --force`. A `--template-repo` folder whose repo id is
|
|
368
|
+
`@m0saic-community` loads under ordinary rules in a shipped build: its reserved
|
|
369
|
+
ids are refused in ONE collapsed line, `⚠ [template-repo]
|
|
370
|
+
TEMPLATE_ID_RESERVED_NAMESPACE: refused <N> ids in reserved namespaces
|
|
371
|
+
(@m0saic-community/…, @m0saic-dev/…): <id1>, <id2>, <id3>, …` (other rejection
|
|
372
|
+
codes stay one line each) → `❌ Refusing to use template repo` with the hint
|
|
373
|
+
`install its signed release instead: m0saic community fetch` → **exit 1**.
|
|
261
374
|
|
|
262
375
|
**`release.json` v1:** `{ schemaVersion: 1, tag, publishedAt, treeSha256,
|
|
263
376
|
signature: { kid, alg: "ed25519", sig: <base64url> } }` (`--unsigned` omits
|
|
264
377
|
`signature`; the legacy `{ tag, publishedAt }` reads as `missing-signature`).
|
|
265
|
-
Tree = every file under `dist/**` + `template-manifest.json` + `package.json
|
|
378
|
+
Tree = every file under `dist/**` + `template-manifest.json` + `package.json` +
|
|
379
|
+
`template-catalog.json` (each root file only when present),
|
|
266
380
|
sorted POSIX paths, one `"<path>\0<sha256>\n"` line each; `*.json` hashed on
|
|
267
381
|
`JSON.stringify(JSON.parse(text))` (BOM stripped; CRLF / re-indent survive, a
|
|
268
382
|
key reorder does not), other files raw bytes; no symlink is ever followed.
|
|
@@ -287,7 +401,8 @@ repo root from the HOST's own `node_modules` walk — the CLI's vendored copies,
|
|
|
287
401
|
the desktop's bundle, the workspace under dev — never from the checkout. So a
|
|
288
402
|
clone with nothing installed loads anywhere (0.2.0 is the first CLI that
|
|
289
403
|
can), and a `node_modules/@m0saic/template-utils` planted beside a tree is
|
|
290
|
-
never evaluated
|
|
404
|
+
never evaluated through a `--template-repo` folder (an installed copy with a
|
|
405
|
+
`node_modules` beside its tree is refused by the gate before any import). Every
|
|
291
406
|
other specifier (`sharp`, `@twemoji/svg`, relative paths) resolves as before,
|
|
292
407
|
from the checkout. CommonJS only — an `.mjs` entry's static `import`s use the
|
|
293
408
|
ESM resolver, which has no hook.
|
|
@@ -297,12 +412,33 @@ with `M0SAIC_SIGNING_KEY_FILE` (REQUIRED — no default path; production laptop
|
|
|
297
412
|
only) under `M0SAIC_SIGNING_KID` (default `k1`), self-verifies against the
|
|
298
413
|
embedded keys, writes `.gitattributes` (`* -text`). Unset or missing key =
|
|
299
414
|
refused, never a silent unsigned release; `--unsigned` = loud banner, a snapshot
|
|
300
|
-
no shipped CLI trusts.
|
|
415
|
+
no shipped CLI trusts. After the tag: `node tools/pack-bundle.mjs --tag <tag>` in
|
|
416
|
+
the snapshot, then `gh release create <tag> -R m0saic-project/m0saic-community-templates
|
|
417
|
+
bundle/bundle.json bundle/runtime.tgz bundle/previews.tgz` — what `community fetch`
|
|
418
|
+
installs (the mint's next steps print both lines).
|
|
419
|
+
|
|
420
|
+
**Samples (2026-10-20).** A pack may ship sample pictures and clips for people
|
|
421
|
+
with no assets yet: `template-manifest.json` names the folder in
|
|
422
|
+
`repo.assets.samplesDir` (say `"samples"`), and `pack-bundle.mjs` packs every
|
|
423
|
+
file under it as a third archive, `samples.tgz` (`bundle.json.samples {file,
|
|
424
|
+
sha256, size, files, unpackedSize, dir} | null`; `--no-samples` skips it; the
|
|
425
|
+
folder must sit inside the repo, hold no links, and stay under 128 MB unpacked —
|
|
426
|
+
`samples-outside-repo`, `samples-missing`, `samples-too-large`). A manifest that
|
|
427
|
+
names no folder ships none. Mosaic Desktop installs it beside the previews (best
|
|
428
|
+
effort, `installed.json.samples = installed | none | failed`) and shows the
|
|
429
|
+
folder as the Files dock's first place, "Samples" (a plain `samples/` folder in
|
|
430
|
+
a clone or the dev stand-in counts too); the CLI never fetches it. Add
|
|
431
|
+
`bundle/samples.tgz` to the `gh release create` line when the packer prints it.
|
|
301
432
|
|
|
302
433
|
**DEV seam (workspace builds only, inside `if (DEV_MODE)`):** reasons
|
|
303
|
-
`missing-release` / `missing-signature` are still granted —
|
|
304
|
-
|
|
305
|
-
|
|
434
|
+
`missing-release` / `missing-signature` are still granted — for an installed copy
|
|
435
|
+
(and `m0saic install` takes an unsigned community bundle), and for a
|
|
436
|
+
`--template-repo` folder whose repo id is `@m0saic-community`: `✓ community
|
|
437
|
+
templates from <path> (dev build: --template-repo trusted when it verifies or is
|
|
438
|
+
unsigned)` (the in-repo https://github.com/m0saic-project/m0saic-community-templates/blob/main has no `release.json`;
|
|
439
|
+
`scripts/bake-front-door-preview.mjs` and the community render e2e load it this
|
|
440
|
+
way, against the real `~/m0saic` toolchain and license; a FIFO/socket/device tree
|
|
441
|
+
is refused there) — and
|
|
306
442
|
`M0SAIC_COMMUNITY_TRUST_KEY_PEM` (+ `M0SAIC_COMMUNITY_TRUST_KEY_KID`, default `k1`)
|
|
307
443
|
adds a public key for tests. Tampered tree, forged release or unknown kid are
|
|
308
444
|
refused even in dev. The tarball folds `process.env.M0SAIC_DEV` to `"0"` and the
|
|
@@ -310,14 +446,15 @@ audit bans the token, so none of this ships.
|
|
|
310
446
|
|
|
311
447
|
**Lookalikes are a display concern** — one stderr line for UNSIGNED repos, never a
|
|
312
448
|
refusal: `⚠ [template-repo] <subject> is not an official m0saic namespace — only
|
|
313
|
-
@m0saic
|
|
314
|
-
|
|
449
|
+
@m0saic/, @m0saic-dev/, @m0saic-community/ are, and they are signed.` Printed for a
|
|
450
|
+
`--template-repo` folder on every load, for an installed repo once (at install).
|
|
451
|
+
Subject: loaded publisher namespaces starting with `@m0saic` that are not exactly
|
|
452
|
+
the reserved ones →
|
|
315
453
|
else a repoId matching `/m0saic|mosaic/i` → else `"<displayName>" (<repoId>)`.
|
|
316
454
|
Never for repoId exactly `@m0saic-community`: via `--template-repo` that gets
|
|
317
455
|
`⚠ [template-repo] repo id "@m0saic-community" is reserved for the official
|
|
318
|
-
community repo
|
|
319
|
-
|
|
320
|
-
not-verified notice.
|
|
456
|
+
community repo; this folder loads under ordinary rules (the signed release
|
|
457
|
+
installs with: m0saic community fetch).`
|
|
321
458
|
|
|
322
459
|
**Desktop:** `installVersion()` runs `validateCommunityTree` → `verifyCommunityTree`
|
|
323
460
|
(inline-requires `@m0saic/product`) → refuses `tag-mismatch`; failure =
|
|
@@ -327,20 +464,23 @@ not-verified notice.
|
|
|
327
464
|
seed/installed load path (`loadCommunityRepo`) does not re-verify; consent gate
|
|
328
465
|
unchanged.
|
|
329
466
|
|
|
330
|
-
Help
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
467
|
+
Help (test-locked, `__tests__/cli.surface.test.js`): the root epilog lists
|
|
468
|
+
`m0saic community fetch`, `m0saic install <source>` and `--template-repo`;
|
|
469
|
+
`make` / `hello-world` / `list-templates` carry `--template-repo` and point at
|
|
470
|
+
`m0saic community fetch`; `--community-repo` is an unknown option
|
|
471
|
+
(`cli.flags.test.js`). The gate and every on-disk tampering case are locked by
|
|
472
|
+
`__tests__/community-install.signing.e2e.test.js`, the install flow by
|
|
473
|
+
`__tests__/template-install.e2e.test.js`.
|
|
334
474
|
|
|
335
475
|
### Hardening (rounds 2 + 3, 2026-09-16)
|
|
336
476
|
|
|
337
|
-
**Host-first `@m0saic/*` resolution.** https://github.com/m0saic-project/m0saic-packages/blob/main/packages/platform/src/template-repos/hostFirstResolution.ts (wired in `loadTemplateRepoFromPath.ts` via `registerHostFirstRepoRoot(repoRoot)` before the native `import(file://…)` of the entry): ONE wrapper around Node's real `Module._resolveFilename` (found by walking the prototype chain — under Jest `node:module` exports a subclass copy), installed once per process, persistent so render-time lazy requires are covered. A request matching `/^@m0saic\/[^/\\]+(?:\/.*)?$/` whose requesting module lies under a registered root (realpath'd) is resolved from the HOST's own `node_modules` walk — never the checkout's. Everything else (relative paths, non-`@m0saic` deps, host code) is untouched. CJS only. Consequences: a
|
|
477
|
+
**Host-first `@m0saic/*` resolution.** https://github.com/m0saic-project/m0saic-packages/blob/main/packages/platform/src/template-repos/hostFirstResolution.ts (wired in `loadTemplateRepoFromPath.ts` via `registerHostFirstRepoRoot(repoRoot)` before the native `import(file://…)` of the entry): ONE wrapper around Node's real `Module._resolveFilename` (found by walking the prototype chain — under Jest `node:module` exports a subclass copy), installed once per process, persistent so render-time lazy requires are covered. A request matching `/^@m0saic\/[^/\\]+(?:\/.*)?$/` whose requesting module lies under a registered root (realpath'd) is resolved from the HOST's own `node_modules` walk — never the checkout's. Everything else (relative paths, non-`@m0saic` deps, host code) is untouched. CJS only. Consequences: a template repo loads from ANY directory with no `node_modules` (an installed bundle never has one); a planted `node_modules/@m0saic/*` beside a tree is never evaluated through a `--template-repo` folder (and an installed copy carrying one is refused by the gate before import); a starter's `file:` link to `@m0saic/template-utils` resolves to the same physical package — no drift.
|
|
338
478
|
|
|
339
|
-
**Fail-closed tree verification** (`inspectReleaseTree` in `communityRelease.ts`; desktop mirror `inspectCommunityTree` in the Mosaic Desktop / Web app source (not published), run by `installVersion`). A release tree is plain files and directories, nothing else. Refused, in this order after the signature check and before the hash compare: `symlink-in-tree` (ANY symlink — file or dir — under `dist/**`, `dist` itself, a signed root file, or ANY entry at the checkout root); `special-file-in-tree` (FIFO / socket / device anywhere in the same places — a FIFO named `dist/publishers.js` shadows the signed `dist/publishers/` dir and blocks or executes whoever opens it); `foreign-modules` (a `node_modules` dir at the root or under `dist/`, matched case-insensitively). `verifyCommunityRelease` lstats `release.json` before opening it (a FIFO planted there used to hang the verifier). The hasher THROWS `ReleaseTreeError` on a special entry rather than skipping it; `signCommunityRelease` refuses to sign such a tree. The signed payload is `"m0saic-community-release/v1\n<tag>\n<publishedAt>\n<treeSha256>\n"`. The CLI
|
|
479
|
+
**Fail-closed tree verification** (`inspectReleaseTree` in `communityRelease.ts`; desktop mirror `inspectCommunityTree` in the Mosaic Desktop / Web app source (not published), run by `installVersion`). A release tree is plain files and directories, nothing else. Refused, in this order after the signature check and before the hash compare: `symlink-in-tree` (ANY symlink — file or dir — under `dist/**`, `dist` itself, a signed root file, or ANY entry at the checkout root); `special-file-in-tree` (FIFO / socket / device anywhere in the same places — a FIFO named `dist/publishers.js` shadows the signed `dist/publishers/` dir and blocks or executes whoever opens it); `foreign-modules` (a `node_modules` dir at the root or under `dist/`, matched case-insensitively). `verifyCommunityRelease` lstats `release.json` before opening it (a FIFO planted there used to hang the verifier). The hasher THROWS `ReleaseTreeError` on a special entry rather than skipping it; `signCommunityRelease` refuses to sign such a tree. The signed payload is `"m0saic-community-release/v1\n<tag>\n<publishedAt>\n<treeSha256>\n"`. The CLI's skip line carries the detail for these reasons — `(foreign-modules: node_modules)`; an installed copy that fails is never imported, so a `special-file-in-tree` (an ordinary load provably hangs) cannot block the process; a dev `--template-repo` community folder with one is refused (`❌ Refusing to use template repo` + exit 1).
|
|
340
480
|
|
|
341
481
|
**Community deps.** The `@m0saic-dev/community-m` pack (the only `sharp` / `@twemoji/svg` user) was deleted as defunct on 2026-09-16; both left the community `package.json` and BOTH allowlists (`dep-allowlist.json` ↔ `TEMPLATE_REPO_DEP_ALLOWLIST`, lockstep-tested). Community templates may import only what the hosts ship: `@m0saic/{types,template-utils,dsl-stdlib,platform,dsl}` + `node:path`. A native or third-party runtime dep is a founder decision. `sharp` survives only as a root devDependency for core/templates tests.
|
|
342
482
|
|
|
343
|
-
**Accepted residual.** A third-party `--template-repo` checkout with its own `node_modules` runs its own copies of non-`@m0saic` deps — third-party code, with the notice printed; it cannot obtain a reserved identity that way (adversary-verified). DEV builds (`M0SAIC_DEV=1`, compile-stripped from the tarball) still trust an unsigned
|
|
483
|
+
**Accepted residual.** A third-party `--template-repo` checkout with its own `node_modules` runs its own copies of non-`@m0saic` deps — third-party code, with the notice printed; it cannot obtain a reserved identity that way (adversary-verified). DEV builds (`M0SAIC_DEV=1`, compile-stripped from the tarball) still trust an unsigned community copy (installed, or a `--template-repo` folder) for `missing-release` / `missing-signature` only; a tampered or forged tree is refused even in dev.
|
|
344
484
|
|
|
345
485
|
## Wireframe commands
|
|
346
486
|
|
|
@@ -115,3 +115,10 @@ at t=0 or grind at ~1 fps. That one is neither parse depth nor `geq` eval cost
|
|
|
115
115
|
that wall is engine-internal: `.ai/moat/runtime/macos-swscale-thread-cap.md`
|
|
116
116
|
(absent in the shipped copy; symptom summary: dense still-image canvases freeze
|
|
117
117
|
at t=0 / grind ~1 fps on macOS — the engine's threadGuard mitigates it).
|
|
118
|
+
Since 0.4.0 the same guard also caps every video decoder in a dense command:
|
|
119
|
+
past six threaded video inputs each `-i` takes `-threads K` (K = 32 / inputs,
|
|
120
|
+
between 1 and 4), because each mp4 decoder otherwise keeps a frame-thread pool of
|
|
121
|
+
one thread per core plus that many frames in flight — 22 inputs on an 8 GB machine
|
|
122
|
+
swapped until the kernel panicked. Stills, lavfi graphs and the engine's own qtrle
|
|
123
|
+
intermediates are not counted. Measured on the panic case (16 videos, 5 s at
|
|
124
|
+
1080p, 8 GB): 29 s, 1.3 GB peak, no swap.
|
|
@@ -53,7 +53,8 @@ duplicated here.
|
|
|
53
53
|
[`prop type: json`](reference/json-prop-type.md) ·
|
|
54
54
|
[`template flags`](reference/template-flags.md) ·
|
|
55
55
|
[`grid builder`](reference/grid.md) · [`MosaicColor`](reference/mosaic-color.md) ·
|
|
56
|
-
[`prop bindings`](reference/prop-bindings.md)
|
|
56
|
+
[`prop bindings`](reference/prop-bindings.md) ·
|
|
57
|
+
[`brand schema`](reference/brand-schema.md)
|
|
57
58
|
|
|
58
59
|
## patterns/ — distilled experience
|
|
59
60
|
|
|
@@ -63,3 +64,6 @@ duplicated here.
|
|
|
63
64
|
early template generation.
|
|
64
65
|
- [`primitive-extraction-pattern.md`](patterns/primitive-extraction-pattern.md)
|
|
65
66
|
— when/how to extract primitives (incl. Rule 7: verify NESTED).
|
|
67
|
+
- [`print-authoring.md`](patterns/print-authoring.md) — P1–P6 for a deliverable
|
|
68
|
+
that goes to paper: paper's black floor, the gap a pattern needs, recheck the
|
|
69
|
+
small inks, lock it in a test, proof on the stock.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Print authoring rules
|
|
2
|
+
|
|
3
|
+
> Templates layer. For any template whose deliverable goes to paper (cards, banners,
|
|
4
|
+
> wraps, posters). A screen is backlit; paper only reflects, and has a black floor.
|
|
5
|
+
> Tune a print palette on the screen and the shadows that read there print as one
|
|
6
|
+
> flat black. P1–P5 were paid for by a printed business card (2026-10-18); P6 by a
|
|
7
|
+
> 10 ft booth banner (2026-10-16).
|
|
8
|
+
|
|
9
|
+
## P1 — A dark pattern must clear paper's black floor
|
|
10
|
+
Paper only gets so black. Soft-proofed through Adobe's press profiles, a near-black
|
|
11
|
+
navy (#050314, L\* 1.3 on screen) prints at **L\* 11 on coated stock (FOGRA39)** and
|
|
12
|
+
**L\* 29 on uncoated stock (FOGRA29)**. Everything darker than the floor collapses
|
|
13
|
+
onto it, and the gaps just above it shrink. Keep every ink that must stay distinct
|
|
14
|
+
well clear of it: the pattern at **L\* 28 or more** in the file, at least **25 L\*
|
|
15
|
+
over the ground**. That prints about 20 apart on coated stock and 10 on uncoated.
|
|
16
|
+
|
|
17
|
+
Paid for: `@m0saic/brand/business-card/v1` and `v2` tuned the dark field on screen,
|
|
18
|
+
tiles at L\* 6.5 on an L\* 1.3 navy (`DEFAULT_FIELD_OPACITY = 0.28`,
|
|
19
|
+
https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/brand/business-card/v1/business-card.ts#L142). The
|
|
20
|
+
printed cards were flat black; the brick showed only under a strong light. The
|
|
21
|
+
soft proof agrees: those tiles print 2.4 L\* above the navy on coated stock and 1.6 on
|
|
22
|
+
uncoated. v3's dark tiles default to #434574 (L\* 31.1), which prints 20 and 10.6 above
|
|
23
|
+
(https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/brand/business-card/v3/business-card.ts#L174).
|
|
24
|
+
|
|
25
|
+
**Lift the lightness, keep the hue, and raise the chroma — then raise it again for
|
|
26
|
+
paper.** A dark brand colour lifted to print lightness at its old chroma reads as
|
|
27
|
+
grey: v3's first cut, #48465C (chroma 14), was the navy's hue but looked like slate.
|
|
28
|
+
Luminance stays put as chroma rises, so the print gap and every text contrast hold.
|
|
29
|
+
Print also takes chroma away: soft-proofed, chroma 24 printed at 21 (coated) and 17
|
|
30
|
+
(uncoated), and chroma 30 printed at 26 and 20, with the hue steady (294° → 288°).
|
|
31
|
+
Only 30 still read blue on uncoated stock, so v3 ships 30. Judge hue and chroma in
|
|
32
|
+
CIELCh through the press profile, not by eye on a backlit screen.
|
|
33
|
+
|
|
34
|
+
## P2 — A light pattern needs a smaller gap
|
|
35
|
+
Highlights survive print well. About **9 L\* under the page** reads as "clearly there,
|
|
36
|
+
still soft": business card v3's light tiles #E5DCD0 under an ivory #FAF7F2, the same
|
|
37
|
+
tint as the jewelry booth's printed fabric banner.
|
|
38
|
+
|
|
39
|
+
## P3 — Lifting a dark pattern lowers text contrast; recheck the small inks
|
|
40
|
+
Text sits on the pattern as well as the ground. When the tiles move from L\* 6.5 to
|
|
41
|
+
30.8, v2's secondary grey #8A89A3 falls to 2.7:1. v3 lifts it to #B0AFC6 (4.2:1 on
|
|
42
|
+
the tiles, 9.5:1 on the navy) so the 8 pt role / handle / command lines still read
|
|
43
|
+
(https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/brand/business-card/v3/business-card.ts#L257). Hold
|
|
44
|
+
secondary inks to **4:1 or better against the pattern**.
|
|
45
|
+
|
|
46
|
+
## P4 — Lock print contrast in a test
|
|
47
|
+
Compute L\* and contrast from the template's palette function, and assert P1–P3. A
|
|
48
|
+
later screen-only tweak then cannot quietly undo them. Reference: the "print
|
|
49
|
+
contrast" block in
|
|
50
|
+
https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/brand/business-card/v3/business-card.test.ts#L138.
|
|
51
|
+
|
|
52
|
+
## P5 — Soft-proof, then proof on the stock you are ordering
|
|
53
|
+
Stocks differ: uncoated, coated and fabric each have their own floor and gamut.
|
|
54
|
+
First soft-proof. Take each ink sRGB → the press CMYK profile (relative colorimetric
|
|
55
|
+
with black-point compensation, a RIP's usual RGB path), then → Lab (absolute
|
|
56
|
+
colorimetric) for what the sheet will measure. littleCMS through Pillow's
|
|
57
|
+
`ImageCms` does it with Adobe's `CoatedFOGRA39.icc` / `UncoatedFOGRA29.icc`. Then give
|
|
58
|
+
the reviewer a ladder, a few strengths of the knob that sets the pattern, to print
|
|
59
|
+
before the order. Keep that knob exposed (v3: `fieldOpacity`) so a stock that needs
|
|
60
|
+
more can get it without a new version.
|
|
61
|
+
|
|
62
|
+
Re-verify (as of 2026-10-18):
|
|
63
|
+
`grep -n "DEFAULT_FIELD_OPACITY\|DARK_FIELD_INK\|#B0AFC6" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/brand/business-card/v3/business-card.ts
|
|
64
|
+
|
|
65
|
+
## P6 — Print scale: the walls past 8K
|
|
66
|
+
A 10 ft banner at 150 DPI is 18000 × 14400 px, 259 MP. Measured on the 8 GB M1 Air
|
|
67
|
+
(`@qsbuilds/booth/backdrop/v1`, 2026-10-16; re-measured 2026-10-20 through the
|
|
68
|
+
engine's own jpeg path):
|
|
69
|
+
|
|
70
|
+
- **ffmpeg's frame limit** is `(8·W + 1024)·(H + 128) < 2^31 − 1`
|
|
71
|
+
(`av_image_check_size`). 18000 × 14400 passes by about 2 %; at that width a frame
|
|
72
|
+
can be at most 14679 px tall, so a 120 × 102 in artwork (pocket allowance) does not
|
|
73
|
+
fit at 150 DPI (146 DPI is the most). Compute this in the template and refuse with
|
|
74
|
+
the max DPI; do not discover it in ffmpeg.
|
|
75
|
+
- **It renders.** About 105 flat tiles plus one masked M: 128–148 s and ~1.5 GB peak
|
|
76
|
+
RSS for the lossless PNG master, which is under 1 MB for flat colour art. One scaled
|
|
77
|
+
image at the same size goes straight to a jpeg in 6 s at 1.4 GB.
|
|
78
|
+
- **JPEG at that size is the engine's job since 0.4.0.** Every `.jpg` still is
|
|
79
|
+
`mjpeg` at `yuvj444p` with the quantizer pinned to the best tables
|
|
80
|
+
(`-q:v 1 -qmin 1 -qmax 1`), one encoder thread, and `-huffman default` once the
|
|
81
|
+
canvas is 64 MP or more: the encoder's default `-huffman optimal` buffers every
|
|
82
|
+
coefficient of ~12 M blocks and failed to open (`ENOMEM`) at 259 MP. Huffman is
|
|
83
|
+
lossless, so quality is unchanged. `encodes` to `jpeg` / `png` / `tiff` / `bmp`
|
|
84
|
+
reach a real muxer too. A `.jpg` output or a `jpeg` encode is enough; the `mjpeg`
|
|
85
|
+
container with hand-set encoder options is no longer needed.
|
|
86
|
+
- **Metadata:** the engine writes no DPI and no ICC. A JPEG's JFIF APP0 density and
|
|
87
|
+
an APP2 `ICC_PROFILE` can be written afterwards by rebuilding only the header
|
|
88
|
+
segments, leaving the scan data byte-identical (the booth's `render.mjs` does this).
|
|
89
|
+
- **Proof labels:** svg-rasterized text is one inline mask per line; ~45 labels over
|
|
90
|
+
a full-canvas drawbox track drove `OVERLAY_CHAIN_DEEP` to 24. Plain `drawtext`
|
|
91
|
+
labels (no `rasterizer`) carry no mask; the only mask left is the logo.
|
|
92
|
+
- **Keep inches authoritative** and convert edges, not sizes, with one rounding
|
|
93
|
+
rule. Never upscale a proof. Verify the written file (dimensions, chroma sampling,
|
|
94
|
+
density, profile, a full decode), not the plan.
|
|
95
|
+
|
|
96
|
+
Re-verify (as of 2026-10-20):
|
|
97
|
+
`grep -n "DEFAULT_JPEG_STILL_HUGE_PIXELS\|huffman" the render engine source (not published)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# A template repo's brand schema (`m0saic.brand.json`)
|
|
2
|
+
|
|
3
|
+
**Since:** Mosaic Desktop 0.4.0 line (2026-10-19). **Applies to:** any template repo a solution or a
|
|
4
|
+
Mosaic Consumer install connects. The CLI ignores the file.
|
|
5
|
+
|
|
6
|
+
## What it is
|
|
7
|
+
|
|
8
|
+
A person using Mosaic for Jewelry, or a Mosaic Consumer install built around one business, fills in
|
|
9
|
+
their details **once** — "Your brand" on the Templates page: a name, a logo, colours, a website, where
|
|
10
|
+
they post most — and every design that has a place for those opens with them already in. The profile is
|
|
11
|
+
kept per solution on the machine as `~/m0saic/solutions/<id>/brand.m0v`, the `brand` slot of a Mosaic
|
|
12
|
+
Vocabulary (`.m0v`; see `file-formats/m0p-and-custom-field.md` §1).
|
|
13
|
+
|
|
14
|
+
A template repo says **what to ask for** and **which props take it** by shipping `m0saic.brand.json`
|
|
15
|
+
at its root, beside `m0saic.workspace.json`:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"fields": [
|
|
20
|
+
{ "key": "lyricFont", "label": "Your font", "placeholder": "The one your lyrics land in", "kind": "choice",
|
|
21
|
+
"options": [{ "value": "anton", "label": "Anton" }, { "value": "bebas-neue", "label": "Bebas Neue" }] },
|
|
22
|
+
{ "key": "textColor", "label": "Lyric colour", "placeholder": "#FFFFFF", "kind": "color" },
|
|
23
|
+
{ "key": "fontFile", "label": "Your own font file", "placeholder": "A .ttf or .otf", "kind": "file" },
|
|
24
|
+
{ "key": "channel", "label": "Where you post most", "kind": "choice",
|
|
25
|
+
"options": [{ "value": "instagram-reel", "label": "Instagram Reels" }, { "value": "tiktok", "label": "TikTok" }] }
|
|
26
|
+
],
|
|
27
|
+
"props": { "font": "lyricFont", "textColor": "textColor", "fontFile": "fontFile", "platform": "channel" }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `fields[]` — what the profile asks for. `key` (a prop-style identifier), `label`, optional
|
|
32
|
+
`placeholder`, and `kind`: `text` | `color` | `file` | `choice`. A `choice` needs `options`
|
|
33
|
+
(`{ value, label }`; a bare string is a value that is its own label). A value outside the options is
|
|
34
|
+
never seeded.
|
|
35
|
+
- `props` — template prop name → field key. **Top-level props only.** The field must be one of
|
|
36
|
+
`fields[]` (or one of the app's own, below).
|
|
37
|
+
|
|
38
|
+
## What Desktop does with it
|
|
39
|
+
|
|
40
|
+
- Reads the file when the repo loads (a local folder, or a GitHub source once installed), beside the
|
|
41
|
+
manifest; drops it when the source is forgotten.
|
|
42
|
+
- **Seeds on a fresh open.** When a template opens with no props of its own (not from a `.mosaicx`, a
|
|
43
|
+
share link or an agent), every mapped prop the template declares gets the profile's value, folded into
|
|
44
|
+
the template's **defaults** — so the open is not dirty, a rebuild keeps the values, and a saved recipe
|
|
45
|
+
carries them like any value. The person can still change any of them per post. A `media` prop takes
|
|
46
|
+
the file path; a `media[]` prop takes a one-item list.
|
|
47
|
+
- **Asks only what some prop takes.** The profile shows the fields that some mapped prop reads; a field
|
|
48
|
+
nothing reads would promise what no design does.
|
|
49
|
+
- **Merges across repos.** A solution may have a map of its own (the jewelry pack's prop names are declared
|
|
50
|
+
in the app) and several connected repos; the solution's map comes first, then each repo's, and a prop
|
|
51
|
+
is never re-mapped once mapped.
|
|
52
|
+
- **The app's own fields win for their keys:** `name`, `tagline`, `logo`, `accent`, `site`, `email`,
|
|
53
|
+
`phone`, `address`, `platform` (the app's channel list: story / feed / square / landscape / email). A
|
|
54
|
+
repo mapping `brand → name` gets the app's "Brand name" field. A repo whose `platform` prop takes
|
|
55
|
+
**its own** values should map it to a key of its own (`channel`), not `platform`.
|
|
56
|
+
|
|
57
|
+
## Authoring rules
|
|
58
|
+
|
|
59
|
+
- One schema per repo, at the root. Keep the prop names consistent across the repo's templates — the
|
|
60
|
+
schema is the API the templates adhere to; a template that spells `brandName` differently is simply
|
|
61
|
+
not seeded.
|
|
62
|
+
- Keys are identifiers (`^[a-zA-Z][a-zA-Z0-9]*$`); at most 24 fields. Odd fields and unmapped props are
|
|
63
|
+
dropped, not fatal.
|
|
64
|
+
- The file is outside the signature (`dist/` + the manifest, package and catalog): adding or changing it never
|
|
65
|
+
moves a release's verdict. It rides in `runtime.tgz` when present (packer `BRAND_FILE`, 2026-10-20), so a repo
|
|
66
|
+
installed from a release has it too — **re-pack and re-publish the bundle** after changing it; a clone or the
|
|
67
|
+
dev stand-in reads it straight from disk.
|
|
68
|
+
- The CLI reads none of this; a `.mosaicx` written by Make carries the seeded values as plain props.
|
|
69
|
+
|
|
70
|
+
## See also
|
|
71
|
+
|
|
72
|
+
- `templates/reference/prop-bindings.md` — how props reach tiles on the canvas.
|
|
73
|
+
- `file-formats/m0p-and-custom-field.md` §1 — `.m0v`, the Mosaic Vocabulary, and its `brand` slot.
|