@marver-design/marver 0.19.1 → 0.20.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 +77 -0
- package/README.md +9 -11
- package/dist/{bake-kaf5kGZ7.mjs → bake-tAb0D6Rc.mjs} +1 -1
- package/dist/{build-7ed5H2vT.mjs → build-DwpNPl6Z.mjs} +3 -3
- package/dist/cli.mjs +18 -12
- package/dist/{dev-D3mP2x27.mjs → dev-BjdDP69b.mjs} +3 -3
- package/dist/{init-BQYCS3EU.mjs → init-DKxuRxBr.mjs} +33 -32
- package/dist/{plugin-DI-7NAnx.mjs → plugin-y4Ch7o_A.mjs} +7 -7
- package/dist/{poster-DNh6N27C.mjs → poster-FCv_nXyP.mjs} +1 -1
- package/dist/{publish-bakes-Dp-ZFk3d.mjs → publish-bakes-XH6Bac58.mjs} +8 -2
- package/dist/{shot-DMDvDbeP.mjs → shot-iw3SEcpn.mjs} +7 -7
- package/docs/live-jam.md +1 -1
- package/docs/slides.md +89 -89
- package/docs/sticky-notes.md +14 -4
- 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/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/canvas/Sticky.tsx +16 -1
- package/src/client/shell/notes.ts +43 -9
- package/src/client/shell/store.ts +17 -8
- package/src/client/shell/tidy.ts +12 -5
- package/src/client/stage/main.tsx +56 -7
- package/templates/AGENTS-embedded.md +3 -2
- package/templates/AGENTS-studio.md +3 -2
- package/templates/instructions/reference/deck-layouts.md +153 -199
- package/templates/instructions/reference/deck-story.md +6 -6
- package/templates/instructions/shape.md +6 -4
- package/templates/instructions/slides.md +275 -383
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,83 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes to `@marver-design/marver`. Format follows [Keep a Changelog](https://keepachangelog.com); versions follow semver.
|
|
4
4
|
|
|
5
|
+
## 0.20.0 - 2026-10-01
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **A slide is any frame with `slide: true` - nothing else.** The slide type keeps everything
|
|
10
|
+
a deck needs around the frame: the badge, the board's reading order as the deck order, slides
|
|
11
|
+
mode, publishing as a deck, comments and laser in play. What goes inside is the author's own
|
|
12
|
+
code: any layout, typeface, drawing or animation. `<Slide>` is now an optional wrapper that
|
|
13
|
+
fills the frame. **Breaking for decks built on it:** it no longer pads the stage, centres the
|
|
14
|
+
content, sets the `sl-*` type sizes, freezes animation at rest or outlines an overflow, and
|
|
15
|
+
the `sl-*` classes carry no styles - such a deck still plays, and needs its own styles to look
|
|
16
|
+
as it did.
|
|
17
|
+
- **The host scales the stage, not the slide.** A slide never reflows. Slides mode renders each
|
|
18
|
+
slide at its stage and scales the whole stage to the window, up as well as down - a projector
|
|
19
|
+
shows the deck at full size without the presenter picking Fill (the Slide device never went
|
|
20
|
+
past 100% before); Fill window now fits the same stage edge to edge, and slides mode offers no
|
|
21
|
+
viewport presets. A slide frame met in present or focus plays the same way - a device preset
|
|
22
|
+
never reflows it. A canvas node resized away from the stage shows it scaled and centred, like
|
|
23
|
+
a thumbnail, with comment pins mapped through the same fit; published sleep textures are
|
|
24
|
+
baked at the stage.
|
|
25
|
+
- **A deck chooses its stage.** A slide's stage is the `viewport` it declares, when the project
|
|
26
|
+
defines it - `viewport: 'laptop'` makes a 16:10 deck at 1280×800 - else 1280×720. The canvas,
|
|
27
|
+
`marver shot`, copy as image and slides mode all use it.
|
|
28
|
+
- **The slides guidance teaches instead of ruling.** `instructions/slides.md` drops the fixed
|
|
29
|
+
type roles, margins, bands, spacing scale, pacing quotas, recipe budgets and the defect
|
|
30
|
+
gate. In their place: how a slide plays and animates, the deck kit a strong deck is built on
|
|
31
|
+
(a master shell, whole-slide tones, the brand's type, hairlines and labels, a drawing
|
|
32
|
+
helper), ten craft habits distilled from the best decks built on marver, the method (the
|
|
33
|
+
answer first, a slide list that tells the argument, a `_brief.md`, an Aim / Say / Visual /
|
|
34
|
+
Source note per slide) and a review that squints at the contact sheet.
|
|
35
|
+
`reference/deck-layouts.md` becomes an idea bank, with the compositions of a strong
|
|
36
|
+
consulting deck described one by one. A new project's `design/slides.md` opens with a deck
|
|
37
|
+
look shaped the same way. Existing projects keep their `design/slides.md`; `marver init`
|
|
38
|
+
updates unedited shipped instructions and stages edited ones in `design/.local/latest/`.
|
|
39
|
+
- **Docs.** [docs/slides.md](docs/slides.md) and the README's Slides section describe the new
|
|
40
|
+
model (a slide is code; the host scales the stage; motion hooks; the deck kit), with a section
|
|
41
|
+
for decks built before 0.20. [docs/sticky-notes.md](docs/sticky-notes.md) shows a note beside a
|
|
42
|
+
slide as the presenter's script - Aim / Say / Visual / Source context - and that published notes
|
|
43
|
+
are readable by viewers.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **Motion hooks for any slide.** While a deck plays, the stage marks `data-sl-play` on
|
|
48
|
+
`<html>`, `data-sl-entered` once each slide has arrived, and `data-mv-slide` while the
|
|
49
|
+
mounted frame is a slide, so a slide's own CSS or JS animation runs when it arrives and the
|
|
50
|
+
canvas, `marver shot` and thumbnails show the finished slide. `useSlidePlay()` is exported from
|
|
51
|
+
`/content` for React. The `data-animate` entrance shortcuts and the `--marver-slide-tempo`
|
|
52
|
+
duration now work on any slide, wrapper or not, and every slide document shares one baseline
|
|
53
|
+
(no body margin) in every host, so its geometry never depends on what played before it.
|
|
54
|
+
- **`Chart` and `Img` know a slide frame without the wrapper.** Labels take the stage scale and
|
|
55
|
+
images skip the canvas's decoded-to-size path, so a slide scaled up on a projector stays sharp.
|
|
56
|
+
`Chart` also reads `--marver-slide-accent`.
|
|
57
|
+
|
|
58
|
+
### Upgrading
|
|
59
|
+
|
|
60
|
+
- Run `npx marver init` to take the new slides guidance (unedited instruction files update in
|
|
61
|
+
place; edited ones are staged in `design/.local/latest/` for your agent to merge).
|
|
62
|
+
- A deck built on `<Slide>` and the `sl-*` classes keeps playing but loses its padding, centring
|
|
63
|
+
and type sizes. Ask your agent to give it a deck kit (instructions/slides.md describes one), or
|
|
64
|
+
stay on 0.19 until you do.
|
|
65
|
+
- A deck of ordinary frames becomes a slide deck by adding `slide: true` to each frame's meta -
|
|
66
|
+
keep its `viewport` if it was designed at one (a 1280×800 deck stays 16:10) - and setting its
|
|
67
|
+
publish row to `"type": "slides"`.
|
|
68
|
+
|
|
69
|
+
## 0.19.2 - 2026-09-09
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **A note longer than its frame gets its room below.** 0.19.1 reserved a note's width beside its
|
|
74
|
+
frame and nothing under it: a long note ran on below the card and over the next row's notes and
|
|
75
|
+
frames. The column's height is now measured as it renders (markdown, fonts and diagrams decide
|
|
76
|
+
it) and fed to the layout: a composed board's next row starts under the note, the gutter still
|
|
77
|
+
the card's, and a board already saved re-applies its recipe when a note lands or grows past its
|
|
78
|
+
frame, as it did for width. Two columns running into each other count as cramped too; the empty
|
|
79
|
+
canvas under a card beside a long column is free. Leaving a device view re-checks the room the
|
|
80
|
+
restored rows have. Folding a note keeps its room, so a fold never moves the board.
|
|
81
|
+
|
|
5
82
|
## 0.19.1 - 2026-09-08
|
|
6
83
|
|
|
7
84
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Frames appear on the canvas the moment the files land. That's the loop.
|
|
|
31
31
|
## Why marver
|
|
32
32
|
|
|
33
33
|
- **Frames are real code.** Plain TSX/HTML files rendered from your repo's actual components and theme - zero imports from this package required. An approved design promotes into the app by moving a file, not by re-implementing a picture.
|
|
34
|
-
- **Decks are real code too.** A slide is a frame with `slide: true
|
|
34
|
+
- **Decks are real code too.** A slide is a frame with `slide: true` - nothing else. Any layout, typeface, drawing or animation the browser can render, a guide that teaches the agent to argue and to wear your brand rather than decorate, and a player that scales the stage to any screen without the agent writing a single breakpoint. See [Slides](#slides).
|
|
35
35
|
- **Everything hot-reloads.** The agent writes, you watch it land - live.
|
|
36
36
|
- **True viewports.** Each frame is a real iframe: drag its edge and your actual breakpoints fire.
|
|
37
37
|
- **Your agent answers on the canvas.** Tag `@marver` in a comment and it picks up the job, edits the real source, and replies in the thread - no wiring, on by default. See [Live Jam](#live-jam).
|
|
@@ -45,23 +45,21 @@ Frames appear on the canvas the moment the files land. That's the loop.
|
|
|
45
45
|
- **Prototype links.** `data-goto="scene/frame"` on any element links frames into a walkable prototype - across boards, too.
|
|
46
46
|
- **Five ways to view a board.** The canvas (frames on a plane), the board (the same, tidy), **present** (`p`: a full-screen clickable walkthrough - `data-goto` navigates, arrows step, `[` / `]` cycle variants, laser, comments, theme and device pickers in the toolbar), **focus** (one frame as a document - the reading preset for specs), and **slides** (a deck). A published board names its landing view; a frame deep link opens straight into it.
|
|
47
47
|
- **Content frames.** Specs, Mermaid diagrams, mood boards, and slides live on the same canvas as the screens - import `Doc`, `Md`, `Diagram`, `Img`, `Slide`, `Chart`, `Video` from `@marver-design/marver/content` and think a feature through before any pixels exist. Works in a repo with no app at all: idea first, design second.
|
|
48
|
-
- **Sticky notes.** A markdown file beside a frame (`cart.note.md`) or a scene (`_note.md`) becomes a yellow note left of the frame on the canvas - what it is for, how two variations differ, an open question. Markdown, `goto:` links to frames, hand-drawn Mermaid; readers comment on a note's text like on a frame's, fold it to its corner, hide them all with `n`. The layout makes room for it; published canvases carry them.
|
|
48
|
+
- **Sticky notes.** A markdown file beside a frame (`cart.note.md`) or a scene (`_note.md`) becomes a yellow note left of the frame on the canvas - what it is for, how two variations differ, an open question. Markdown, `goto:` links to frames, hand-drawn Mermaid; readers comment on a note's text like on a frame's, fold it to its corner, hide them all with `n`. The layout makes room for it, beside the frame and below; published canvases carry them.
|
|
49
49
|
- **Hi-fi at rest.** A frame at rest is its own live document, asleep: animations paused and every `backdrop-filter` element painted with a certified texture of its filtered backdrop (compiled by the dev server's headless Chrome), so a board of 30 glass screens pans like 30 statics and wakes pixel-identical on interact. Glass inside glass, blend modes and frames whose paint depends on random data or the clock stay live; `?awake=1` keeps every frame live for comparison.
|
|
50
50
|
- **Charts and video in any frame.** `Chart` (Apache ECharts, SVG, still at rest) inherits the ink, typeface and accent of whatever frame it sits in - a Tailwind dashboard, a dark spec, a slide - sizes its type to the context and follows the layout on resize. `Video` is poster-first everywhere: click to play wherever the frame is live, `autoplay` for an ambient loop, `ratio` for vertical clips; omit the poster and marver renders one from the clip. A screen with a chart or a clip is still a screen.
|
|
51
|
-
- **Copy as image.** Select a frame, press `i` - a 2x PNG of it lands on the clipboard, rendered by the same headless Chrome that serves `marver shot`; `⇧i` for 4x (a slide is 5120×2880). Paste into Slack, a doc, or a chat with your agent.
|
|
51
|
+
- **Copy as image.** Select a frame, press `i` - a 2x PNG of it lands on the clipboard, rendered by the same headless Chrome that serves `marver shot`; `⇧i` for 4x (a 1280×720 slide is 5120×2880, whatever size its node has). Paste into Slack, a doc, or a chat with your agent.
|
|
52
52
|
|
|
53
53
|
## Slides
|
|
54
54
|
|
|
55
|
-
A deck is a scene of `slide: true` frames. On the canvas they are
|
|
55
|
+
A deck is a scene of `slide: true` frames. On the canvas they are frames like any other - comment on them, laser them, fork variants, drag them to reorder the deck (the board's reading order is the play order). Press `p` on a slides board and you get slides mode: arrows / Space / click to advance, `d` cycles the theme, morphs between slides where the agent named the same element twice.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
- **A slide is just code.** No component library, no wrapper, no fixed type scale: the agent builds each slide from your project's own components and styles - an intricate diagram, a full-bleed photograph, an SVG drawing that animates in - the way it builds a screen.
|
|
58
|
+
- **A stage, and a player that scales it.** A slide renders at 1280×720, or at the `viewport` it declares (a 16:10 deck at 1280×800). Slides mode scales the whole stage to the screen - up on a projector, down on a phone - so the composition you approved is the one everyone sees.
|
|
59
|
+
- **Still at rest, alive in play.** On the canvas a resting slide is asleep like any frame; while the deck plays, the stage marks `data-sl-play` and `data-sl-entered` on the document so the agent's own animations run when a slide arrives, and `view-transition-name` morphs carry elements from one slide to the next.
|
|
60
|
+
- **Craft, not rules.** `init` ships `design/instructions/slides.md`: the deck kit a strong deck is built on (a master, whole-slide tones, the brand's type, a drawing system), the habits that make a deck look made rather than typed, the method (the answer first, a slide list that tells the argument, notes that carry the talk track), an idea bank of compositions, and your own project-owned `design/slides.md` with a **deck look** the agent drafts from your brand.
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
- **The fit is pure CSS.** The agent authors at exactly 1280×720; the root scales and centers itself to a phone, a laptop, a projector, or a resized canvas node. No `vw`, no media queries, no breakpoints - the composition you approved is the composition everyone sees, and a dev-only overflow marker outlines a slide whose content escapes the stage, or whose flex/grid child outgrows its parent.
|
|
61
|
-
- **Still at rest.** Charts are final-state SVG in a lazy chunk, videos are posters, and every CSS animation under `Slide` is suspended until slides mode plays - so a 40-slide deck of the shipped primitives pans like 40 statics on the canvas. (Your own `<canvas>`, `<video>`, or JS-driven motion stays live, as in any frame.)
|
|
62
|
-
- **A doctrine, not a template.** `init` ships `design/instructions/slides.md`: assertion-first argument, the space (three bands, the 85% rule, one spacing scale), seven silhouettes chosen before any recipe so a deck never reads as one repeated shape, 19 recipes with budgets, choreography rules, and a review gate - plus two depth references and your own project-owned `design/slides.md` with a fill-in **deck look** the agent drafts from your brand.
|
|
63
|
-
|
|
64
|
-
Publish a deck with `{ "boards": { "pitch": { "max": "read", "type": "slides", "open": "slides", "lock": true } } }` in `design/publish.json` and the link opens straight into the deck, read-only, with no canvas behind it. The [slides guide](docs/slides.md) has the rest - morphs, build steps, `Chart` and `Video`, the theme tokens.
|
|
62
|
+
Publish a deck with `{ "boards": { "pitch": { "max": "read", "type": "slides", "open": "slides", "lock": true } } }` in `design/publish.json` and the link opens straight into the deck, read-only, with no canvas behind it. The [slides guide](docs/slides.md) has the rest - motion, build steps, `Chart` and `Video`, notes.
|
|
65
63
|
|
|
66
64
|
## Collaboration
|
|
67
65
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AREA, SURFACE, pool, shotConcurrency, withBrowser } from "./shot-
|
|
1
|
+
import { AREA, SURFACE, pool, shotConcurrency, withBrowser } from "./shot-iw3SEcpn.mjs";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join, sep } from "node:path";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as ROUTE, r as NAME } from "./cli.mjs";
|
|
2
2
|
import { a as listBoardFiles, d as flatten, f as isBoardName, n as checkBoardsDir, s as readRegistry, t as boardFields, u as buildTree } from "./boards-BmxcT3Lc.mjs";
|
|
3
3
|
import { c as loadConfig, r as scanFrames, u as detectHost } from "./manifest-B01PSyDc.mjs";
|
|
4
|
-
import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-
|
|
4
|
+
import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-y4Ch7o_A.mjs";
|
|
5
5
|
import { cpSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { basename, dirname, join, sep } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -683,7 +683,7 @@ async function buildSite(root, boardsFlag, allBoardsFlag, embedSeeds, textures =
|
|
|
683
683
|
const realAssets = existsSync(assetsDir) ? realpathSync(assetsDir) : null;
|
|
684
684
|
for (const r of refs) {
|
|
685
685
|
if (!isLocalAssetRef(r) || !r.endsWith(".poster.png") || existsSync(join(assetsDir, r))) continue;
|
|
686
|
-
const { ensurePoster } = await import("./poster-
|
|
686
|
+
const { ensurePoster } = await import("./poster-FCv_nXyP.mjs");
|
|
687
687
|
const g = await ensurePoster(assetsDir, r.slice(0, -11));
|
|
688
688
|
if (!g.ok) throw new Error(`design/assets/${r}: ${g.error}`);
|
|
689
689
|
console.log(` poster: rendered design/assets/${r} (${g.width}×${g.height})`);
|
|
@@ -750,7 +750,7 @@ async function buildSite(root, boardsFlag, allBoardsFlag, embedSeeds, textures =
|
|
|
750
750
|
}));
|
|
751
751
|
let textureLine = " textures: skipped (--no-textures)";
|
|
752
752
|
if (textures && bakeGen) {
|
|
753
|
-
const { bakePublished } = await import("./publish-bakes-
|
|
753
|
+
const { bakePublished } = await import("./publish-bakes-XH6Bac58.mjs");
|
|
754
754
|
const pubFrameFile = new Map(pubFrames.map((f) => [f.id, f]));
|
|
755
755
|
const r = await bakePublished({
|
|
756
756
|
root,
|
package/dist/cli.mjs
CHANGED
|
@@ -17,19 +17,25 @@ const CONTENT_WIDTH = {
|
|
|
17
17
|
document: 760,
|
|
18
18
|
wide: 1280
|
|
19
19
|
};
|
|
20
|
-
/** The slide stage
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
/** The default slide stage: 16:9 at 1280×720, deliberately NOT a config viewport - no
|
|
21
|
+
* migration for existing projects, no deck device in sweeps. Dependency-neutral so
|
|
22
|
+
* server (shot) and shell (store, play) share it. */
|
|
23
23
|
const SLIDE_INTRINSIC = {
|
|
24
24
|
width: 1280,
|
|
25
25
|
height: 720
|
|
26
26
|
};
|
|
27
|
-
/** The one
|
|
28
|
-
* `slide: true`
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
/** The one sizing rule for slide frames, shared by canvas, shot and slides mode: a
|
|
28
|
+
* `slide: true` frame's stage is its declared viewport when the project defines one
|
|
29
|
+
* (a 16:10 deck authored at `laptop` stays 1280×800), else the 1280×720 default. A
|
|
30
|
+
* slide is an ordinary frame at that size - slides mode scales the whole stage to the
|
|
31
|
+
* screen, so the frame never has to. */
|
|
32
|
+
function slideSize(frame, viewports = {}) {
|
|
33
|
+
if (!frame.slide) return null;
|
|
34
|
+
const vp = frame.viewport ? viewports[frame.viewport] : void 0;
|
|
35
|
+
return vp ? {
|
|
36
|
+
width: vp.width,
|
|
37
|
+
height: vp.height
|
|
38
|
+
} : SLIDE_INTRINSIC;
|
|
33
39
|
}
|
|
34
40
|
//#endregion
|
|
35
41
|
//#region src/cli/name.ts
|
|
@@ -72,14 +78,14 @@ const resolve$1 = (dir) => {
|
|
|
72
78
|
};
|
|
73
79
|
const cli = cac(NAME);
|
|
74
80
|
cli.command("init", "Scaffold design/ in this repo").option("--mode <mode>", "studio | embedded", { default: "studio" }).option("--no-demo", "Skip the demo scene (the demo ships unless this flag is passed)").option("--root <dir>", "Host repo root", { default: "." }).action(async (opts) => {
|
|
75
|
-
const { init } = await import("./init-
|
|
81
|
+
const { init } = await import("./init-DKxuRxBr.mjs");
|
|
76
82
|
init(resolve$1(opts.root), {
|
|
77
83
|
mode: opts.mode === "embedded" ? "embedded" : "studio",
|
|
78
84
|
demo: opts.demo !== false
|
|
79
85
|
});
|
|
80
86
|
});
|
|
81
87
|
for (const [name, desc] of [["dev", "Start the local canvas (everything on: hot reload, comments, Live Jam)"], ["canvas", "Start the local canvas - same as dev"]]) cli.command(name, desc).option("--root <dir>", "Host repo root", { default: "." }).option("--port <port>", "Port (default 5199)").action(async (opts) => {
|
|
82
|
-
const { dev } = await import("./dev-
|
|
88
|
+
const { dev } = await import("./dev-BjdDP69b.mjs");
|
|
83
89
|
let port;
|
|
84
90
|
if (opts.port !== void 0) {
|
|
85
91
|
const n = Number(opts.port);
|
|
@@ -89,7 +95,7 @@ for (const [name, desc] of [["dev", "Start the local canvas (everything on: hot
|
|
|
89
95
|
await dev(resolve$1(opts.root), port);
|
|
90
96
|
});
|
|
91
97
|
cli.command("build", "Static export → design/.dist (what ships comes from design/publish.json - publishing is default-closed)").option("--boards <names>", "Publish only these boards (comma-separated); overrides the publish policy").option("--all-boards", "Publish every board - the loud override for the default-closed policy").option("--embed-seeds", "Copy comment history INTO the web root (identifying - every event carries its author's email)").option("--no-textures", "Skip compiling the glass textures the published frames rest under (needs Chrome; a CI without one skips on its own)").option("--root <dir>", "Host repo root", { default: "." }).action(async (opts) => {
|
|
92
|
-
const { buildSite } = await import("./build-
|
|
98
|
+
const { buildSite } = await import("./build-DwpNPl6Z.mjs");
|
|
93
99
|
try {
|
|
94
100
|
const boards = opts.boards === void 0 ? void 0 : typeof opts.boards === "string" ? opts.boards : "";
|
|
95
101
|
await buildSite(resolve$1(opts.root), boards, opts.allBoards === true, opts.embedSeeds === true, opts.textures !== false && !process.env.MARVER_NO_TEXTURES);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as PKG, r as NAME } from "./cli.mjs";
|
|
2
2
|
import { c as loadConfig, u as detectHost } from "./manifest-B01PSyDc.mjs";
|
|
3
|
-
import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-
|
|
3
|
+
import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-y4Ch7o_A.mjs";
|
|
4
4
|
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -452,12 +452,12 @@ async function dev(root, portFlag) {
|
|
|
452
452
|
console.log(`\n ${NAME} · ${projectName} → http://localhost:${port}/\n`);
|
|
453
453
|
setTimeout(async () => {
|
|
454
454
|
try {
|
|
455
|
-
await (await import("./shot-
|
|
455
|
+
await (await import("./shot-iw3SEcpn.mjs")).sweepGhosts((m) => console.log(` ${m}`));
|
|
456
456
|
} catch {}
|
|
457
457
|
}, 0);
|
|
458
458
|
try {
|
|
459
459
|
const { staleManagedInstructions } = await import("./managed-HwHVNI3h.mjs");
|
|
460
|
-
const { installedVersion } = await import("./plugin-
|
|
460
|
+
const { installedVersion } = await import("./plugin-y4Ch7o_A.mjs").then((n) => n.i);
|
|
461
461
|
const stale = staleManagedInstructions(root);
|
|
462
462
|
if (stale.length) {
|
|
463
463
|
const shown = stale.slice(0, 4).join(", ") + (stale.length > 4 ? `, +${stale.length - 4} more` : "");
|
|
@@ -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
|
""
|
|
@@ -422,7 +422,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
422
422
|
};
|
|
423
423
|
const asPng = url.searchParams.get("format") === "png";
|
|
424
424
|
const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
|
|
425
|
-
const { shootFrame } = await import("./shot-
|
|
425
|
+
const { shootFrame } = await import("./shot-iw3SEcpn.mjs");
|
|
426
426
|
const r = await shootFrame({
|
|
427
427
|
root,
|
|
428
428
|
viewports: opts.viewports ?? {},
|
|
@@ -472,7 +472,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
472
472
|
if (typeof theme !== "string" || !/^[a-z0-9-]+$/i.test(theme)) return json(res, 400, { error: "invalid theme" });
|
|
473
473
|
const scale = body.scale === void 0 ? void 0 : body.scale;
|
|
474
474
|
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-
|
|
475
|
+
const { resolveFrames, shootBatch } = await import("./shot-iw3SEcpn.mjs");
|
|
476
476
|
const sel = resolveFrames(root, body);
|
|
477
477
|
if (!sel.ok) return json(res, sel.status, { error: sel.error });
|
|
478
478
|
const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
|
|
@@ -500,7 +500,7 @@ function apiMiddleware(root, opts = {}) {
|
|
|
500
500
|
return json(res, 400, { error: "malformed JSON" });
|
|
501
501
|
}
|
|
502
502
|
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-
|
|
503
|
+
const { bakeBatch, ASK_MAX } = await import("./bake-tAb0D6Rc.mjs");
|
|
504
504
|
let manifest = {};
|
|
505
505
|
try {
|
|
506
506
|
manifest = JSON.parse(readFileSync(join(root, "design", "manifest.json"), "utf8"));
|
|
@@ -544,8 +544,8 @@ function apiMiddleware(root, opts = {}) {
|
|
|
544
544
|
if (path === "poster" && req.method === "GET") {
|
|
545
545
|
if (!ownerGated(req)) return json(res, 403, { error: "forbidden" });
|
|
546
546
|
const src = url.searchParams.get("src") ?? "";
|
|
547
|
-
const { ensurePoster, isLocalClip } = await import("./poster-
|
|
548
|
-
const { isLocalAssetRef } = await import("./build-
|
|
547
|
+
const { ensurePoster, isLocalClip } = await import("./poster-FCv_nXyP.mjs");
|
|
548
|
+
const { isLocalAssetRef } = await import("./build-DwpNPl6Z.mjs");
|
|
549
549
|
if (!isLocalAssetRef(src) || !isLocalClip(src)) return json(res, 400, { error: "src must be a clip under design/assets/" });
|
|
550
550
|
const r = await ensurePoster(join(root, "design", "assets"), src);
|
|
551
551
|
if (!r.ok) return json(res, r.error.includes("does not exist") ? 404 : 503, { error: r.error });
|
|
@@ -1027,7 +1027,7 @@ function marverPlugin(ctx) {
|
|
|
1027
1027
|
};
|
|
1028
1028
|
const prune = () => setTimeout(async () => {
|
|
1029
1029
|
try {
|
|
1030
|
-
(await import("./bake-
|
|
1030
|
+
(await import("./bake-tAb0D6Rc.mjs")).pruneBakes(root, bakeGen);
|
|
1031
1031
|
} catch {}
|
|
1032
1032
|
}, 500);
|
|
1033
1033
|
prune();
|
|
@@ -1166,7 +1166,7 @@ function marverPlugin(ctx) {
|
|
|
1166
1166
|
});
|
|
1167
1167
|
return;
|
|
1168
1168
|
}
|
|
1169
|
-
const { shootFrame, shootBatch, resolveFrames } = await import("./shot-
|
|
1169
|
+
const { shootFrame, shootBatch, resolveFrames } = await import("./shot-iw3SEcpn.mjs");
|
|
1170
1170
|
if (ways[0] === "frame") {
|
|
1171
1171
|
if (typeof spec.frame !== "string") {
|
|
1172
1172
|
write({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { capture, t as findChrome } from "./shot-
|
|
1
|
+
import { capture, t as findChrome } from "./shot-iw3SEcpn.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-iw3SEcpn.mjs";
|
|
3
|
+
import { ASK_MAX, bakeBatch } from "./bake-tAb0D6Rc.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-DwpNPl6Z.mjs");
|
|
341
|
+
const { ensurePoster } = await import("./poster-FCv_nXyP.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.
|