@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
|
@@ -14,6 +14,7 @@ import { Component, createElement, useEffect, useRef, useState, type ComponentTy
|
|
|
14
14
|
import { flushSync } from 'react-dom'
|
|
15
15
|
import { createRoot } from 'react-dom/client'
|
|
16
16
|
import { frameFile, frames, layoutChain, layouts, providers } from '../frame-host/registry.ts'
|
|
17
|
+
import { SLIDE_DOC_CSS } from '../const.ts'
|
|
17
18
|
import { createInspect } from '../frame-host/inspect.js'
|
|
18
19
|
|
|
19
20
|
const params = new URLSearchParams(location.search)
|
|
@@ -21,13 +22,54 @@ const params = new URLSearchParams(location.search)
|
|
|
21
22
|
// token systems, .dark for class-keyed ones (Tailwind/shadcn). Missing the class made
|
|
22
23
|
// play render class-keyed apps light while the canvas showed them dark.
|
|
23
24
|
const bootTheme = params.get('theme') ?? 'light'
|
|
24
|
-
// slides mode
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
25
|
+
// slides mode: the shell says so in the URL; the stage stamps the attributes every slide
|
|
26
|
+
// can key its own motion off - data-sl-play (the deck is playing) on <html> for the whole
|
|
27
|
+
// show, data-sl-entered once each swap settles, data-mv-slide while the mounted frame is a
|
|
28
|
+
// `slide: true` frame. `tr=none` and prefers-reduced-motion both skip view transitions.
|
|
28
29
|
const slidesMode = params.get('slides') === '1'
|
|
29
30
|
const deckTransition = params.get('tr') ?? 'fade'
|
|
30
31
|
if (slidesMode) document.documentElement.setAttribute('data-sl-play', '')
|
|
32
|
+
|
|
33
|
+
/** The deck's playback CSS - player behaviour, so it lives with the player and serves every
|
|
34
|
+
* slide, whatever its markup: one tempo (--marver-slide-tempo) for the morphs between slides
|
|
35
|
+
* and the optional entrance presets (data-animate + data-animate-delay), which stay inert
|
|
36
|
+
* until the slide has arrived. Anything beyond these is the author's own CSS or JS. */
|
|
37
|
+
const DECK_CSS = `
|
|
38
|
+
::view-transition-group(*), ::view-transition-old(root), ::view-transition-new(root) {
|
|
39
|
+
animation-duration: var(--marver-slide-tempo, 350ms);
|
|
40
|
+
}
|
|
41
|
+
[data-sl-play] [data-animate] { opacity: 0 }
|
|
42
|
+
[data-sl-play][data-sl-entered] [data-animate] {
|
|
43
|
+
opacity: 1; animation-duration: var(--marver-slide-tempo, 350ms); animation-timing-function: cubic-bezier(.2, .7, .2, 1);
|
|
44
|
+
animation-fill-mode: both;
|
|
45
|
+
}
|
|
46
|
+
[data-sl-play][data-sl-entered] [data-animate="fade-up"] { animation-name: sl-fade-up }
|
|
47
|
+
[data-sl-play][data-sl-entered] [data-animate="fade"] { animation-name: sl-fade }
|
|
48
|
+
[data-sl-play][data-sl-entered] [data-animate="scale-in"] { animation-name: sl-scale-in }
|
|
49
|
+
[data-sl-play][data-sl-entered] [data-animate-delay="1"] { animation-delay: 80ms }
|
|
50
|
+
[data-sl-play][data-sl-entered] [data-animate-delay="2"] { animation-delay: 160ms }
|
|
51
|
+
[data-sl-play][data-sl-entered] [data-animate-delay="3"] { animation-delay: 240ms }
|
|
52
|
+
@keyframes sl-fade-up { from { opacity: 0; transform: translateY(18px) } to { opacity: 1; transform: none } }
|
|
53
|
+
@keyframes sl-fade { from { opacity: 0 } to { opacity: 1 } }
|
|
54
|
+
@keyframes sl-scale-in { from { opacity: 0; transform: scale(.94) } to { opacity: 1; transform: none } }
|
|
55
|
+
@media (prefers-reduced-motion: reduce) {
|
|
56
|
+
[data-sl-play] [data-animate] { opacity: 1; animation: none !important }
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
if (slidesMode) {
|
|
60
|
+
const el = document.createElement('style')
|
|
61
|
+
el.setAttribute('data-mv-deck', '')
|
|
62
|
+
el.textContent = DECK_CSS
|
|
63
|
+
document.head.appendChild(el)
|
|
64
|
+
}
|
|
65
|
+
// the slide document baseline, keyed on data-mv-slide (set per mounted frame): any mode can
|
|
66
|
+
// show a slide, and its geometry must not depend on what the stage showed before
|
|
67
|
+
{
|
|
68
|
+
const base = document.createElement('style')
|
|
69
|
+
base.setAttribute('data-mv-slide-doc', '')
|
|
70
|
+
base.textContent = SLIDE_DOC_CSS
|
|
71
|
+
document.head.appendChild(base)
|
|
72
|
+
}
|
|
31
73
|
const reducedMotion = typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
32
74
|
document.documentElement.dataset.theme = bootTheme
|
|
33
75
|
document.documentElement.classList.toggle('dark', bootTheme === 'dark')
|
|
@@ -45,7 +87,7 @@ document.addEventListener('gesturestart', (e) => e.preventDefault())
|
|
|
45
87
|
// B0.2: a nested scroll container hitting its boundary must not chain into the shell page
|
|
46
88
|
document.documentElement.style.overscrollBehavior = 'contain'
|
|
47
89
|
|
|
48
|
-
interface Mounted { id: string; Frame: ComponentType; wrappers: ComponentType[] }
|
|
90
|
+
interface Mounted { id: string; Frame: ComponentType; wrappers: ComponentType[]; slide: boolean }
|
|
49
91
|
|
|
50
92
|
/** Resolve a frame id to its component + wrapper chain. Modules are import()-cached by
|
|
51
93
|
* Vite, so re-resolving a chain yields the SAME component references - React keeps
|
|
@@ -59,7 +101,7 @@ async function resolve(id: string): Promise<Mounted> {
|
|
|
59
101
|
const providerKey = Object.keys(providers)[0]
|
|
60
102
|
if (providerKey) wrappers.push((await providers[providerKey]() as any).default)
|
|
61
103
|
for (const lk of layoutChain(fileKey)) wrappers.push((await layouts[lk]() as any).default)
|
|
62
|
-
return { id, Frame: mod.default, wrappers: wrappers.filter((w) => w != null) }
|
|
104
|
+
return { id, Frame: mod.default, wrappers: wrappers.filter((w) => w != null), slide: mod.meta?.slide === true }
|
|
63
105
|
}
|
|
64
106
|
|
|
65
107
|
class Boundary extends Component<{ resetKey: string; children?: ReactNode }, { err: Error | null }> {
|
|
@@ -119,7 +161,14 @@ function Stage() {
|
|
|
119
161
|
for (const el of document.querySelectorAll('[data-animate]'))
|
|
120
162
|
if (getComputedStyle(el).viewTransitionName !== 'none') el.removeAttribute('data-animate')
|
|
121
163
|
}
|
|
122
|
-
const apply = () => {
|
|
164
|
+
const apply = () => {
|
|
165
|
+
if (seq !== swapSeq.current) return
|
|
166
|
+
// the slide mark first: content primitives read it while they render
|
|
167
|
+
document.documentElement.toggleAttribute('data-mv-slide', next.slide)
|
|
168
|
+
flushSync(() => { setErr(null); setMounted(next) })
|
|
169
|
+
current.current = id
|
|
170
|
+
disarm()
|
|
171
|
+
}
|
|
123
172
|
const entered = () => {
|
|
124
173
|
if (!slidesMode || seq !== swapSeq.current) return
|
|
125
174
|
document.documentElement.setAttribute('data-sl-entered', '')
|