@honeydeck/honeydeck 0.3.0 → 0.5.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/DEVELOPMENT.md +4 -1
- package/Readme.md +2 -2
- package/SPEC.md +3 -3
- package/docs/components-browser-frame.md +34 -0
- package/docs/components-keyboard.md +31 -0
- package/docs/components-list-style.md +49 -0
- package/docs/components-notes.md +36 -0
- package/docs/components-reveal-group.md +58 -0
- package/docs/components-reveal-with.md +37 -0
- package/docs/components-reveal.md +33 -0
- package/docs/components-timeline-steps.md +48 -0
- package/docs/components.md +13 -54
- package/docs/configuration.md +11 -0
- package/docs/deeper-dive.md +30 -7
- package/docs/getting-started.md +2 -2
- package/docs/navigation.md +1 -1
- package/docs/pdf-export.md +4 -2
- package/docs/presenter-mode.md +6 -3
- package/docs/skills.md +3 -3
- package/docs/slidev-migration.md +3 -0
- package/docs/steps-and-reveals.md +143 -8
- package/package.json +4 -1
- package/skills/SPEC.md +2 -2
- package/skills/honeydeck/SKILL.md +2 -2
- package/skills/slidev-migration/SKILL.md +1 -0
- package/src/SPEC.md +8 -3
- package/src/cli/SPEC.md +3 -2
- package/src/cli/pdf.ts +11 -4
- package/src/layouts/SPEC.md +1 -1
- package/src/remark/SPEC.md +102 -2
- package/src/remark/code-utils.ts +151 -0
- package/src/remark/shiki-code-blocks.ts +329 -136
- package/src/remark/step-numbering.ts +408 -103
- package/src/runtime/Deck.tsx +133 -116
- package/src/runtime/EffectiveColorModeContext.tsx +37 -0
- package/src/runtime/SPEC.md +21 -8
- package/src/runtime/SlideCanvas.tsx +19 -16
- package/src/runtime/SlideScaleContext.tsx +23 -0
- package/src/runtime/components/CodeBlock.tsx +19 -202
- package/src/runtime/components/CodeBlockCopyButton.tsx +64 -0
- package/src/runtime/components/CodeBlockShared.ts +17 -0
- package/src/runtime/components/Fade.tsx +51 -0
- package/src/runtime/components/FadeGroup.tsx +175 -0
- package/src/runtime/components/FadeWith.tsx +54 -0
- package/src/runtime/components/MagicCodeBlock.tsx +223 -0
- package/src/runtime/components/NavBar.tsx +1 -1
- package/src/runtime/components/NormalCodeBlock.tsx +128 -0
- package/src/runtime/components/Reveal.tsx +27 -27
- package/src/runtime/components/RevealGroup.tsx +143 -41
- package/src/runtime/components/RevealWith.tsx +63 -0
- package/src/runtime/components/SPEC.md +112 -7
- package/src/runtime/components/TimelineReveal.tsx +81 -0
- package/src/runtime/components/index.ts +13 -5
- package/src/runtime/components/timelineVisibility.ts +45 -0
- package/src/runtime/index.ts +9 -1
- package/src/runtime/navigation.ts +6 -4
- package/src/runtime/presentationApi.ts +449 -0
- package/src/runtime/views/PresenterCastButton.tsx +39 -0
- package/src/runtime/views/PresenterView.tsx +21 -4
- package/src/runtime/views/SPEC.md +7 -5
- package/src/theme/base.css +67 -2
- package/src/vite-plugin/SPEC.md +20 -2
- package/src/vite-plugin/index.ts +16 -2
- package/src/vite-plugin/layout-demo-crawler.ts +304 -33
- package/src/vite-plugin/splitter.ts +1 -0
- package/src/vite-plugin/virtual-modules.ts +16 -6
package/DEVELOPMENT.md
CHANGED
|
@@ -211,7 +211,10 @@ honeydeck
|
|
|
211
211
|
│ └── bee/ → src/layouts/bee/*
|
|
212
212
|
└── ./components/
|
|
213
213
|
├── . → src/runtime/components/index.ts
|
|
214
|
-
|
|
214
|
+
├── code-block → src/runtime/components/CodeBlock.tsx
|
|
215
|
+
└── code-block/
|
|
216
|
+
├── normal → src/runtime/components/NormalCodeBlock.tsx
|
|
217
|
+
└── magic → src/runtime/components/MagicCodeBlock.tsx
|
|
215
218
|
```
|
|
216
219
|
|
|
217
220
|
---
|
package/Readme.md
CHANGED
|
@@ -22,8 +22,8 @@ Decks are plain MDX files separated into slides with `---`; see the first deck e
|
|
|
22
22
|
- [Deeper dive](docs/deeper-dive.md) - CLI options, authoring patterns, imports, themes, architecture, and agent skills
|
|
23
23
|
- [Slides](docs/slides.md) - separators, multiple files, layouts, and assets
|
|
24
24
|
- [Configuration](docs/configuration.md) - deck-level and slide-level frontmatter
|
|
25
|
-
- [Steps and reveals](docs/steps-and-reveals.md) - timeline, reveal groups, code steps, and PDF behavior
|
|
26
|
-
- [Components](docs/components.md) - core Honeydeck components such as `
|
|
25
|
+
- [Steps and reveals](docs/steps-and-reveals.md) - timeline, reveal groups, code steps, Magic Code, and PDF behavior
|
|
26
|
+
- [Components](docs/components.md) - core Honeydeck components such as `Reveal`, `RevealWith`, `BrowserFrame`, and `Keyboard`
|
|
27
27
|
- [Customization](docs/customization.md) - themes, layout sets, Tailwind tokens, custom layouts, and layout demos
|
|
28
28
|
- [Navigation](docs/navigation.md) - keyboard, touch, overview mode, and URL state
|
|
29
29
|
- [Mobile and touch](docs/mobile.md) - mobile controls, touch gestures, and pinch zoom
|
package/SPEC.md
CHANGED
|
@@ -33,7 +33,7 @@ The Honeydeck specification is distributed:
|
|
|
33
33
|
| Styling | Explicit CSS imports, Tailwind-compatible utilities, and CSS custom properties | [`src/theme/SPEC.md`](src/theme/SPEC.md) |
|
|
34
34
|
| Markdown | MDX with GitHub-flavored Markdown tables; canonical docs use `docs/*.md` | [`src/vite-plugin/SPEC.md`](src/vite-plugin/SPEC.md), [`src/remark/SPEC.md`](src/remark/SPEC.md) |
|
|
35
35
|
| PDF | Rasterized slide pages matching browser rendering | [`src/cli/SPEC.md`](src/cli/SPEC.md) |
|
|
36
|
-
| Syntax highlighting | Built-in code highlighting
|
|
36
|
+
| Syntax highlighting | Built-in code highlighting, runtime step dimming, and Magic Code transitions | [`src/remark/SPEC.md`](src/remark/SPEC.md) |
|
|
37
37
|
| Icons | `lucide-react` suffixed `...Icon` component imports | [`src/SPEC.md`](src/SPEC.md) |
|
|
38
38
|
| Public imports | Explicit import subpaths for components, layouts, themes, and types | [`src/SPEC.md`](src/SPEC.md) |
|
|
39
39
|
|
|
@@ -55,9 +55,9 @@ The Honeydeck specification is distributed:
|
|
|
55
55
|
| Starter templates | [`src/cli/templates/SPEC.md`](src/cli/templates/SPEC.md) | generated project tree, starter `deck.mdx`, `styles.css`, demo component |
|
|
56
56
|
| Agent skills | [`skills/SPEC.md`](skills/SPEC.md) | bundled installable skills and installer expectations |
|
|
57
57
|
| Deck loading / frontmatter | [`src/vite-plugin/SPEC.md`](src/vite-plugin/SPEC.md) | deck entry, slide separators, MDX imports, assets, Markdown features, frontmatter |
|
|
58
|
-
| Remark transforms | [`src/remark/SPEC.md`](src/remark/SPEC.md) | code highlighting
|
|
58
|
+
| Remark transforms | [`src/remark/SPEC.md`](src/remark/SPEC.md) | timeline annotation, code highlighting, step-through code metadata, and Magic Code syntax |
|
|
59
59
|
| Runtime | [`src/runtime/SPEC.md`](src/runtime/SPEC.md) | timeline semantics, keyboard/touch navigation, SPA/build behavior, runtime errors |
|
|
60
|
-
| Runtime components | [`src/runtime/components/SPEC.md`](src/runtime/components/SPEC.md) | `Reveal`, `RevealGroup`, `TimelineSteps`, `ListStyle`, `Keyboard`, `BrowserFrame`, `Notes` |
|
|
60
|
+
| Runtime components | [`src/runtime/components/SPEC.md`](src/runtime/components/SPEC.md) | `Reveal`, `RevealWith`, `RevealGroup`, `Fade`, `FadeWith`, `FadeGroup`, `TimelineSteps`, `ListStyle`, `Keyboard`, `BrowserFrame`, `Notes` |
|
|
61
61
|
| Runtime views | [`src/runtime/views/SPEC.md`](src/runtime/views/SPEC.md) | presenter mode, overview mode, theme/layout/component reference pages |
|
|
62
62
|
| Layouts and customization | [`src/layouts/SPEC.md`](src/layouts/SPEC.md) | custom theme/layout/component model, built-in layouts, layout props, demos, layout resolution |
|
|
63
63
|
| Theme | [`src/theme/SPEC.md`](src/theme/SPEC.md) | design tokens, base theme CSS, Tailwind mapping, color mode behavior |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# BrowserFrame
|
|
2
|
+
|
|
3
|
+
Use `<BrowserFrame>` to show a live iframe inside a macOS-style browser window.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { BrowserFrame } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
<BrowserFrame
|
|
9
|
+
src="https://example.com"
|
|
10
|
+
addressBar="example.com"
|
|
11
|
+
fallbackImage="/example-fallback-light.png"
|
|
12
|
+
fallbackDarkImage="/example-fallback-dark.png"
|
|
13
|
+
/>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The component renders browser chrome with macOS traffic-light controls and an optional address bar. It can show a light or dark fallback screenshot when the iframe cannot be loaded. It uses Honeydeck theme tokens for the frame, border, typography, and iframe surface.
|
|
17
|
+
|
|
18
|
+
## Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| `src` | `string` | Required | URL loaded by the iframe. Use an external `https://` URL or a local Vite-served route like `/demo.html`. |
|
|
23
|
+
| `addressBar` | `ReactNode` | — | Optional content shown in the address-bar field. Omit it to hide the input-like address field. |
|
|
24
|
+
| `fallbackImage` | `string` | — | Light/default screenshot shown when iframe loading fails or fallback mode is toggled on. |
|
|
25
|
+
| `fallbackDarkImage` | `string` | — | Dark-mode screenshot shown when fallback mode is active. Falls back to `fallbackImage` when omitted. |
|
|
26
|
+
| `fallbackAlt` | `string` | `Fallback preview` | Accessible alt text for fallback images. |
|
|
27
|
+
| `defaultFallback` | `boolean` | `false` | Starts in fallback mode instead of loading the iframe. Useful for deterministic demos, final-state screenshots, and PDF-friendly decks. |
|
|
28
|
+
| `aspectRatio` | `CSSProperties["aspectRatio"]` | `16 / 9` | Aspect ratio for the full browser window, including chrome. Accepts values such as `16 / 9`, `"4 / 3"`, or `1.6`. |
|
|
29
|
+
| `className` | `string` | — | Additional CSS class for the outer browser frame. |
|
|
30
|
+
| `iframeClassName` | `string` | — | Additional CSS class for the iframe element. Only applies while live iframe content is rendered. |
|
|
31
|
+
|
|
32
|
+
Standard iframe attributes such as `allow`, `sandbox`, `loading`, and `referrerPolicy` are forwarded.
|
|
33
|
+
|
|
34
|
+
When fallback mode is active, the browser frame shows a badge in the top chrome, visually aligned with the address bar. A fourth round control sits beside the macOS traffic-light controls and only becomes visible when the control itself is hovered or keyboard-focused; it toggles fallback mode.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Keyboard
|
|
2
|
+
|
|
3
|
+
Use `<Keyboard>` to show one key or a shortcut in slide prose.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { Keyboard } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
Press <Keyboard>Esc</Keyboard> to close overview.
|
|
9
|
+
|
|
10
|
+
Advance with <Keyboard keys="Space" />.
|
|
11
|
+
|
|
12
|
+
Open command palette with <Keyboard keys={["Ctrl", "Shift", "P"]} />.
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`keys` accepts a single value or an ordered array. When `keys` is omitted, `children` is rendered as one key. Array values render one `<kbd>` per key, separated by `+` by default:
|
|
16
|
+
|
|
17
|
+
```mdx
|
|
18
|
+
<Keyboard keys={["⌘", "K"]} />
|
|
19
|
+
<Keyboard keys={["Ctrl", "Alt", "Delete"]} separator=" " />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
| --- | --- | --- | --- |
|
|
26
|
+
| `keys` | `ReactNode \| ReactNode[]` | — | Key label or ordered shortcut key labels. |
|
|
27
|
+
| `children` | `ReactNode` | — | Single key label when `keys` is omitted. |
|
|
28
|
+
| `separator` | `ReactNode` | `+` | Separator rendered between array entries. |
|
|
29
|
+
| `className` | `string` | — | Custom class for the outer wrapper. |
|
|
30
|
+
|
|
31
|
+
The component uses semantic `<kbd>` markup, is inline by default, uses Honeydeck theme styling, and does not add timeline steps.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ListStyle
|
|
2
|
+
|
|
3
|
+
Use `<ListStyle>` to style Markdown, HTML, or JSX lists inside a wrapper.
|
|
4
|
+
|
|
5
|
+
By default it removes native markers:
|
|
6
|
+
|
|
7
|
+
```mdx
|
|
8
|
+
import { ListStyle } from '@honeydeck/honeydeck'
|
|
9
|
+
|
|
10
|
+
<ListStyle>
|
|
11
|
+
- No marker
|
|
12
|
+
- Still aligned
|
|
13
|
+
</ListStyle>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Pass `bullets` to render custom markers:
|
|
17
|
+
|
|
18
|
+
```mdx
|
|
19
|
+
import { ListStyle } from '@honeydeck/honeydeck'
|
|
20
|
+
import { CheckIcon, CircleIcon } from 'lucide-react'
|
|
21
|
+
|
|
22
|
+
<ListStyle bullets={[<CheckIcon />, <CircleIcon />]}>
|
|
23
|
+
- Level one uses a check icon
|
|
24
|
+
- Level two uses a circle icon
|
|
25
|
+
</ListStyle>
|
|
26
|
+
|
|
27
|
+
<ListStyle bullets={["→", "–", "·"]}>
|
|
28
|
+
- Level one
|
|
29
|
+
- Level two
|
|
30
|
+
- Level three
|
|
31
|
+
</ListStyle>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Props
|
|
35
|
+
|
|
36
|
+
| Prop | Type | Default | Description |
|
|
37
|
+
| --- | --- | --- | --- |
|
|
38
|
+
| `children` | `ReactNode` | — | List content to style. |
|
|
39
|
+
| `bullets` | `ReactNode \| ReactNode[] \| false \| "none" \| null` | `undefined` | Marker config. Omit it, pass `false`, `"none"`, or `null` for markerless lists. |
|
|
40
|
+
| `className` | `string` | — | Custom class for the wrapper. |
|
|
41
|
+
| `style` | `CSSProperties` | — | Inline style for the wrapper. |
|
|
42
|
+
|
|
43
|
+
## Behavior
|
|
44
|
+
|
|
45
|
+
- Native list markers are removed for all nested lists in the wrapper.
|
|
46
|
+
- A single `bullets` value is reused for every nesting level.
|
|
47
|
+
- An array uses one marker per nesting level.
|
|
48
|
+
- Deeper levels reuse the last configured marker.
|
|
49
|
+
- Custom markers apply to authored list elements passed as children.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Notes
|
|
2
|
+
|
|
3
|
+
Use `<Notes>` to add presenter-only speaker notes to a slide.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { Notes } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
# Launch plan
|
|
9
|
+
|
|
10
|
+
- What changed
|
|
11
|
+
- Why it matters
|
|
12
|
+
|
|
13
|
+
<Notes>
|
|
14
|
+
# Demo cue
|
|
15
|
+
|
|
16
|
+
- Demo the interactive component.
|
|
17
|
+
- Mention PDF export.
|
|
18
|
+
</Notes>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Notes render nothing in audience view, overview thumbnails, and normal PDF output. In presenter mode, Markdown inside `<Notes>` renders as formatted speaker notes, including headings, paragraphs, lists, links, inline code, code blocks, and block quotes.
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `children` | `ReactNode` | — | Speaker-note content collected by presenter mode. |
|
|
28
|
+
|
|
29
|
+
## Behavior
|
|
30
|
+
|
|
31
|
+
- `<Notes>` is a no-op outside presenter mode.
|
|
32
|
+
- Notes are collected from the current slide preview in presenter mode.
|
|
33
|
+
- Updating the slide notes updates the presenter notes panel.
|
|
34
|
+
- You can use Markdown inside Notes.
|
|
35
|
+
|
|
36
|
+
See [Presenter mode](presenter-mode.md) for the full presenter workflow.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# RevealGroup
|
|
2
|
+
|
|
3
|
+
Use `<RevealGroup>` when a short sequence should appear one item at a time.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { RevealGroup } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
<RevealGroup>
|
|
9
|
+
- First point
|
|
10
|
+
- Second point
|
|
11
|
+
- Third point
|
|
12
|
+
</RevealGroup>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Each meaningful direct child becomes one timeline step. Whitespace-only text children are ignored. Markdown, HTML, and JSX lists are special: each top-level list item is revealed one after another while preserving the list container.
|
|
16
|
+
|
|
17
|
+
Use `listRevealMode="nested"` when nested list items should also become individual steps:
|
|
18
|
+
|
|
19
|
+
```mdx
|
|
20
|
+
<RevealGroup listRevealMode="nested">
|
|
21
|
+
- Parent
|
|
22
|
+
- Child A
|
|
23
|
+
- Child B
|
|
24
|
+
- Sibling
|
|
25
|
+
</RevealGroup>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Timeline: parent → child A → child B → sibling. The default mode is `"direct"`, which keeps nested items grouped with their parent.
|
|
29
|
+
|
|
30
|
+
To reveal multiple elements together, wrap them in one direct child:
|
|
31
|
+
|
|
32
|
+
```mdx
|
|
33
|
+
<RevealGroup>
|
|
34
|
+
<div>
|
|
35
|
+
<h3>One idea</h3>
|
|
36
|
+
<p>Supporting context appears with it.</p>
|
|
37
|
+
</div>
|
|
38
|
+
<div>Next idea</div>
|
|
39
|
+
</RevealGroup>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Nested timeline entries inside a group target are flattened after that target and before the following group target:
|
|
43
|
+
|
|
44
|
+
```mdx
|
|
45
|
+
<RevealGroup>
|
|
46
|
+
<div>
|
|
47
|
+
Parent item
|
|
48
|
+
<Reveal>Nested detail</Reveal>
|
|
49
|
+
</div>
|
|
50
|
+
<div>Sibling item</div>
|
|
51
|
+
</RevealGroup>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Timeline:**
|
|
55
|
+
|
|
56
|
+
1. Parent item appears
|
|
57
|
+
2. Nested detail appears
|
|
58
|
+
3. Sibling item appears
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# RevealWith
|
|
2
|
+
|
|
3
|
+
Use `<RevealWith>` when content should appear at the same timeline step as a named `<Reveal>` or any existing numeric slide step. It never adds a new step.
|
|
4
|
+
|
|
5
|
+
````mdx
|
|
6
|
+
import { Reveal, RevealWith } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
<Reveal name="intro">Intro appears first</Reveal>
|
|
9
|
+
<RevealWith target="intro">This appears with the intro reveal</RevealWith>
|
|
10
|
+
|
|
11
|
+
```ts {1|2|3}
|
|
12
|
+
const answer = 42
|
|
13
|
+
console.log(answer)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
<RevealWith at={2}>This appears with step 2</RevealWith>
|
|
17
|
+
````
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| `target` | `string` | — | Same-slide `<Reveal name="...">` target. Must be a literal non-empty string. Use exactly one of `target` or `at`. |
|
|
24
|
+
| `at` | `number` | resolved/injected | Existing 1-based slide-local timeline step. Must be a literal positive integer when authored. Use exactly one of `target` or `at`. |
|
|
25
|
+
| `children` | `ReactNode` | — | Content to reveal with the target step. |
|
|
26
|
+
| `className` | `string` | — | Custom class for styling or transitions. |
|
|
27
|
+
| `as` | `"div" | "span"` | injected | Wrapper element. Honeydeck injects this to keep valid MDX/HTML around block or inline content. |
|
|
28
|
+
|
|
29
|
+
## Behavior
|
|
30
|
+
|
|
31
|
+
- `RevealWith` is cumulative like `Reveal`: once visible, it stays visible.
|
|
32
|
+
- `target` supports forward references to named reveals later on the same slide.
|
|
33
|
+
- `at` can target any existing slide step, including a `RevealGroup` item, code highlight, Magic Code state, or `TimelineSteps` state.
|
|
34
|
+
- Hidden content reserves layout space and uses the same fade/future-preview behavior as `Reveal`.
|
|
35
|
+
- Invalid targets, duplicate reveal names, non-literal values, and out-of-range numeric steps are build errors.
|
|
36
|
+
|
|
37
|
+
See [Steps and reveals](steps-and-reveals.md#revealwith) for timeline ordering examples.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Reveal
|
|
2
|
+
|
|
3
|
+
Use `<Reveal>` when content should appear at the next timeline step.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { Reveal } from '@honeydeck/honeydeck'
|
|
7
|
+
|
|
8
|
+
<Reveal>Start with Markdown</Reveal>
|
|
9
|
+
<Reveal>Enhance with React</Reveal>
|
|
10
|
+
<Reveal>Export to PDF</Reveal>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Reveals are cumulative: once visible, they stay visible while you advance through the slide. Hidden reveal content reserves layout space, so the slide does not jump when the content appears.
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Default | Description |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `children` | `ReactNode` | — | Content to reveal. |
|
|
20
|
+
| `name` | `string` | — | Optional slide-local target for [`RevealWith target="..."`](components-reveal-with.md). Must be a literal non-empty string. |
|
|
21
|
+
| `className` | `string` | — | Custom class for styling or transitions. |
|
|
22
|
+
| `at` | `number` | injected | Timeline step. Honeydeck injects this during compilation; author-authored values are build errors. Use [`RevealWith`](components-reveal-with.md) to sync with an existing step. |
|
|
23
|
+
| `as` | `"div" \| "span"` | injected | Wrapper element. Honeydeck injects this to keep valid MDX/HTML around block or inline content. |
|
|
24
|
+
|
|
25
|
+
## Behavior
|
|
26
|
+
|
|
27
|
+
- Reveals fade in.
|
|
28
|
+
- Hidden content uses `visibility: hidden` plus `opacity: 0`, not `display: none`.
|
|
29
|
+
- Nested reveals are supported.
|
|
30
|
+
- Inline reveals inside paragraphs render inline wrappers.
|
|
31
|
+
- Optional `name="..."` creates a slide-local target for [`RevealWith`](components-reveal-with.md).
|
|
32
|
+
|
|
33
|
+
See [Steps and reveals](steps-and-reveals.md) for timeline ordering examples.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# TimelineSteps
|
|
2
|
+
|
|
3
|
+
Use `<TimelineSteps>` when an imported React component should control part of a slide timeline.
|
|
4
|
+
|
|
5
|
+
```mdx
|
|
6
|
+
import { Reveal, TimelineSteps } from '@honeydeck/honeydeck'
|
|
7
|
+
import { AccordionDemo } from './AccordionDemo'
|
|
8
|
+
|
|
9
|
+
<Reveal>Before the custom component</Reveal>
|
|
10
|
+
|
|
11
|
+
<TimelineSteps steps={3}>
|
|
12
|
+
<AccordionDemo />
|
|
13
|
+
</TimelineSteps>
|
|
14
|
+
|
|
15
|
+
<Reveal>After the custom component</Reveal>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Inside the custom component, read local step state with `useTimelineSteps()`:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { useTimelineSteps } from '@honeydeck/honeydeck'
|
|
22
|
+
|
|
23
|
+
export function AccordionDemo() {
|
|
24
|
+
const { phase, stepIndex, stepCount, isPdfFinalRender } = useTimelineSteps()
|
|
25
|
+
|
|
26
|
+
return <div>Step {stepIndex} of {stepCount}</div>
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Description |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| `steps` | `number` | Required | Literal positive integer number of reserved timeline steps. |
|
|
35
|
+
| `children` | `ReactNode` | — | Custom component content. |
|
|
36
|
+
|
|
37
|
+
`useTimelineSteps()` returns `{ phase, stepIndex, stepCount, startAt, endAt, isPdfFinalRender }`.
|
|
38
|
+
|
|
39
|
+
- `phase` is `"before"`, `"active"`, or `"after"`.
|
|
40
|
+
- `stepIndex` is `0` before start, `1..stepCount` while active, and `stepCount` after end.
|
|
41
|
+
- `isPdfFinalRender` is `true` only for one-page final-state PDF export.
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- `steps` must be a literal positive integer in slide MDX, for example `steps={3}`.
|
|
46
|
+
- `<TimelineSteps>` must appear at the usage site in slide MDX. Imported TSX components cannot register steps by rendering `<TimelineSteps>` internally.
|
|
47
|
+
- Nested Honeydeck timeline producers inside `<TimelineSteps>` are not supported.
|
|
48
|
+
- In `isPdfFinalRender`, allows custom components to controll how they appear in PDFs.
|
package/docs/components.md
CHANGED
|
@@ -3,61 +3,20 @@
|
|
|
3
3
|
Honeydeck core components are explicit imports from `@honeydeck/honeydeck`. They are also exported from `@honeydeck/honeydeck/components`.
|
|
4
4
|
|
|
5
5
|
```mdx
|
|
6
|
-
import { Keyboard } from '@honeydeck/honeydeck'
|
|
6
|
+
import { Reveal, RevealWith, RevealGroup, TimelineSteps, ListStyle, Keyboard, BrowserFrame, Notes } from '@honeydeck/honeydeck'
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Use these pages as the component reference:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
| Component | Use it for |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| [`Reveal`](components-reveal.md) | Show content at a specific slide timeline step. |
|
|
14
|
+
| [`RevealWith`](components-reveal-with.md) | Show content with an existing reveal or numeric slide step without adding another step. |
|
|
15
|
+
| [`RevealGroup`](components-reveal-group.md) | Reveal each direct child or list item one after another, with optional nested-list reveals. |
|
|
16
|
+
| [`TimelineSteps`](components-timeline-steps.md) | Reserve timeline steps for an imported custom React component. |
|
|
17
|
+
| [`ListStyle`](components-list-style.md) | Style Markdown, HTML, or JSX lists with no markers or custom markers. |
|
|
18
|
+
| [`Keyboard`](components-keyboard.md) | Render semantic inline keyboard keys and shortcuts. |
|
|
19
|
+
| [`BrowserFrame`](components-browser-frame.md) | Show a live iframe or fallback screenshot inside browser chrome. |
|
|
20
|
+
| [`Notes`](components-notes.md) | Add formatted speaker notes for presenter mode. |
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
import { BrowserFrame } from '@honeydeck/honeydeck'
|
|
15
|
-
|
|
16
|
-
<BrowserFrame
|
|
17
|
-
src="https://example.com"
|
|
18
|
-
addressBar="example.com"
|
|
19
|
-
fallbackImage="/example-fallback-light.png"
|
|
20
|
-
fallbackDarkImage="/example-fallback-dark.png"
|
|
21
|
-
/>
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
The component renders browser chrome with macOS traffic-light controls and an optional address bar. It can show a light or dark fallback screenshot when the iframe cannot be loaded. It uses Honeydeck theme tokens for the frame, border, typography, and iframe surface.
|
|
25
|
-
|
|
26
|
-
Props:
|
|
27
|
-
|
|
28
|
-
| Prop | Type | Default | Description |
|
|
29
|
-
| --- | --- | --- | --- |
|
|
30
|
-
| `src` | `string` | Required | URL loaded by the iframe. Use an external `https://` URL or a local Vite-served route like `/demo.html`. |
|
|
31
|
-
| `addressBar` | `ReactNode` | — | Optional content shown in the address-bar field. Omit it to hide the input-like address field. |
|
|
32
|
-
| `fallbackImage` | `string` | — | Light/default screenshot shown when iframe loading fails or fallback mode is toggled on. |
|
|
33
|
-
| `fallbackDarkImage` | `string` | — | Dark-mode screenshot shown when fallback mode is active. Falls back to `fallbackImage` when omitted. |
|
|
34
|
-
| `fallbackAlt` | `string` | `Fallback preview` | Accessible alt text for fallback images. |
|
|
35
|
-
| `defaultFallback` | `boolean` | `false` | Starts in fallback mode instead of loading the iframe. Useful for deterministic demos, final-state screenshots, and PDF-friendly decks. |
|
|
36
|
-
| `aspectRatio` | `CSSProperties["aspectRatio"]` | `16 / 9` | Aspect ratio for the full browser window, including chrome. Accepts values such as `16 / 9`, `"4 / 3"`, or `1.6`. |
|
|
37
|
-
| `className` | `string` | — | Additional CSS class for the outer browser frame. |
|
|
38
|
-
| `iframeClassName` | `string` | — | Additional CSS class for the iframe element. Only applies while live iframe content is rendered. |
|
|
39
|
-
|
|
40
|
-
Standard iframe attributes such as `allow`, `sandbox`, `loading`, and `referrerPolicy` are forwarded.
|
|
41
|
-
|
|
42
|
-
When fallback mode is active, the browser frame shows a badge in the top chrome, visually aligned with the address bar. A fourth round control sits beside the macOS traffic-light controls and only becomes visible when the control itself is hovered or keyboard-focused; it toggles fallback mode.
|
|
43
|
-
|
|
44
|
-
## Keyboard
|
|
45
|
-
|
|
46
|
-
Use `<Keyboard>` to show one key or a shortcut in slide prose.
|
|
47
|
-
|
|
48
|
-
```mdx
|
|
49
|
-
Press <Keyboard>Esc</Keyboard> to close overview.
|
|
50
|
-
|
|
51
|
-
Advance with <Keyboard keys="Space" />.
|
|
52
|
-
|
|
53
|
-
Open command palette with <Keyboard keys={["Ctrl", "Shift", "P"]} />.
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
`keys` accepts a single value or an ordered array. When `keys` is omitted, `children` is rendered as one key. Array values render one `<kbd>` per key, separated by `+` by default:
|
|
57
|
-
|
|
58
|
-
```mdx
|
|
59
|
-
<Keyboard keys={["⌘", "K"]} />
|
|
60
|
-
<Keyboard keys={["Ctrl", "Alt", "Delete"]} separator=" " />
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
The component is inline by default, uses Honeydeck theme styling, supports `className`, and does not add timeline steps.
|
|
22
|
+
For broader timing concepts, see [Steps and reveals](steps-and-reveals.md). For custom components and layouts, see [Customization](customization.md).
|
package/docs/configuration.md
CHANGED
|
@@ -15,6 +15,7 @@ Defined in the first frontmatter block of the deck entry file (before any slide
|
|
|
15
15
|
| `pdfColorMode` | `"light" \| "dark"` | unset | Optional PDF color mode; when unset, falls back to pinned `colorMode`, then `light` |
|
|
16
16
|
| `pdfSteps` | `"final" \| "all"` | `"final"` | PDF includes all steps or final state |
|
|
17
17
|
| `transition` | `boolean` | `true` | Enable crossfade between slides |
|
|
18
|
+
| `magicCodeDuration` | `number` | `800` | Default Magic Code animation duration in milliseconds |
|
|
18
19
|
| `layouts` | `string` | `""` (built-in) | Layout map module path |
|
|
19
20
|
| `defaultLayout` | `string` | `"Default"` | Layout used when slide has no `layout:` |
|
|
20
21
|
| `showSlideNumbers` | `boolean` | `false` | Show the current slide number in the bottom-right corner of slides |
|
|
@@ -50,6 +51,16 @@ transition: true # default
|
|
|
50
51
|
transition: false # disable
|
|
51
52
|
```
|
|
52
53
|
|
|
54
|
+
### Magic Code Duration
|
|
55
|
+
|
|
56
|
+
Magic Code animations default to 800ms. Set a deck-wide default with `magicCodeDuration`:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
magicCodeDuration: 500
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A Magic Code block can override this locally with `{duration:500}`. Slide-level frontmatter does not configure Magic Code; the same key in slide frontmatter is treated as a layout prop.
|
|
63
|
+
|
|
53
64
|
## Slide-Level Settings
|
|
54
65
|
|
|
55
66
|
Per-slide frontmatter (after `---`):
|
package/docs/deeper-dive.md
CHANGED
|
@@ -128,7 +128,7 @@ For the full reference, see [Configuration](configuration.md).
|
|
|
128
128
|
Core runtime components are explicit imports from `honeydeck`.
|
|
129
129
|
|
|
130
130
|
```mdx
|
|
131
|
-
import { Reveal, RevealGroup, TimelineSteps, BrowserFrame, Notes } from '@honeydeck/honeydeck'
|
|
131
|
+
import { Reveal, RevealWith, RevealGroup, TimelineSteps, BrowserFrame, Notes } from '@honeydeck/honeydeck'
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
### Reveal steps
|
|
@@ -136,11 +136,18 @@ import { Reveal, RevealGroup, TimelineSteps, BrowserFrame, Notes } from '@honeyd
|
|
|
136
136
|
`<Reveal>` reveals content at the next timeline step. Hidden content keeps its layout space so the slide does not jump.
|
|
137
137
|
|
|
138
138
|
```mdx
|
|
139
|
-
<Reveal>Appears at step 1</Reveal>
|
|
139
|
+
<Reveal name="first">Appears at step 1</Reveal>
|
|
140
140
|
<Reveal>Appears at step 2</Reveal>
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
`<
|
|
143
|
+
`<RevealWith>` syncs extra content to an existing step without adding a step:
|
|
144
|
+
|
|
145
|
+
```mdx
|
|
146
|
+
<RevealWith target="first">Appears with step 1</RevealWith>
|
|
147
|
+
<RevealWith at={2}>Appears with step 2</RevealWith>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`<RevealGroup>` wraps each direct child in a reveal step. Markdown and HTML lists are special: each top-level list item reveals one after another. Use `listRevealMode="nested"` to reveal nested list items depth-first.
|
|
144
151
|
|
|
145
152
|
```mdx
|
|
146
153
|
<RevealGroup>
|
|
@@ -202,6 +209,22 @@ console.log(b)
|
|
|
202
209
|
- `{2-3|5|all}` starts with lines 2-3 active, then steps to line 5, then all lines.
|
|
203
210
|
- Highlight groups after the first interleave with `<Reveal>` in document order.
|
|
204
211
|
|
|
212
|
+
Magic Code animates between multiple code states. It builds on Shiki Magic Code / Shiki Magic Move while keeping Honeydeck's build-time highlighting and timeline behavior:
|
|
213
|
+
|
|
214
|
+
`````mdx
|
|
215
|
+
````md magic-code {duration:500}
|
|
216
|
+
```ts
|
|
217
|
+
const count = 1
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const count = 2
|
|
222
|
+
```
|
|
223
|
+
````
|
|
224
|
+
`````
|
|
225
|
+
|
|
226
|
+
Each inner code fence is a state. Inner code fence line metadata still works, so Honeydeck advances through a state's highlight groups before morphing to the next code state. Content inside a Magic Code block that is not a fenced code block is ignored. `md magic-move` is accepted as a Slidev compatibility alias, but Honeydeck docs and examples use `md magic-code`.
|
|
227
|
+
|
|
205
228
|
## Navigation, presenting, and PDF
|
|
206
229
|
|
|
207
230
|
Keyboard shortcuts:
|
|
@@ -213,7 +236,7 @@ Keyboard shortcuts:
|
|
|
213
236
|
| `down` / `s` | Next slide |
|
|
214
237
|
| `up` / `w` | Previous slide |
|
|
215
238
|
| `o` | Toggle overview mode |
|
|
216
|
-
| `p` | Open presenter mode |
|
|
239
|
+
| `p` | Open presenter mode in the current tab |
|
|
217
240
|
| `f` | Toggle fullscreen |
|
|
218
241
|
| `Escape` | Exit overview or fullscreen |
|
|
219
242
|
|
|
@@ -236,7 +259,7 @@ Presenter mode opens with `p` and includes:
|
|
|
236
259
|
- speaker notes from `<Notes>`
|
|
237
260
|
- slide/step counter and wall clock
|
|
238
261
|
- an "Open audience view" button
|
|
239
|
-
- audience sync through `BroadcastChannel
|
|
262
|
+
- audience sync through `BroadcastChannel`, with Presentation API casting when supported
|
|
240
263
|
|
|
241
264
|
See [Navigation](navigation.md), [Mobile and touch](mobile.md), [Presenter mode](presenter-mode.md), and [PDF export](pdf-export.md).
|
|
242
265
|
|
|
@@ -328,7 +351,7 @@ Package Markdown guides live on the Honeydeck docs site and in the package `docs
|
|
|
328
351
|
| Layouts | Runtime dynamic import of the layout map |
|
|
329
352
|
| Tailwind | v4 CSS-first; Honeydeck adds the Vite plugin internally |
|
|
330
353
|
| Shiki | Dual-theme CSS variables with build-time highlighting |
|
|
331
|
-
| Presenter sync | `BroadcastChannel
|
|
354
|
+
| Presenter sync | `BroadcastChannel` fallback + Presentation API casting, no server needed |
|
|
332
355
|
| Scaling | `transform: scale()` from the configured aspect ratio |
|
|
333
356
|
| Build | `tsc` only, ESM, no bundler |
|
|
334
357
|
| Testing | `node:test` plus fixture files |
|
|
@@ -336,7 +359,7 @@ Package Markdown guides live on the Honeydeck docs site and in the package `docs
|
|
|
336
359
|
## Types and exports
|
|
337
360
|
|
|
338
361
|
```ts
|
|
339
|
-
import { Reveal, RevealGroup, Notes } from '@honeydeck/honeydeck'
|
|
362
|
+
import { Reveal, RevealWith, RevealGroup, Notes } from '@honeydeck/honeydeck'
|
|
340
363
|
import type { LayoutProps, LayoutMap, LayoutDemo } from '@honeydeck/honeydeck/types'
|
|
341
364
|
|
|
342
365
|
import defaultLayouts from '@honeydeck/honeydeck/layouts'
|
package/docs/getting-started.md
CHANGED
|
@@ -70,7 +70,7 @@ Use `layout:` to choose a slide layout. Built-in layouts include `Blank`, `Defau
|
|
|
70
70
|
- Vite dev/build with hot reload
|
|
71
71
|
- Built-in layouts and theme tokens
|
|
72
72
|
- Tailwind v4-friendly styling
|
|
73
|
-
- Reveal steps
|
|
73
|
+
- Reveal steps, code step-through, and Magic Code transitions
|
|
74
74
|
- Presenter mode with speaker notes
|
|
75
75
|
- Overview mode, keyboard/touch navigation, and URL state
|
|
76
76
|
- PDF export through headless Chromium
|
|
@@ -102,7 +102,7 @@ honeydeck skill # install optional Honeydeck agent skills
|
|
|
102
102
|
- [Slides](slides.md) - separators, multiple files, layouts, and assets
|
|
103
103
|
- [Configuration](configuration.md) - deck-level and slide-level frontmatter
|
|
104
104
|
- [Steps and reveals](steps-and-reveals.md) - timeline, reveal groups, code steps, and PDF behavior
|
|
105
|
-
- [Components](components.md) - core Honeydeck components such as `
|
|
105
|
+
- [Components](components.md) - core Honeydeck components such as `Reveal`, `RevealWith`, `TimelineSteps`, `BrowserFrame`, and `Keyboard`
|
|
106
106
|
- [Customization](customization.md) - themes, layout sets, Tailwind tokens, custom layouts, and layout demos
|
|
107
107
|
- [Navigation](navigation.md) - keyboard, touch, overview mode, and URL state
|
|
108
108
|
- [Mobile and touch](mobile.md) - mobile controls, touch gestures, and pinch zoom
|
package/docs/navigation.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| `↓` / `s` | Next slide |
|
|
10
10
|
| `↑` / `w` | Previous slide |
|
|
11
11
|
| `o` | Toggle overview mode |
|
|
12
|
-
| `p` | Open presenter mode (
|
|
12
|
+
| `p` | Open presenter mode (same tab) |
|
|
13
13
|
| `f` | Toggle fullscreen |
|
|
14
14
|
| `Escape` | Exit overview / exit fullscreen |
|
|
15
15
|
|
package/docs/pdf-export.md
CHANGED
|
@@ -51,14 +51,16 @@ Honeydeck builds an ordered capture plan before taking screenshots. The final PD
|
|
|
51
51
|
|
|
52
52
|
When `pdfSteps: all`:
|
|
53
53
|
|
|
54
|
-
- Each `Reveal`/`RevealGroup` step becomes a separate page.
|
|
54
|
+
- Each `Reveal`/`RevealGroup` step becomes a separate page. `RevealWith` appears on the page for its target step and adds no extra page.
|
|
55
55
|
- Stepped code blocks show their first highlight group on the baseline page; each later code highlight group becomes a separate page.
|
|
56
|
-
-
|
|
56
|
+
- Magic Code blocks show their first inner code fence on the baseline page; later line-highlight states and code morph states become separate pages according to the same timeline.
|
|
57
|
+
- Reveals, stepped code blocks, Magic Code, and custom `TimelineSteps` blocks use the same underlying timeline.
|
|
57
58
|
|
|
58
59
|
When `pdfSteps: final` (default):
|
|
59
60
|
|
|
60
61
|
- All reveals shown in final (visible) state.
|
|
61
62
|
- Code blocks shown with final highlight applied.
|
|
63
|
+
- Magic Code blocks shown at their final code state with final highlight applied.
|
|
62
64
|
- `useTimeline()` and `useTimelineSteps()` expose `isPdfFinalRender: true`, so
|
|
63
65
|
custom step-driven components can render an all-open/all-visible PDF state.
|
|
64
66
|
|
package/docs/presenter-mode.md
CHANGED
|
@@ -29,6 +29,7 @@ Includes:
|
|
|
29
29
|
- Slide number / step counter
|
|
30
30
|
- Wall clock
|
|
31
31
|
- Button to open audience view in a new tab/window
|
|
32
|
+
- Button to cast the audience view to a secondary display when supported; the same control becomes a stop button while casting and is disabled with a hint when unsupported
|
|
32
33
|
- Navigation buttons that move through the timeline while staying in presenter mode
|
|
33
34
|
|
|
34
35
|
## Speaker Notes
|
|
@@ -58,11 +59,11 @@ Content here.
|
|
|
58
59
|
|
|
59
60
|
## Opening Presenter Mode
|
|
60
61
|
|
|
61
|
-
- Keyboard shortcut `p` from normal presentation → opens in
|
|
62
|
+
- Keyboard shortcut `p` from normal presentation → opens presenter mode in the current tab.
|
|
62
63
|
- Navigation controls button in normal presentation.
|
|
63
64
|
- Direct URL: `/#/presenter/1/0`
|
|
64
65
|
|
|
65
|
-
Pressing `p` opens presenter mode in
|
|
66
|
+
Pressing `p` opens presenter mode in the **current tab**.
|
|
66
67
|
|
|
67
68
|
## Navigation
|
|
68
69
|
|
|
@@ -85,10 +86,12 @@ When no next timeline state exists, the `Next` preview shows an end-of-deck plac
|
|
|
85
86
|
|
|
86
87
|
## Presenter/Audience Sync
|
|
87
88
|
|
|
88
|
-
Presenter mode and audience view synchronize navigation via `BroadcastChannel`
|
|
89
|
+
Presenter mode and audience view synchronize navigation via `BroadcastChannel` and, when supported, the Presentation API.
|
|
89
90
|
|
|
90
91
|
- Presenter mode acts as the controller.
|
|
91
92
|
- Audience view (opened from presenter mode) listens for navigation updates.
|
|
93
|
+
- The cast audience sends a `sync-request` as soon as the receiver connection appears; the presenter replies with the current slide/step in a `sync-response` so late connections resync immediately.
|
|
94
|
+
- The cast audience follows presenter navigation through Presentation API receiver messages.
|
|
92
95
|
- No server, internet, WebSocket, or device pairing required.
|
|
93
96
|
- If `BroadcastChannel` is unavailable, both views function independently.
|
|
94
97
|
|