@marver-design/marver 0.19.2 → 0.21.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/CHANGELOG.md +90 -0
- package/README.md +8 -10
- package/dist/{bake-kaf5kGZ7.mjs → bake-BID6mo-N.mjs} +1 -1
- package/dist/{boards-BmxcT3Lc.mjs → boards-BwiDAmPf.mjs} +95 -48
- package/dist/{boards-PuVzw5Wp.mjs → boards-DnLewfj8.mjs} +20 -11
- package/dist/{build-7ed5H2vT.mjs → build-C7MqQ7hq.mjs} +18 -8
- package/dist/cli.mjs +19 -13
- package/dist/{daemon-DbHvLQUL.mjs → daemon-CRZFpl6K.mjs} +1 -1
- package/dist/{dev-D3mP2x27.mjs → dev-BNZF4Mup.mjs} +5 -5
- package/dist/{init-BQYCS3EU.mjs → init-C34BY3R4.mjs} +34 -33
- package/dist/{manifest-B01PSyDc.mjs → manifest-mMfUhPtL.mjs} +8 -5
- package/dist/{plugin-DI-7NAnx.mjs → plugin-omHLCn91.mjs} +34 -26
- package/dist/{poster-DNh6N27C.mjs → poster-BvxiAzy1.mjs} +1 -1
- package/dist/{publish-bakes-Dp-ZFk3d.mjs → publish-bakes-BqzAAa3w.mjs} +8 -2
- package/dist/{shot-DMDvDbeP.mjs → shot-DswS4iRK.mjs} +7 -7
- package/docs/live-jam.md +1 -1
- package/docs/slides.md +89 -89
- package/docs/sticky-notes.md +9 -0
- package/package.json +1 -1
- package/src/client/const.ts +27 -9
- package/src/client/content/chart.tsx +8 -8
- package/src/client/content/index.tsx +5 -4
- package/src/client/content/slide.tsx +26 -201
- package/src/client/frame-host/main.tsx +10 -0
- package/src/client/shell/BoardList.tsx +111 -52
- package/src/client/shell/Comments.tsx +12 -4
- package/src/client/shell/Play.tsx +26 -14
- package/src/client/shell/Toolbar.tsx +7 -5
- package/src/client/shell/canvas/FrameNode.tsx +16 -2
- package/src/client/shell/store.ts +8 -8
- package/src/client/shell/styles.css +10 -9
- package/src/client/stage/main.tsx +56 -7
- package/src/shared/board-tree.ts +271 -140
- package/templates/AGENTS-embedded.md +3 -3
- package/templates/AGENTS-studio.md +3 -3
- package/templates/instructions/boards.md +47 -22
- package/templates/instructions/reference/deck-layouts.md +153 -199
- package/templates/instructions/reference/deck-story.md +6 -6
- package/templates/instructions/slides.md +275 -383
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as NAME } from "./cli.mjs";
|
|
2
|
-
import { d as readJson, l as detectAgent, o as writeManifest, r as scanFrames, s as DEFAULTS, u as detectHost } from "./manifest-
|
|
2
|
+
import { d as readJson, l as detectAgent, o as writeManifest, r as scanFrames, s as DEFAULTS, u as detectHost } from "./manifest-mMfUhPtL.mjs";
|
|
3
3
|
import { MANAGED_PREFIX, enumerateInstructionTemplates, hashBody, managedFile, pkgDir } from "./managed-HwHVNI3h.mjs";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, relative } from "node:path";
|
|
@@ -153,11 +153,11 @@ function init(root, opts) {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
write("slides.md", [
|
|
156
|
-
"# This project's
|
|
156
|
+
"# This project's deck look and house habits",
|
|
157
157
|
"",
|
|
158
|
-
"The agent reads BOTH the shipped
|
|
159
|
-
"file - and this file WINS where they disagree. Add your own
|
|
160
|
-
"
|
|
158
|
+
"The agent reads BOTH the shipped guide (instructions/slides.md) and this",
|
|
159
|
+
"file - and this file WINS where they disagree. Add your own deck look,",
|
|
160
|
+
"compositions and habits here; it is yours and marver never touches it.",
|
|
161
161
|
"",
|
|
162
162
|
"To grow it from examples: drop decks (PPTX, PDF, screenshots) into",
|
|
163
163
|
"design/slides-inspiration/ and ask the agent to \"study my inspiration\" -",
|
|
@@ -166,44 +166,45 @@ function init(root, opts) {
|
|
|
166
166
|
"## The deck look",
|
|
167
167
|
"",
|
|
168
168
|
"How a deck wears this brand. The agent drafts it from DESIGN.md and",
|
|
169
|
-
"theme.css on the first deck (a reviewed edit
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"shape.",
|
|
169
|
+
"theme.css on the first deck (a reviewed edit); anything it cannot settle",
|
|
170
|
+
"stays `TBD` for you. Each line shows the shape with an example - replace",
|
|
171
|
+
"the example, keep the shape.",
|
|
173
172
|
"",
|
|
174
|
-
"- **
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"- **
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"- **
|
|
186
|
-
"
|
|
187
|
-
"- **Imagery**: e.g.
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"
|
|
173
|
+
"- **Stage**: e.g. 1280×720 (the default); or `viewport: 'laptop'` on every",
|
|
174
|
+
" slide for a 16:10 deck.",
|
|
175
|
+
"- **The master**: what every content slide wears. e.g. the mark top left,",
|
|
176
|
+
" \"Prepared for <client> · Confidential · <month year>\" top right in the",
|
|
177
|
+
" muted colour; the cover and the closing step outside it.",
|
|
178
|
+
"- **Tones**: whole-slide colour sets. e.g. paper (#F1F0EA on #151616) for",
|
|
179
|
+
" working slides, ink for statements and turns, the brand blue once, for",
|
|
180
|
+
" the proof.",
|
|
181
|
+
"- **Type**: family, weights, tracking, the few sizes. e.g. the brand sans at",
|
|
182
|
+
" 400 for headlines and text, tight tracking on headlines; a display, a",
|
|
183
|
+
" heading, a body and a small label size - hierarchy by size and colour.",
|
|
184
|
+
"- **The mark**: which asset, where, how big. e.g. the lockup in the master,",
|
|
185
|
+
" paired with the client's mark on the cover.",
|
|
186
|
+
"- **Imagery**: e.g. the client's own photography, full- or half-bleed under",
|
|
187
|
+
" a uniform scrim; product screenshots on a device-less frame; no stock.",
|
|
188
|
+
"- **Drawings**: e.g. native SVG line drawings on a shared dotted grid, ink",
|
|
189
|
+
" with one accent per slide, light and dark versions.",
|
|
190
|
+
"- **Colour meaning**: e.g. accent = the point of the slide; dark red = the",
|
|
191
|
+
" problem loop; green = the outcome; never decoration.",
|
|
192
|
+
"- **Motion** (`--marver-slide-tempo` in theme.css): e.g. 350ms morphs;",
|
|
193
|
+
" drawings trace in when a slide arrives; nothing loops.",
|
|
193
194
|
"- **Numbers**: e.g. $ and k / M (lowercase k), fiscal years as FY26,",
|
|
194
195
|
" negatives in brackets, one decimal on percentages.",
|
|
195
196
|
"- **Voice**: three words the deck sounds like, and the words it never",
|
|
196
197
|
" uses. e.g. direct, warm, specific; never \"leverage\", \"seamless\", \"journey\".",
|
|
197
198
|
"- **Terminology**: user-facing word → never-shown internal word.",
|
|
198
199
|
" e.g. \"Comments\" → \"Enrichment\".",
|
|
199
|
-
"- **End card**:
|
|
200
|
-
" ask lives on the slide before
|
|
200
|
+
"- **End card**: e.g. a full-bleed photograph, the mark and a contact; the",
|
|
201
|
+
" ask lives on the slide before.",
|
|
201
202
|
"",
|
|
202
|
-
"##
|
|
203
|
+
"## Compositions",
|
|
203
204
|
"",
|
|
204
|
-
"(none yet - the shipped
|
|
205
|
+
"(none yet - the shipped idea bank in instructions/reference/deck-layouts.md applies)",
|
|
205
206
|
"",
|
|
206
|
-
"## House
|
|
207
|
+
"## House habits",
|
|
207
208
|
"",
|
|
208
209
|
"(none yet)",
|
|
209
210
|
""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as PKG, n as CONTENT_WIDTH } from "./cli.mjs";
|
|
2
|
-
import { a as listBoardFiles, d as flatten, f as
|
|
2
|
+
import { a as listBoardFiles, d as flatten, f as folderEntries, h as readTitle, m as readDescription, n as checkBoardsDir, p as isBoardName, r as checkRealDirs, s as readRegistry, t as boardFields, u as buildTree, v as hash } from "./boards-BwiDAmPf.mjs";
|
|
3
3
|
import { accessSync, constants, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { delimiter, isAbsolute, join, relative, sep } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
@@ -615,8 +615,9 @@ function setSceneTitle(root, scene, title) {
|
|
|
615
615
|
}
|
|
616
616
|
return null;
|
|
617
617
|
}
|
|
618
|
-
/** The sidebar as files say it is, for the manifest: folders in
|
|
619
|
-
*
|
|
618
|
+
/** The sidebar as files say it is, for the manifest: folders in reading order (a sub-folder
|
|
619
|
+
* after its parent, with `parent`), boards in reading order with the folder they sit in
|
|
620
|
+
* directly and their description. A boards dir we may not read (symlink)
|
|
620
621
|
* or a malformed registry yields nothing here - the API and the build say why; the
|
|
621
622
|
* orientation file must never fail to write. */
|
|
622
623
|
function scanBoards(root) {
|
|
@@ -629,10 +630,12 @@ function scanBoards(root) {
|
|
|
629
630
|
...boardFields(b.json, isBoardName)
|
|
630
631
|
}));
|
|
631
632
|
const tree = buildTree(rows, reg.folders);
|
|
633
|
+
const entries = folderEntries(tree);
|
|
632
634
|
const folderOf = /* @__PURE__ */ new Map();
|
|
633
|
-
for (const
|
|
634
|
-
const folders =
|
|
635
|
+
for (const { folder } of entries) for (const k of folder.items) if (k.kind === "board") folderOf.set(k.name, folder.name);
|
|
636
|
+
const folders = entries.map(({ folder: it, parent }) => ({
|
|
635
637
|
name: it.name,
|
|
638
|
+
...parent ? { parent } : {},
|
|
636
639
|
...it.title ? { title: it.title } : {},
|
|
637
640
|
...it.description ? { description: it.description } : {}
|
|
638
641
|
}));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { a as ROUTE, i as PKG, r as NAME } from "./cli.mjs";
|
|
3
|
-
import { a as listBoardFiles, c as BOARD_NAME, g as
|
|
3
|
+
import { _ as wireKids, a as listBoardFiles, c as BOARD_NAME, g as validateWire, h as readTitle, i as isRegularFile, l as FOLDERS_FILE, m as readDescription, n as checkBoardsDir, o as nodeExists, s as readRegistry, t as boardFields, v as hash } from "./boards-BwiDAmPf.mjs";
|
|
4
4
|
import { n as localProfile, t as isConnected } from "./profile-BjAPAJSb.mjs";
|
|
5
|
-
import { c as loadConfig, i as setSceneTitle, n as isNoteFile, o as writeManifest, r as scanFrames, t as affectedFrameIds } from "./manifest-
|
|
5
|
+
import { c as loadConfig, i as setSceneTitle, n as isNoteFile, o as writeManifest, r as scanFrames, t as affectedFrameIds } from "./manifest-mMfUhPtL.mjs";
|
|
6
6
|
import { copyFileSync, existsSync, linkSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, watch, writeFileSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -261,6 +261,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
261
261
|
}
|
|
262
262
|
const reg = readRegistry(boardsDir);
|
|
263
263
|
if (reg.state === "malformed") return json(res, 422, { error: reg.error });
|
|
264
|
+
if (reg.state === "ok" && reg.folders.some((f) => f.parent) && parsed.protocol !== 2) return json(res, 422, { error: "this tab predates folders in folders - reload the canvas, then try again" });
|
|
264
265
|
if (reg.sha256 !== b.folders) return json(res, 409, {
|
|
265
266
|
error: "folders changed on disk",
|
|
266
267
|
stale: [FOLDERS_FILE]
|
|
@@ -300,23 +301,29 @@ function apiMiddleware(root, opts = {}) {
|
|
|
300
301
|
return null;
|
|
301
302
|
};
|
|
302
303
|
const folders = [];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
304
|
+
const walk = (list, parent) => {
|
|
305
|
+
for (const [i, it] of list.entries()) {
|
|
306
|
+
if (typeof it === "string") {
|
|
307
|
+
const e = consider(it, i, parent);
|
|
308
|
+
if (e) return e;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const title = readTitle(it.title), description = readDescription(it.description);
|
|
312
|
+
folders.push({
|
|
313
|
+
name: it.folder,
|
|
314
|
+
order: i,
|
|
315
|
+
...parent ? { parent } : {},
|
|
316
|
+
...title ? { title } : {},
|
|
317
|
+
...description ? { description } : {}
|
|
318
|
+
});
|
|
319
|
+
const e = walk(wireKids(it), it.folder);
|
|
320
|
+
if (e) return e;
|
|
319
321
|
}
|
|
322
|
+
return null;
|
|
323
|
+
};
|
|
324
|
+
{
|
|
325
|
+
const e = walk(tree, null);
|
|
326
|
+
if (e) return json(res, 422, { error: e });
|
|
320
327
|
}
|
|
321
328
|
if (stale.length) return json(res, 409, {
|
|
322
329
|
error: "boards changed on disk",
|
|
@@ -335,8 +342,9 @@ function apiMiddleware(root, opts = {}) {
|
|
|
335
342
|
mkdirSync(boardsDir, { recursive: true });
|
|
336
343
|
let foldersSha = null;
|
|
337
344
|
if (folders.length) {
|
|
345
|
+
const version = folders.some((f) => f.parent) ? 2 : 1;
|
|
338
346
|
const next = JSON.stringify({
|
|
339
|
-
version
|
|
347
|
+
version,
|
|
340
348
|
folders
|
|
341
349
|
}, null, 2) + "\n";
|
|
342
350
|
atomicWrite(foldersPath, next);
|
|
@@ -422,7 +430,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
422
430
|
};
|
|
423
431
|
const asPng = url.searchParams.get("format") === "png";
|
|
424
432
|
const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
|
|
425
|
-
const { shootFrame } = await import("./shot-
|
|
433
|
+
const { shootFrame } = await import("./shot-DswS4iRK.mjs");
|
|
426
434
|
const r = await shootFrame({
|
|
427
435
|
root,
|
|
428
436
|
viewports: opts.viewports ?? {},
|
|
@@ -472,7 +480,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
472
480
|
if (typeof theme !== "string" || !/^[a-z0-9-]+$/i.test(theme)) return json(res, 400, { error: "invalid theme" });
|
|
473
481
|
const scale = body.scale === void 0 ? void 0 : body.scale;
|
|
474
482
|
if (scale !== void 0 && !(Number.isInteger(scale) && scale >= 1 && scale <= 4)) return json(res, 400, { error: "invalid scale" });
|
|
475
|
-
const { resolveFrames, shootBatch } = await import("./shot-
|
|
483
|
+
const { resolveFrames, shootBatch } = await import("./shot-DswS4iRK.mjs");
|
|
476
484
|
const sel = resolveFrames(root, body);
|
|
477
485
|
if (!sel.ok) return json(res, sel.status, { error: sel.error });
|
|
478
486
|
const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
|
|
@@ -500,7 +508,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
500
508
|
return json(res, 400, { error: "malformed JSON" });
|
|
501
509
|
}
|
|
502
510
|
if (!body || typeof body !== "object" || !Array.isArray(body.asks) || !body.asks.length || body.asks.length > 200) return json(res, 400, { error: "asks must list 1-200 frames" });
|
|
503
|
-
const { bakeBatch, ASK_MAX } = await import("./bake-
|
|
511
|
+
const { bakeBatch, ASK_MAX } = await import("./bake-BID6mo-N.mjs");
|
|
504
512
|
let manifest = {};
|
|
505
513
|
try {
|
|
506
514
|
manifest = JSON.parse(readFileSync(join(root, "design", "manifest.json"), "utf8"));
|
|
@@ -544,8 +552,8 @@ function apiMiddleware(root, opts = {}) {
|
|
|
544
552
|
if (path === "poster" && req.method === "GET") {
|
|
545
553
|
if (!ownerGated(req)) return json(res, 403, { error: "forbidden" });
|
|
546
554
|
const src = url.searchParams.get("src") ?? "";
|
|
547
|
-
const { ensurePoster, isLocalClip } = await import("./poster-
|
|
548
|
-
const { isLocalAssetRef } = await import("./build-
|
|
555
|
+
const { ensurePoster, isLocalClip } = await import("./poster-BvxiAzy1.mjs");
|
|
556
|
+
const { isLocalAssetRef } = await import("./build-C7MqQ7hq.mjs");
|
|
549
557
|
if (!isLocalAssetRef(src) || !isLocalClip(src)) return json(res, 400, { error: "src must be a clip under design/assets/" });
|
|
550
558
|
const r = await ensurePoster(join(root, "design", "assets"), src);
|
|
551
559
|
if (!r.ok) return json(res, r.error.includes("does not exist") ? 404 : 503, { error: r.error });
|
|
@@ -1027,7 +1035,7 @@ function marverPlugin(ctx) {
|
|
|
1027
1035
|
};
|
|
1028
1036
|
const prune = () => setTimeout(async () => {
|
|
1029
1037
|
try {
|
|
1030
|
-
(await import("./bake-
|
|
1038
|
+
(await import("./bake-BID6mo-N.mjs")).pruneBakes(root, bakeGen);
|
|
1031
1039
|
} catch {}
|
|
1032
1040
|
}, 500);
|
|
1033
1041
|
prune();
|
|
@@ -1166,7 +1174,7 @@ function marverPlugin(ctx) {
|
|
|
1166
1174
|
});
|
|
1167
1175
|
return;
|
|
1168
1176
|
}
|
|
1169
|
-
const { shootFrame, shootBatch, resolveFrames } = await import("./shot-
|
|
1177
|
+
const { shootFrame, shootBatch, resolveFrames } = await import("./shot-DswS4iRK.mjs");
|
|
1170
1178
|
if (ways[0] === "frame") {
|
|
1171
1179
|
if (typeof spec.frame !== "string") {
|
|
1172
1180
|
write({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { capture, t as findChrome } from "./shot-
|
|
1
|
+
import { capture, t as findChrome } from "./shot-DswS4iRK.mjs";
|
|
2
2
|
import { constants, copyFileSync, existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, sep } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { o as slideSize } from "./cli.mjs";
|
|
2
|
+
import { planShot, t as findChrome } from "./shot-DswS4iRK.mjs";
|
|
3
|
+
import { ASK_MAX, bakeBatch } from "./bake-BID6mo-N.mjs";
|
|
3
4
|
import { MIME } from "./serve-z5qtj_wJ.mjs";
|
|
4
5
|
import { copyFileSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
6
|
import { extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -43,6 +44,11 @@ function publishedAsks(boards, themes, frames, viewports, allScenes = false) {
|
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
46
|
const size = (f, n) => {
|
|
47
|
+
const sl = slideSize(f, viewports);
|
|
48
|
+
if (sl) return {
|
|
49
|
+
w: sl.width,
|
|
50
|
+
h: sl.height
|
|
51
|
+
};
|
|
46
52
|
const nw = n && typeof n.w === "number" && n.w > 0 ? n.w : void 0, nh = n && typeof n.h === "number" && n.h > 0 ? n.h : void 0;
|
|
47
53
|
const p = planShot(f, viewports, {});
|
|
48
54
|
if (p.fullHeight && !nh) return null;
|
|
@@ -283,16 +283,16 @@ function planShot(frame, viewports, override = {}) {
|
|
|
283
283
|
const cw = num(frame.contentWidth);
|
|
284
284
|
const vpObj = frame.viewport ? viewports[frame.viewport] : void 0;
|
|
285
285
|
const ow = num(override.w), oh = num(override.h);
|
|
286
|
-
const
|
|
286
|
+
const fallback = viewports.mobile ?? {
|
|
287
|
+
width: 390,
|
|
288
|
+
height: 844
|
|
289
|
+
};
|
|
290
|
+
const sl = slideSize(frame, viewports);
|
|
287
291
|
if (sl) return {
|
|
288
292
|
width: sl.width,
|
|
289
293
|
initialHeight: sl.height,
|
|
290
294
|
fullHeight: false
|
|
291
295
|
};
|
|
292
|
-
const fallback = viewports.mobile ?? {
|
|
293
|
-
width: 390,
|
|
294
|
-
height: 844
|
|
295
|
-
};
|
|
296
296
|
if (cw) {
|
|
297
297
|
const width = clamp(ow ?? vpObj?.width ?? cw, 320, 1600);
|
|
298
298
|
return {
|
|
@@ -337,8 +337,8 @@ async function shootFrame(opts) {
|
|
|
337
337
|
};
|
|
338
338
|
const plan = planShot(frame, viewports, size);
|
|
339
339
|
if (frame.kind !== "html") try {
|
|
340
|
-
const { scanAssetRefs } = await import("./build-
|
|
341
|
-
const { ensurePoster } = await import("./poster-
|
|
340
|
+
const { scanAssetRefs } = await import("./build-C7MqQ7hq.mjs");
|
|
341
|
+
const { ensurePoster } = await import("./poster-BvxiAzy1.mjs");
|
|
342
342
|
const refs = scanAssetRefs(readFileSync(join(root, frame.file), "utf8"), frame.file);
|
|
343
343
|
for (const r of refs) if (r.endsWith(".poster.png")) await ensurePoster(join(root, "design", "assets"), r.slice(0, -11));
|
|
344
344
|
} catch {}
|
package/docs/live-jam.md
CHANGED
|
@@ -105,7 +105,7 @@ obvious one:
|
|
|
105
105
|
scale}` for humans and shell-ful agents - the same renderer, one line. A batch is ONE
|
|
106
106
|
operation: one headless browser, `MARVER_SHOT_CONCURRENCY` frames at a time inside it
|
|
107
107
|
(default up to 6, sized to the machine), so a scene costs about what a frame does. Default
|
|
108
|
-
2x; `--scale 4` for a print-quality still (a slide comes back 5120×2880). A frame too tall
|
|
108
|
+
2x; `--scale 4` for a print-quality still (a 1280×720 slide comes back 5120×2880). A frame too tall
|
|
109
109
|
for the asked scale steps down and says so in `note`; the file name carries the scale
|
|
110
110
|
actually used (`…@4x.png`). A frame that ran out of settle budget still ships, marked
|
|
111
111
|
`unsettled` with a note.
|
package/docs/slides.md
CHANGED
|
@@ -3,67 +3,63 @@
|
|
|
3
3
|
A slide is an ordinary frame with `slide: true`:
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
|
-
|
|
7
|
-
export const meta = { title: 'Cover', slide: true }
|
|
6
|
+
export const meta = { title: 'Onboarding time halved', slide: true }
|
|
8
7
|
export default () => (
|
|
9
|
-
<
|
|
10
|
-
<h1
|
|
11
|
-
</
|
|
8
|
+
<main className="deck deck-ink">
|
|
9
|
+
<h1>Onboarding time halved in one quarter.</h1>
|
|
10
|
+
</main>
|
|
12
11
|
)
|
|
13
12
|
```
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
fill-in template the agent drafts on the first deck), layouts, and house
|
|
57
|
-
rules live in `design/slides.md`, which overrides the doctrine and which
|
|
58
|
-
marver never overwrites.
|
|
14
|
+
That is the whole contract. Everything inside is your code - your
|
|
15
|
+
project's components and classes, any layout, any typeface, any image,
|
|
16
|
+
any SVG drawing, any CSS or JS animation. marver adds what a deck needs
|
|
17
|
+
around it: a stage, a player, and a place on the board.
|
|
18
|
+
|
|
19
|
+
- **The stage.** A slide renders at its stage size: 1280×720 by default,
|
|
20
|
+
or the `viewport` it declares (`viewport: 'laptop'` gives a 16:10 deck at
|
|
21
|
+
1280×800). On the canvas it is a frame like any other - comments, laser,
|
|
22
|
+
variants, Live Jam - with a slide badge.
|
|
23
|
+
- **The deck.** One scene is one deck; numbered files (`01-cover.tsx`) are
|
|
24
|
+
the authoring order, and **the board's reading order is the played
|
|
25
|
+
order** - drag slides around the canvas to reorder the deck.
|
|
26
|
+
- **The host scales, not the slide.** A slide never reflows. Slides mode
|
|
27
|
+
renders each slide at its stage and scales the whole stage to the screen -
|
|
28
|
+
up on a projector, down on a laptop or a phone - and a canvas node resized
|
|
29
|
+
smaller shows the same stage, scaled, like a thumbnail. The composition you
|
|
30
|
+
approved is the one everyone sees, without breakpoints.
|
|
31
|
+
- **Notes.** `<slide>.note.md` beside the frame is its
|
|
32
|
+
[sticky note](sticky-notes.md): the aim, the talk track, the visual
|
|
33
|
+
intent, the sources. Notes ship with a published canvas, so keep anything
|
|
34
|
+
the audience must not read out of them.
|
|
35
|
+
|
|
36
|
+
## The guidance the agent reads
|
|
37
|
+
|
|
38
|
+
`marver init` ships `design/instructions/slides.md` - not a rulebook, a
|
|
39
|
+
guide: how a slide plays and animates, the **deck kit** a strong deck is
|
|
40
|
+
built on (a master shell, whole-slide tones, the brand's type, hairlines
|
|
41
|
+
and labels, a drawing helper), the craft that makes a deck look made
|
|
42
|
+
rather than typed (the brand's own voice, one idea per slide, structure
|
|
43
|
+
with space instead of boxes, real images used big, a drawing system of its
|
|
44
|
+
own, pacing, honest numbers), the method (the answer first, a slide list
|
|
45
|
+
that tells the argument, a `_brief.md`, notes per slide), and a review that
|
|
46
|
+
squints at the contact sheet. Two references sit beside it:
|
|
47
|
+
`instructions/reference/deck-story.md` (intake, answer-first structure, the
|
|
48
|
+
evidence check, the words) and `instructions/reference/deck-layouts.md` (an
|
|
49
|
+
idea bank of compositions, rebuilding an existing deck, chart craft).
|
|
50
|
+
|
|
51
|
+
Your own **deck look** - master, tones, type, mark, imagery, drawing style,
|
|
52
|
+
colour meaning, voice - lives in `design/slides.md`, which the agent drafts
|
|
53
|
+
from your brand on the first deck, which overrides the shipped guide, and
|
|
54
|
+
which marver never overwrites.
|
|
59
55
|
|
|
60
56
|
## Playing and publishing a deck
|
|
61
57
|
|
|
62
58
|
Press `p` on a board whose publish row says slides and you get slides mode:
|
|
63
|
-
the
|
|
64
|
-
the default)
|
|
65
|
-
|
|
66
|
-
Publish it with:
|
|
59
|
+
the stage scaled to the window, the standard prototype toolbars (with
|
|
60
|
+
`chrome: full`, the default), arrows / Space / click to advance, `d` cycles
|
|
61
|
+
the theme, and two views of the stage: Slide (fit to the window, room for
|
|
62
|
+
the chrome) and Fill window (edge to edge). Publish it with:
|
|
67
63
|
|
|
68
64
|
```json
|
|
69
65
|
{ "boards": { "pitch": { "max": "comment", "type": "slides",
|
|
@@ -72,8 +68,8 @@ Publish it with:
|
|
|
72
68
|
|
|
73
69
|
- `transition`: `fade` (default) or `none`.
|
|
74
70
|
- `chrome`: `full` (default - the standard prototype chrome: the top-right
|
|
75
|
-
toolbar with comment, laser, theme, and devices
|
|
76
|
-
|
|
71
|
+
toolbar with comment, laser, theme, and devices, plus the bottom-left
|
|
72
|
+
walker; a locked deck-only share also carries the brand pill),
|
|
77
73
|
`minimal` (a slim progress strip, comments, the canvas door when the
|
|
78
74
|
board is not locked, and a pending-update control), or `none` (bare
|
|
79
75
|
stage).
|
|
@@ -83,35 +79,44 @@ Publish it with:
|
|
|
83
79
|
|
|
84
80
|
Viewers land straight in the deck; the URL survives refresh and back.
|
|
85
81
|
|
|
86
|
-
## Motion
|
|
82
|
+
## Motion
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
Motion is yours to write. While a deck plays, the stage puts
|
|
85
|
+
`data-sl-play` on `<html>` for the whole show, and `data-sl-entered` once
|
|
86
|
+
each slide has arrived (removed at each swap, set again when the new slide
|
|
87
|
+
settles); `useSlidePlay()` from `/content` is the playing flag in React. Key
|
|
88
|
+
your animations off them, so the canvas, `marver shot` and thumbnails show
|
|
89
|
+
the finished slide:
|
|
90
|
+
|
|
91
|
+
```css
|
|
92
|
+
:root[data-sl-entered] .route { animation: draw 900ms ease-out both }
|
|
93
|
+
@keyframes draw { from { stroke-dashoffset: 600 } to { stroke-dashoffset: 0 } }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
On the canvas, a resting frame's animations are paused anyway (see sleep in
|
|
97
|
+
the README). marver adds three things on top:
|
|
94
98
|
|
|
95
99
|
- **Morphs**: give the same `view-transition-name` to an element on two
|
|
96
|
-
adjacent slides and it travels
|
|
97
|
-
- **Build steps**: progressive disclosure is sibling frames (`03a-`,
|
|
98
|
-
sharing morph names - every step visible and commentable on the
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
adjacent slides and it travels or resizes between them.
|
|
101
|
+
- **Build steps**: progressive disclosure is sibling frames (`03a-`,
|
|
102
|
+
`03b-`) sharing morph names - every step visible and commentable on the
|
|
103
|
+
board.
|
|
104
|
+
- **Entrance shortcuts**: `data-animate="fade-up | fade | scale-in"` +
|
|
105
|
+
`data-animate-delay="1-3"`, run once after a slide arrives. Never on an
|
|
106
|
+
element that carries a morph name.
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
`--marver-slide-tempo` in your theme (default 350ms) times the morphs and
|
|
109
|
+
the shortcuts. `prefers-reduced-motion` flattens both.
|
|
105
110
|
|
|
106
111
|
## Charts and video
|
|
107
112
|
|
|
108
113
|
- `<Chart option={...} h={420} />` - an Apache ECharts option, on a
|
|
109
114
|
fixed supported surface: series bar, line, pie, scatter, radar, gauge, heatmap, funnel, treemap, sunburst, sankey, boxplot; components grid, polar, radar, tooltip, legend, title, dataset (+ transform), markLine, markPoint, markArea, visualMap, dataZoom. Anything outside
|
|
110
115
|
it is dropped by ECharts without an error, so stay inside. marver
|
|
111
|
-
supplies the house theme
|
|
112
|
-
`
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
supplies the house theme from the slide's own ink and typeface (and
|
|
117
|
+
`--marver-slide-accent`), sizes labels for a stage, keeps the chart
|
|
118
|
+
still at rest and plays its entrance in slides mode. SVG-rendered, in a
|
|
119
|
+
lazy chunk chart-free canvases never download.
|
|
115
120
|
- `<Video src="intro.mp4" poster="intro.jpg" />` - the poster is the frame
|
|
116
121
|
at rest. Omit it on a local clip and marver renders one from the clip's own
|
|
117
122
|
first moments (`intro.mp4.poster.png` beside it - the dev server on first
|
|
@@ -122,19 +127,14 @@ slides and the entrance presets.
|
|
|
122
127
|
`ratio="9 / 16"` for a vertical clip; `autoplay` for a muted ambient loop
|
|
123
128
|
(that frame then stays live on the canvas). Remote https direct files work
|
|
124
129
|
too.
|
|
130
|
+
- Images: `Img` for a framed asset, or a plain `<img>` when the slide needs
|
|
131
|
+
full control. Slides play scaled up, so use files at least 2x the size
|
|
132
|
+
they show.
|
|
133
|
+
|
|
134
|
+
## Decks built before 0.20
|
|
125
135
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
both the entrances and the morphs between slides) from your theme and falls
|
|
132
|
-
back to the house palette. The stage is 1280×720 (`SLIDE_W` / `SLIDE_H`, exported from `/content`)
|
|
133
|
-
with asymmetric margins - 88px sides, 44px top and bottom, overridable in
|
|
134
|
-
px via `--marver-slide-pad-x` / `--marver-slide-pad-y` - leaving a 1104×632
|
|
135
|
-
content box. Morphs between slides are progressive enhancement: where
|
|
136
|
-
`document.startViewTransition` is missing, slides crossfade at the tempo.
|
|
137
|
-
Type roles, fixed: `sl-display` (160px, the one
|
|
138
|
-
oversize - a hero number, a section numeral), `sl-stat` (88px, a row of
|
|
139
|
-
figures), `sl-assertion` (56px),
|
|
140
|
-
`sl-support` (30px), `sl-body` (24px), `sl-caption` (18px).
|
|
136
|
+
Earlier decks wrap each slide in `<Slide>` and use its `sl-*` type classes.
|
|
137
|
+
`<Slide>` still exists, as an optional wrapper that fills the frame - but it
|
|
138
|
+
no longer pads the stage, centres content, sizes type or freezes animation,
|
|
139
|
+
and the `sl-*` classes carry no styles. Such a deck still plays; give it its
|
|
140
|
+
own styles (a deck kit, as the guide describes) to restore the look.
|
package/docs/sticky-notes.md
CHANGED
|
@@ -44,6 +44,15 @@ own theme.
|
|
|
44
44
|
- Comments: in comment mode (`C`) click any element of a note; the pin sits on the note, follows a
|
|
45
45
|
fold onto the tab, and the thread card opens beside the column.
|
|
46
46
|
|
|
47
|
+
## On a slide
|
|
48
|
+
|
|
49
|
+
Beside a slide (`slide: true`), the note is the presenter's script and the next agent's memory,
|
|
50
|
+
and the [slides guide](slides.md) teaches agents to write it in four short parts - **Aim** (what the
|
|
51
|
+
slide must do in the argument), **Say** (the talk track), **Visual** (what the image or drawing
|
|
52
|
+
carries) and **Source context** (where the facts come from, and their limits) - so the slide itself
|
|
53
|
+
can stay sparse. A note ships with a published canvas like any other: keep anything the audience
|
|
54
|
+
must not read out of it.
|
|
55
|
+
|
|
47
56
|
## What it is not
|
|
48
57
|
|
|
49
58
|
A note is an aside, a screen's worth of reading at most. Specs, flows and mood boards stay content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marver-design/marver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "The agent-native design canvas. A design/ folder, one command, a canvas of live frames built from your repo's real components - comment @marver and your own coding agent does the work. The tool ships no AI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|