@protoyard/decks-ui 0.0.0-stage → 1.1.1

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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -2
  3. package/dist/app/CustomSlide.svelte +42 -0
  4. package/dist/app/CustomSlide.svelte.d.ts +60 -0
  5. package/dist/app/DeckApp.svelte +527 -0
  6. package/dist/app/DeckApp.svelte.d.ts +41 -0
  7. package/dist/app/FreeBlock.svelte +66 -0
  8. package/dist/app/FreeBlock.svelte.d.ts +10 -0
  9. package/dist/app/FreeSlide.svelte +135 -0
  10. package/dist/app/FreeSlide.svelte.d.ts +5 -0
  11. package/dist/app/context.d.ts +14 -0
  12. package/dist/app/context.js +11 -0
  13. package/dist/app/fit.d.ts +39 -0
  14. package/dist/app/fit.js +97 -0
  15. package/dist/app/free-prefetch.d.ts +3 -0
  16. package/dist/app/free-prefetch.js +13 -0
  17. package/dist/app/index.d.ts +6 -0
  18. package/dist/app/index.js +5 -0
  19. package/dist/app/interactive.d.ts +3 -0
  20. package/dist/app/interactive.js +25 -0
  21. package/dist/app/renderers.d.ts +14 -0
  22. package/dist/app/renderers.js +49 -0
  23. package/dist/app/stage-deck.d.ts +40 -0
  24. package/dist/app/stage-deck.js +65 -0
  25. package/dist/debug/BlockOutline.svelte +117 -0
  26. package/dist/debug/BlockOutline.svelte.d.ts +12 -0
  27. package/dist/debug/DeckDebugPanel.svelte +205 -0
  28. package/dist/debug/DeckDebugPanel.svelte.d.ts +18 -0
  29. package/dist/debug/availability.d.ts +19 -0
  30. package/dist/debug/availability.js +27 -0
  31. package/dist/debug/format.d.ts +12 -0
  32. package/dist/debug/format.js +39 -0
  33. package/dist/debug/index.d.ts +10 -0
  34. package/dist/debug/index.js +10 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +7 -0
  37. package/dist/layouts/CustomSlideHost.svelte +97 -0
  38. package/dist/layouts/CustomSlideHost.svelte.d.ts +14 -0
  39. package/dist/layouts/custom-slides.d.ts +28 -0
  40. package/dist/layouts/custom-slides.js +20 -0
  41. package/dist/layouts/index.d.ts +7 -0
  42. package/dist/layouts/index.js +7 -0
  43. package/dist/player.css +6 -0
  44. package/dist/player.css.d.ts +1 -0
  45. package/dist/studio/DeckStudioPanel.svelte +261 -0
  46. package/dist/studio/DeckStudioPanel.svelte.d.ts +8 -0
  47. package/dist/studio/StudioIcon.svelte +9 -0
  48. package/dist/studio/StudioIcon.svelte.d.ts +6 -0
  49. package/dist/studio/index.d.ts +26 -0
  50. package/dist/studio/index.js +21 -0
  51. package/dist/studio/studio.svelte.d.ts +565 -0
  52. package/dist/studio/studio.svelte.js +85 -0
  53. package/dist/styles.css +5 -0
  54. package/dist/styles.css.d.ts +1 -0
  55. package/package.json +106 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dailybot, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,171 @@
1
- # Temporary Holding Version
1
+ # @protoyard/decks-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The deck host for Protoyard ([ADR-038](../../docs/DECISIONS.md)): `DeckApp`,
4
+ the one deck component the player's deck entry and generated deck prototypes
5
+ mount. Decks are presented by the [`@texuli/slides`](../../../texuli/packages/slides/README.md)
6
+ stage core (`DeckStageCore` from `@texuli/slides/stage-core`, Texuli ADR-009),
7
+ the stage without its default renderer tables, which a Protoyard deck never
8
+ uses: the fixed canvas, `#/<slide-id>/<step>`
9
+ routes, keys, touch, the toolbar (below the slide at the inline end), slide
10
+ list, overview, presenter view and window, blackout, laser, help, copy link,
11
+ first-visit hint, full screen, transitions and print are the stage's.
12
+ DeckApp adds what is Protoyard's own:
13
+
14
+ - the Protoyard deck shape, migrated to the deck model and prepared once with
15
+ Protoyard's block check (`prepareDeckV2`), so the stage paints it on its
16
+ first render without validating it again;
17
+ - Protoyard themes, palette seeds and font pairings (`loadTheme` through
18
+ `@protoyard/decks/style`), and `mode` (`light`/`dark`) as the host
19
+ `data-tx-mode`;
20
+ - free slides painted by `@protoyard/blocks-ui`, so interactive blocks keep
21
+ their state in the host's `stateHost`, `embed-app` mounts through the host's
22
+ `mountEmbed`, votes use the host's `viewerId`, and Protoyard-only blocks
23
+ (poll, quiz, calculator, form, flow, terminal, embed-app…) render;
24
+ - the generated deck's custom slides (`customSlides`);
25
+ - the "Debug for agents" panel (`D` and a toolbar button), Deck Studio, and the
26
+ print page for `?print=1` and the browser's own print (Ctrl/Cmd+P).
27
+
28
+ ## Subpaths
29
+
30
+ | Import | What it provides |
31
+ | --- | --- |
32
+ | `@protoyard/decks-ui` | `fitDeckCanvas` (the engine's canvas fit) and its `DeckCanvasFit` type. |
33
+ | `@protoyard/decks-ui/app` | `DeckApp` and the custom slide types (`CustomSlideProps`, `CustomSlideComponent`, `CustomSlideLoader`, `CustomSlideRegistry`). |
34
+ | `@protoyard/decks-ui/layouts` | `CustomSlideHost`, `customSlideLoader`, `frozenCopy` and the custom slide types. The 20 catalog layouts render on the stage (`@texuli/slides/layouts`). |
35
+ | `@protoyard/decks-ui/studio` | Deck Studio: `createDeckStudio`, `deckStudioTab`, `prefixDeckPatch`. |
36
+ | `@protoyard/decks-ui/debug` | `DeckDebugPanel`, `BlockOutline`, `deckDebugAvailable`, `debugQueryOn`, `isDevBuild`, `deckFileName`, `downloadText`, `inertJson`. |
37
+ | `@protoyard/decks-ui/styles.css`, `./player.css` | The blocks styles (Tailwind sources) for free slides, custom slides and the debug panel. DeckApp imports `@texuli/slides/styles.css` itself. |
38
+
39
+ ## DeckApp
40
+
41
+ | Prop | Default | What it does |
42
+ | --- | --- | --- |
43
+ | `deck` | required | A deck that passed `DeckSchema` |
44
+ | `syncHash` | `true` | Keep `#/<slide-id>/<step>` in the URL and follow it |
45
+ | `autofocus` | `true` | Focus the active slide on mount |
46
+ | `namespace` | `deck` | Presenter channel, view transitions and the first-visit hint |
47
+ | `mode` | `light` | The deck theme's mode (decks default to light for stable exports) |
48
+ | `fit` | `viewport` | `viewport` takes the app height (`h-app`); `container` fills a sized parent |
49
+ | `debug` | `auto` | The debug panel: `auto` in dev builds or with `?debug=1`, `on`, `off` |
50
+ | `debugSource` | `null` | Where the deck came from, for the agent reference |
51
+ | `customSlides` | — | A generated deck's `src/custom-slides.ts` registry |
52
+ | `resolveAvatar` | — | Library avatar id → URL (`avatarUrl` from `@protoyard/renderer-svelte`) |
53
+
54
+ The wrapper keeps `data-py-deck-app`, `data-slide-id` and `data-slide-step`
55
+ (they follow the stage's controller, including a deep link's first position),
56
+ `data-presenter`, `data-py-deck-theme`, `data-py-deck-mode` and
57
+ `data-py-deck-motion` for devtools and e2e. The stage uses the `developer` URL
58
+ tier: every deck reaching DeckApp passed Protoyard's stricter offline media
59
+ policy, which allows bounded `data:` images the untrusted tier refuses. A
60
+ followed hash past a slide's last step is rewritten to that step.
61
+
62
+ ### Free slides
63
+
64
+ The stage validates free-slide blocks with `@texuli/blocks`, which does not
65
+ know Protoyard's block vocabulary, so DeckApp hands each free slide to the
66
+ stage as a host (`custom`) slide with the same step count (its own step
67
+ labels, then "Step n") and paints the original blocks with
68
+ `@protoyard/blocks-ui` (`app/FreeSlide.svelte`). Reveals use the stage's step
69
+ machinery (`createDeckStepContext` over the model's step table of the
70
+ original slide), so fragments carry `data-tx-deck-fragment` and animate with
71
+ the stage's styles; code highlights move per step. A leading unstepped
72
+ heading is the slide title; the blocks zoom down to fit the canvas.
73
+ The stage's `--tx-*` theme tokens feed Protoyard's blocks inside the slide, so
74
+ blocks follow the deck theme and the background's text color.
75
+
76
+ ### Custom slides
77
+
78
+ `layout: "custom"` slides run the generated deck's own component
79
+ (`CustomSlideHost`): `{ slide, step, stepCount, data }` as frozen copies,
80
+ errors kept on the slide, clicks never advance the deck. The player (no
81
+ registry) and the stage's thumbnails, presenter previews and print show a
82
+ labelled card and never run owned code.
83
+
84
+ ### Keyboard shortcuts
85
+
86
+ Generated from `DECK_KEY_BINDINGS` (the engine's registry, which the stage's
87
+ help dialog also reads). With the debug panel available, `D` toggles it and
88
+ `Esc` closes it first.
89
+
90
+ <!-- key-bindings:start (written from the code; no test checks it since PLAN_009) -->
91
+ | Keys | Action | What it does |
92
+ | --- | --- | --- |
93
+ | `←`, `↑`, `Page Up`, `Shift + Space`, `Backspace` | `prev` | Previous step or slide |
94
+ | `→`, `↓`, `Page Down`, `Space`, `Enter` | `next` | Next step or slide |
95
+ | `Home` | `first` | First slide |
96
+ | `End` | `last` | Last slide |
97
+ | `O`, `Esc` | `overview` | Overview of all slides |
98
+ | `F` | `fullscreen` | Full screen |
99
+ | `Esc` | `fullscreen-exit` | Leave full screen |
100
+ | `S`, `P` | `presenter` | Presenter view |
101
+ | `B`, `.`, `Esc` | `blackout` | Black out the screen |
102
+ | `L`, `Esc` | `laser` | Laser pointer |
103
+ | `Ctrl + P`, `⌘ + P` | `print` | Print, one slide per page |
104
+ | `?`, `Esc` | `help` | Controls and shortcuts |
105
+ <!-- key-bindings:end -->
106
+
107
+ ### Debug for agents (`D`)
108
+
109
+ In dev builds, with `?debug=1` or `debug="on"`, the stage's toolbar gains a
110
+ "Debug for agents" button (`data-py-deck-tool="debug"`) as its last group,
111
+ "More tools" (the stage's `tools` snippet), and `D` toggles the
112
+ panel (`data-py-deck-debug`): the slide record, its checks, outlines, and
113
+ copy/download actions. It loads on its own chunk (under 12.4 KB gzip,
114
+ `tests/debug-panel.test.ts`).
115
+
116
+ ## Print
117
+
118
+ The stage's Print button opens `?print=1`, where DeckApp renders the stage's
119
+ print view (`DeckPrintViewCore` from `@texuli/slides/export-core`, painted
120
+ with DeckApp's own renderer tables): one page per
121
+ visible slide, notes with `&notes=1`. The browser's own print (Ctrl/Cmd+P)
122
+ mounts the same view for paper: its code is preloaded while the browser is
123
+ idle after the deck opens (`preloadDeckPrintViewCore`), and the view is mounted on
124
+ the first print. That first print captures the page before the view has
125
+ settled its themes, images and fonts, so it prints the current slide and says
126
+ so; every later print has all the slides. PPTX export is available from `@texuli/slides/export`; no
127
+ Protoyard UI calls it yet.
128
+
129
+ ## Deck Studio
130
+
131
+ A lazy DevTools tab that edits the deck style and the current slide with a
132
+ live preview and exports RFC 6902 patches (`@protoyard/decks/patch`).
133
+
134
+ ## Measured numbers
135
+
136
+ `tests/bundle-size.test.ts` builds the `./app` entry in production
137
+ (2026-10-31, npm Texuli 0.10.0): 105,816 B gzip initial JavaScript (cap
138
+ 111,657 B), 9,404 B first-screen CSS (cap 10,481 B) and 57,011 B of all
139
+ emitted CSS (cap 88,094 B). The print view comes from
140
+ `@texuli/slides/export-core` (G105), so the build no longer emits the stage's
141
+ default component registry that decks never load (86,765 B of all emitted CSS
142
+ before, on the same Texuli).
143
+ The debug panel chunk is 11,179 B gzip.
144
+
145
+ ## Known limits
146
+
147
+ - Free slides cannot use the deck model's masters, `math` blocks or component
148
+ items; component slides are not part of Protoyard's deck shape yet.
149
+ - The stage always installs its default component registry, so its lazy
150
+ painter chunks and CSS are emitted into every deck build.
151
+ - Layout strings for assistive tech (comparison "Yes"/"No", chart table
152
+ caption) are English only (the stage's limit).
153
+
154
+ ## Install in a workspace prototype
155
+
156
+ Add `"@protoyard/decks-ui": "workspace:*"` to the prototype dependencies. Then
157
+ import the styles after Tailwind and the theme/UI styles:
158
+
159
+ ```css
160
+ @import '@protoyard/decks-ui/styles.css';
161
+ ```
162
+
163
+ ## Tests
164
+
165
+ ```bash
166
+ pnpm vitest run packages/decks-ui
167
+ pnpm vitest run packages/decks-ui/tests/deck-app.test.ts # stage, keyboard, hash, hostile strings inert, print status
168
+ pnpm vitest run packages/decks-ui/tests/debug-panel.test.ts # Debug for agents
169
+ pnpm vitest run packages/decks-ui/tests/bundle-size.test.ts # deck JS/CSS caps and the Debug chunk cap
170
+ pnpm --filter @protoyard/decks-ui typecheck
171
+ ```
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import type { CustomSlide as Slide } from '@protoyard/decks/schema';
3
+ import type { CustomSlide as CustomSlideModel } from '@texuli/deck/model';
4
+ import { type DeckSlideRendererProps, getDeckStepContext } from '@texuli/slides/stage-core';
5
+ import CustomSlideHost from '../layouts/CustomSlideHost.svelte';
6
+ import { getDeckHostContext } from './context.js';
7
+
8
+ /**
9
+ * A `layout: "custom"` slide on the `@texuli/slides` stage: the generated
10
+ * deck's own component (DeckApp's `customSlides`), or a labelled card in the
11
+ * player. Overview thumbnails, presenter previews and print show the card and
12
+ * never run owned code (the stage paints them with the same renderers, so
13
+ * the slide checks where it was mounted).
14
+ */
15
+ let { slide, context }: DeckSlideRendererProps<CustomSlideModel> = $props();
16
+ const host = getDeckHostContext();
17
+ const fromContext = getDeckStepContext();
18
+ const step = $derived(fromContext ?? context.step);
19
+ /** Unknown until mounted; a preview or print page never turns live. */
20
+ let live = $state(false);
21
+
22
+ function placement(node: HTMLElement): void {
23
+ live = node.closest('[data-tx-deck-preview], .tx-deck-print-view') === null;
24
+ }
25
+ </script>
26
+
27
+ <div class="py-deck-custom absolute inset-0 overflow-hidden" {@attach placement}>
28
+ <CustomSlideHost
29
+ slide={slide as unknown as Slide}
30
+ step={step.step}
31
+ stepCount={step.count}
32
+ registry={host.customSlides}
33
+ {live}
34
+ />
35
+ </div>
36
+
37
+ <style>
38
+ /* The deck's radius choice reaches the custom slide's Texuli components (see FreeSlide). */
39
+ .py-deck-custom {
40
+ --tx-radius: var(--tx-deck-radius);
41
+ }
42
+ </style>
@@ -0,0 +1,60 @@
1
+ import { type DeckSlideRendererProps } from '@texuli/slides/stage-core';
2
+ declare const CustomSlide: import("svelte").Component<DeckSlideRendererProps<{
3
+ layout: "custom";
4
+ props: {
5
+ component: string;
6
+ data?: string | number | boolean | import("@texuli/deck").JsonValue[] | {
7
+ [key: string]: import("@texuli/deck").JsonValue;
8
+ } | null | undefined;
9
+ };
10
+ id: string;
11
+ title?: string | undefined;
12
+ notes?: string | undefined;
13
+ hidden?: boolean | undefined;
14
+ background?: "solid-color" | "solid-surface" | "solid-primary" | "solid-accent" | "solid-muted" | "solid-inverse" | "gradient-brand" | "gradient-dusk" | "gradient-dawn" | "gradient-forest" | "gradient-mist" | "pattern-dots" | "pattern-grid" | {
15
+ kind: "solid";
16
+ token?: "background" | "accent" | "surface" | "primary" | "muted" | "inverse" | undefined;
17
+ color?: string | undefined;
18
+ } | {
19
+ kind: "gradient";
20
+ gradient: "brand" | "dusk" | "dawn" | "forest" | "mist";
21
+ angle?: number | undefined;
22
+ } | {
23
+ kind: "pattern";
24
+ pattern: "dots" | "grid";
25
+ } | {
26
+ kind: "image" | "image-full";
27
+ src: string;
28
+ tone?: "dark" | "light" | undefined;
29
+ overlay?: number | undefined;
30
+ position?: "center" | "top" | "bottom" | "left" | "right" | undefined;
31
+ alt?: string | undefined;
32
+ fit?: "cover" | "contain" | undefined;
33
+ } | {
34
+ kind: "video" | "video-full";
35
+ src: string;
36
+ tone?: "dark" | "light" | undefined;
37
+ overlay?: number | undefined;
38
+ position?: "center" | "top" | "bottom" | "left" | "right" | undefined;
39
+ poster?: string | undefined;
40
+ } | undefined;
41
+ transition?: "fade-up" | "fade" | "none" | "slide" | "zoom" | "morph" | undefined;
42
+ theme?: string | {
43
+ id: string;
44
+ kind: "catalog";
45
+ mode?: "dark" | "light" | "high-contrast" | "system" | undefined;
46
+ } | {
47
+ theme: string | number | boolean | import("@texuli/deck").JsonValue[] | {
48
+ [key: string]: import("@texuli/deck").JsonValue;
49
+ } | null;
50
+ kind: "embedded";
51
+ mode?: "dark" | "light" | "high-contrast" | "system" | undefined;
52
+ } | {
53
+ kind: "seed";
54
+ seed: number;
55
+ mode?: "dark" | "light" | "high-contrast" | "system" | undefined;
56
+ } | undefined;
57
+ steps?: string[] | undefined;
58
+ }>, {}, "">;
59
+ type CustomSlide = ReturnType<typeof CustomSlide>;
60
+ export default CustomSlide;