footprint-storydeck 0.8.0 → 0.8.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1 — footprint-narration is now footprint-story
4
+
5
+ - **footprint-narration is now footprint-story.** StoryDeck's shared narration code (shown-vs-spoken text, captions,
6
+ chapters, the voice cache) comes from `footprint-story` 0.3.0, the same package under its new name: what StoryDeck
7
+ and footprint-storyreel share, now with a `/motion` door too. Nothing a deck says, shows or voices changes. A project
8
+ that imports `footprint-narration` itself beside StoryDeck should move to `footprint-story` (the two doors export
9
+ exactly what they did).
10
+
3
11
  ## 0.8.0 — a part can have its own look (data-theme)
4
12
 
5
13
  - **A part can have its own look: `data-theme`.** `<section … data-theme="ledger">` in clicks.html puts
package/README.md CHANGED
@@ -240,7 +240,7 @@ through it the same way.
240
240
  A talk lives on after the room: someone opens the deck alone, or it becomes a video. The words were in the
241
241
  speaker notes all along, so narration is data on each click, and the library turns it into speech, captions and
242
242
  timing — the same words, three uses. The rules underneath — what a voice says for a number or a symbol, the clip
243
- cache, the captions and YouTube's chapter rule — are [footprint-narration](https://github.com/footprintjs/footprint-narration)'s,
243
+ cache, the captions and YouTube's chapter rule — are [footprint-story](https://github.com/footprintjs/footprint-story)'s,
244
244
  shared with footprint-storyreel; StoryDeck adds what is the deck's own:
245
245
 
246
246
  - **what the voice says** — `narrationText(notes)`: the notes as a voice reads them. An acronym stays as written
@@ -249,7 +249,7 @@ shared with footprint-storyreel; StoryDeck adds what is the deck's own:
249
249
  another script its reading), numbers become words, a lone symbol that means something is said (`SAY`: & + = < > ×
250
250
  % and Greek letters), any other word without a letter is a pause that keeps its sentence end (a forced aligner
251
251
  needs letters in every word), and a line after `[draft]` (a bridge you wrote for the deck) is said, and shown apart;
252
- - **a clip per click, cached by what it says** — `footprint-narration/voice` (Node). A clip's key is a hash of the
252
+ - **a clip per click, cached by what it says** — `footprint-story/voice` (Node). A clip's key is a hash of the
253
253
  voice settings and the exact words, so editing one note re-voices that click and nothing else, and an interrupted
254
254
  run keeps every batch it finished. The voice is a port (`{ name, synthesize(scenes, { work }) }`); `chatterboxKit`
255
255
  is one adapter — a local voice kit (Chatterbox, words aligned with MMS_FA; nothing is uploaded). `footprint-storydeck/voice`
@@ -259,7 +259,7 @@ shared with footprint-storyreel; StoryDeck adds what is the deck's own:
259
259
  the browser's where a click has none — captions lighting sentence by sentence (styles: `footprint-storydeck/narration.css`).
260
260
 
261
261
  ```js
262
- import { voiceClips, chatterboxKit } from 'footprint-narration/voice';
262
+ import { voiceClips, chatterboxKit } from 'footprint-story/voice';
263
263
  import { narrationText } from 'footprint-storydeck/narration';
264
264
  import { embedClips } from 'footprint-storydeck/voice';
265
265
  import { notesData, notesRuntime, listenRuntime } from 'footprint-storydeck/narration-html';
@@ -290,7 +290,7 @@ would drop one entrance frame in six at 30 fps, and a test with the real ffmpeg
290
290
  ```js
291
291
  import { chromium } from 'playwright-core';
292
292
  import { renderVideo, deckStageDriver } from 'footprint-storydeck/video';
293
- import { clipFor } from 'footprint-narration/voice';
293
+ import { clipFor } from 'footprint-story/voice';
294
294
  import { narrationText, writtenText } from 'footprint-storydeck/narration';
295
295
 
296
296
  const steps = clicks.map(c => {
@@ -547,9 +547,9 @@ const { theme, toggle, setTheme } = useTheme(); // flips an html class + persi
547
547
  | `planFocus` · `focusStep` · `readFocus` · `FOCUS` | focus: per click, what it is about and what happens to the rest (also `footprint-storydeck/focus`) |
548
548
  | `readPieces` · `readStep` · `drawFocus` · `wrapStage` · `slideClass` · `focusOverlay` · `focusRuntime` | focus, written into slide HTML (also `footprint-storydeck/focus-html`; styles in `footprint-storydeck/focus.css`) |
549
549
  | `idleTour` · `tourPlan` · `tourText` | tour: a deck (or a figure) that shows its point while nobody is using it, and its plan in data and in one line (also `footprint-storydeck/tour`) |
550
- | `narrationText` · `writtenText` | narration: what a click says (also `footprint-storydeck/narration`); the rules beneath, sentences, captions and chapters are `footprint-narration`'s |
550
+ | `narrationText` · `writtenText` | narration: what a click says (also `footprint-storydeck/narration`); the rules beneath, sentences, captions and chapters are `footprint-story`'s |
551
551
  | `notesData` · `notesRuntime` · `listenRuntime` | narration in the page: notes, presenter window, listen mode (also `footprint-storydeck/narration-html`; styles in `footprint-storydeck/narration.css`) |
552
- | `embedClips` | `footprint-storydeck/voice` (Node only): a deck's clips inside one page; the clip cache, the voice port and its local-kit adapter are `footprint-narration/voice`'s |
552
+ | `embedClips` | `footprint-storydeck/voice` (Node only): a deck's clips inside one page; the clip cache, the voice port and its local-kit adapter are `footprint-story/voice`'s |
553
553
  | `renderVideo` · `planVideo` · `deckStageDriver` · `ffmpegMajor` | `footprint-storydeck/video` (Node only, ffmpeg 5.0+): the deck as a narrated video, with captions and chapters |
554
554
  | `exportPptx` · `pptxParts` · `zip` · `unzip` · `checkPptx` | `footprint-storydeck/pptx` (Node only): the deck as a PowerPoint file — a slide per click, its picture, notes and clip — through a footprintjs flowchart that records why |
555
555
  | `loadDeck` · `buildDeck` · `serveDeck` · `outline` · `reviewClick` · `checkDeck` · `layoutCheck` · `initDeck` · `serveMcp` · `runCli` | `footprint-storydeck/deck` (Node only): a deck folder made into one page, served for editing, read back as text — the `storydeck` command (also `npx footprint-storydeck`); its page's base styles are `footprint-storydeck/deck.css` |
package/dist/narration.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spokenMap, autoRules } from "footprint-narration";
1
+ import { spokenMap, autoRules } from "footprint-story";
2
2
  const AUTO = autoRules();
3
3
  const writtenText = (notes) => (Array.isArray(notes) ? notes : [notes ?? ""]).join(" ").replace(/\[draft\]\s*/g, "").replace(/\s+/g, " ").trim();
4
4
  function narrationText(notes, { spell, say } = {}) {
package/dist/pptx.js CHANGED
@@ -2,7 +2,7 @@ import { Buffer } from "node:buffer";
2
2
  import { closeSync, copyFileSync, existsSync, fstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import path from "node:path";
5
- import { clipFor } from "footprint-narration/voice";
5
+ import { clipFor } from "footprint-story/voice";
6
6
  import { narrationText, writtenText } from "./narration.js";
7
7
  import { planVideo } from "./video.js";
8
8
  import { pptxParts, pngSize, plainText, isSound, AUDIO, LANG_TAG, SOUND } from "./pptxParts.js";
@@ -49,13 +49,13 @@ function headOf(file) {
49
49
  }
50
50
  function soundProblem(clip, ext) {
51
51
  const { size, head } = headOf(clip.audio);
52
- if (!size) return `its clip ${clip.audio} is empty (0 bytes) \u2014 voice it again (footprint-narration/voice \xB7 voiceClips)`;
52
+ if (!size) return `its clip ${clip.audio} is empty (0 bytes) \u2014 voice it again (footprint-story/voice \xB7 voiceClips)`;
53
53
  return isSound(head, ext) ? null : `its clip ${clip.audio} is not a .${ext} file (no ${SOUND[ext].sign}) \u2014 voice it again, or convert it with ffmpeg`;
54
54
  }
55
55
  function clipProblem(c, { voiced, maxClip }) {
56
56
  const clip = c.clip, d = clip?.duration, ext = extOf(clip?.audio);
57
- if (!clip) return voiced && c.spoken.trim() ? ["unvoiced", "it says words but has no clip \u2014 voice it (footprint-narration/voice \xB7 voiceClips), or give its step its clip"] : null;
58
- if (!clip.audio || !existsSync(clip.audio)) return ["missing", `its clip ${clip.audio} is not there \u2014 voice it again (footprint-narration/voice \xB7 voiceClips)`];
57
+ if (!clip) return voiced && c.spoken.trim() ? ["unvoiced", "it says words but has no clip \u2014 voice it (footprint-story/voice \xB7 voiceClips), or give its step its clip"] : null;
58
+ if (!clip.audio || !existsSync(clip.audio)) return ["missing", `its clip ${clip.audio} is not there \u2014 voice it again (footprint-story/voice \xB7 voiceClips)`];
59
59
  if (!AUDIO[ext]) return ["unplayable", `its clip is .${ext} \u2014 PowerPoint plays .wav, .m4a and .mp3 (convert it with ffmpeg)`];
60
60
  const bytes = soundProblem(clip, ext);
61
61
  if (bytes) return ["unplayable", bytes];
package/dist/video.js CHANGED
@@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import path from "node:path";
5
- import { captionCues, captionFile, readCaptions, youtubeChapters } from "footprint-narration";
5
+ import { captionCues, captionFile, readCaptions, youtubeChapters } from "footprint-story";
6
6
  const NUM = (n) => String(n).padStart(3, "0");
7
7
  const entry = (file) => `file '${path.resolve(file).replace(/'/g, "'\\''")}'`;
8
8
  const failure = (done) => done?.error ? done.error.message : done?.signal ? `signal ${done.signal}` : done?.status !== 0 ? `exit ${done?.status}` : null;
package/dist/voice.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import path from "node:path";
4
- import { clipFor } from "footprint-narration/voice";
4
+ import { clipFor } from "footprint-story/voice";
5
5
  const failure = (done) => done?.error ? done.error.message : done?.signal ? `signal ${done.signal}` : done?.status !== 0 ? `exit ${done?.status}` : null;
6
6
  function embedClips(texts, { cache, profile, run = spawnSync }) {
7
7
  cache = path.resolve(cache);
package/index.d.ts CHANGED
@@ -192,7 +192,7 @@ export * from './focusHtml';
192
192
  // Tour — a deck or a figure that shows its point by itself while nobody is using it (tour.d.ts).
193
193
  export * from './tour';
194
194
  // Narration — what a deck says on each click (narration.d.ts), in the page: notes, presenter, listen mode (narrationHtml.d.ts).
195
- // The rules a voice reads by, sentences, captions and chapters are footprint-narration's (its own types).
195
+ // The rules a voice reads by, sentences, captions and chapters are footprint-story's (its own types).
196
196
  // footprint-storydeck/voice, /video and /pptx (Node-only) carry their own types.
197
197
  export * from './narration';
198
198
  export * from './narrationHtml';
package/narration.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * storydeck · narration (narration.js) — what a deck says on each click. The rules a voice reads by, the sentences,
3
- * the captions and the chapters are footprint-narration's: import them from 'footprint-narration'.
3
+ * the captions and the chapters are footprint-story's: import them from 'footprint-story'.
4
4
  */
5
5
 
6
6
  /** The notes as written, for captions: [draft] marks dropped, white space collapsed. */
7
7
  export function writtenText(notes: string | readonly string[] | null | undefined): string;
8
- /** The notes as the voice should read them: footprint-narration's automatic rules over the written text (`spell`, `say` replace its default lists). */
8
+ /** The notes as the voice should read them: footprint-story's automatic rules over the written text (`spell`, `say` replace its default lists). */
9
9
  export function narrationText(notes: string | readonly string[] | null | undefined, options?: { spell?: Readonly<Record<string, string>>; say?: Readonly<Record<string, string>> }): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprint-storydeck",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "One source, many lenses — render structured content as a Read article, a Scroll (scrollytelling) story, or a Watch deck; and make a deck from a folder with a live dev loop.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -118,7 +118,7 @@
118
118
  }
119
119
  },
120
120
  "dependencies": {
121
- "footprint-narration": "^0.1.0",
121
+ "footprint-story": "^0.3.0",
122
122
  "markdown-it": "^14.1.0"
123
123
  },
124
124
  "devDependencies": {
@@ -8,9 +8,9 @@ description: Build, focus, narrate and film talks and posts with StoryDeck — o
8
8
  A talk or a post is written once: **sections** of slide steps plus prose. StoryDeck shows it as an article
9
9
  (Read), a scrollytelling story (Scroll) or a click-through deck (Watch), decides per click what the audience
10
10
  looks at (focus), and turns the speaker notes into a voice, captions and a video (narration). Most of it is
11
- plain data and string builders; only `footprint-storydeck/voice`, `footprint-storydeck/video`, `footprint-storydeck/pptx` and `footprint-narration/voice` touch the
11
+ plain data and string builders; only `footprint-storydeck/voice`, `footprint-storydeck/video`, `footprint-storydeck/pptx` and `footprint-story/voice` touch the
12
12
  file system. The narration rules underneath (what a voice says for a number, captions, chapters, the clip cache)
13
- are footprint-narration's, a package StoryDeck depends on and shares with footprint-storyreel.
13
+ are footprint-story's, a package StoryDeck depends on and shares with footprint-storyreel.
14
14
 
15
15
  ## Pick the door
16
16
 
@@ -22,15 +22,15 @@ are footprint-narration's, a package StoryDeck depends on and shares with footpr
22
22
  | that focus written into slide HTML | `footprint-storydeck/focus-html` + `footprint-storydeck/focus.css`, `focusRuntime` once in the page | build + page |
23
23
  | a deck (or figure) that shows its point while nobody is using it (`idleTour`, its plan `tourPlan`, a notes line `tourText`) | `footprint-storydeck/tour`; declared as `<deck-stage data-tour="3 4 5 9:6s">` + `(${idleTour})()`, or `SlideDeck`'s `tour` / post.json's `tour` | build + page |
24
24
  | what each click says (`narrationText`, `writtenText`: `[draft]`-aware) | `footprint-storydeck/narration` | anywhere |
25
- | the rules beneath (`spokenMap`, `autoRules`, `SPELL`, `SAY`), sentences, captions, YouTube chapters | `footprint-narration` | anywhere |
25
+ | the rules beneath (`spokenMap`, `autoRules`, `SPELL`, `SAY`), sentences, captions, YouTube chapters | `footprint-story` | anywhere |
26
26
  | notes (N), presenter (P), listen mode (L) in the page | `footprint-storydeck/narration-html` + `footprint-storydeck/narration.css` | build + page |
27
- | a clip per click in a voice, cached (`voiceClips`, `clipFor`, `chatterboxKit`, `pickSteps`) | `footprint-narration/voice` | **Node only** |
27
+ | a clip per click in a voice, cached (`voiceClips`, `clipFor`, `chatterboxKit`, `pickSteps`) | `footprint-story/voice` | **Node only** |
28
28
  | those clips carried inside one page (`embedClips`) | `footprint-storydeck/voice` | **Node only** |
29
29
  | the deck as a narrated video | `footprint-storydeck/video` (+ ffmpeg 5.0 or newer on the path, Playwright's `chromium` passed in) | **Node only** |
30
30
  | the deck as PowerPoint (.pptx): a slide per click, its picture, notes and clip | `footprint-storydeck/pptx` — `exportPptx` (the same driver as the video; no ffmpeg; `npm i footprintjs` beside it, an optional peer) | **Node only** |
31
31
  | a deck from a folder: one page, a dev server that reloads on the same click, the deck read back as text | the `storydeck` command (`npx footprint-storydeck init · dev · outline · review · check · build · mcp`); `footprint-storydeck/deck` for scripts | **Node only** |
32
32
 
33
- Never import `footprint-storydeck/voice`, `footprint-storydeck/video`, `footprint-storydeck/pptx` or `footprint-narration/voice` into browser code: they use `node:fs`
33
+ Never import `footprint-storydeck/voice`, `footprint-storydeck/video`, `footprint-storydeck/pptx` or `footprint-story/voice` into browser code: they use `node:fs`
34
34
  and child processes.
35
35
  The main entry (`footprint-storydeck`) re-exports only what runs anywhere.
36
36
 
@@ -99,7 +99,7 @@ whole path .4 s).
99
99
  **Narrate a deck in a voice.**
100
100
 
101
101
  ```js
102
- import { voiceClips, chatterboxKit } from 'footprint-narration/voice';
102
+ import { voiceClips, chatterboxKit } from 'footprint-story/voice';
103
103
  import { narrationText } from 'footprint-storydeck/narration';
104
104
  import { embedClips } from 'footprint-storydeck/voice';
105
105
  import { notesData, notesRuntime, listenRuntime } from 'footprint-storydeck/narration-html';
@@ -113,14 +113,14 @@ page += notesData(clicks.map(c => c.notes)) + voice.tags + `<script>(${notesRunt
113
113
 
114
114
  The engine is a port — `{ name, synthesize(scenes, { work }) }`, scenes `{ id, text }`, results `{ id, audio,
115
115
  duration, words: [{ text, start, end }] }` — so a cloud voice or a test fake plugs in the same way as the local kit.
116
- A clip keeps its timed words (`clipFor(…).words`; `null` for a clip voiced before footprint-narration).
116
+ A clip keeps its timed words (`clipFor(…).words`; `null` for a clip voiced before footprint-story).
117
117
 
118
118
  **Render the narrated video.** A strip first, then the whole.
119
119
 
120
120
  ```js
121
121
  import { chromium } from 'playwright-core';
122
122
  import { renderVideo, deckStageDriver } from 'footprint-storydeck/video';
123
- import { clipFor } from 'footprint-narration/voice';
123
+ import { clipFor } from 'footprint-story/voice';
124
124
  import { narrationText, writtenText } from 'footprint-storydeck/narration';
125
125
 
126
126
  const steps = clicks.map(c => {
@@ -149,7 +149,7 @@ It is pictures + notes + sound, not editable PowerPoint shapes.
149
149
 
150
150
  - **A clip's key is its recipe**: the voice profile's settings and the exact spoken text. Change the words a
151
151
  voice reads — a note, or the `spell` list — and those clicks are voiced again; everything else stays cached.
152
- `spell` replaces the default list: extend it with `{ ...SPELL, K8s: 'K eight s' }` (`SPELL` from `footprint-narration`;
152
+ `spell` replaces the default list: extend it with `{ ...SPELL, K8s: 'K eight s' }` (`SPELL` from `footprint-story`;
153
153
  a value is words — a letter in every word, no digits, no comma, semicolon or colon at its end — or it is refused).
154
154
  - **Every word needs letters.** A lone symbol that means something is said (`SAY`: & + = < > × % and Greek
155
155
  letters; pass `say` to change it); any other (— → ... an emoji) is a pause that keeps its sentence end. The local
package/pptx.d.ts CHANGED
@@ -3,14 +3,14 @@
3
3
  * its clip. Node. The export is a footprintjs flowchart: every run leaves a record that explains it. footprintjs is an
4
4
  * optional peer dependency: `npm i footprintjs` beside StoryDeck to export.
5
5
  */
6
- import type { VoiceProfile } from 'footprint-narration/voice';
6
+ import type { VoiceProfile } from 'footprint-story/voice';
7
7
  import type { VideoClip, VideoDriver, VideoStep } from './video';
8
8
 
9
9
  /** A click to export: as renderVideo takes it, plus the slide's headline (the title, before the label). */
10
10
  export interface PptxStep extends VideoStep { headline?: string }
11
11
  /** A click of a deck: its label (and headline), and its notes — narration-html's notesData entry ({ now }), a string, or a list of strings. */
12
12
  export interface DeckClick { label?: string; headline?: string; notes?: string | readonly string[] | { now: readonly string[] } | null }
13
- /** Where a deck's clips are: footprint-narration/voice's cache and profile, and the reading rules the clips were voiced with. */
13
+ /** Where a deck's clips are: footprint-story/voice's cache and profile, and the reading rules the clips were voiced with. */
14
14
  export interface PptxVoice { cache: string; profile: VoiceProfile; spell?: Readonly<Record<string, string>>; say?: Readonly<Record<string, string>> }
15
15
  /** A click as exported: its number, title, notes as written, words as spoken, clip, and the seconds it stays (null: the speaker clicks). */
16
16
  export interface PlannedSlide {
package/video.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * storydeck · video (video.js) — a narrated deck as a video: every click on screen while its clip plays. Node + ffmpeg 5.0+.
3
3
  */
4
- import type { Clip } from 'footprint-narration/voice';
5
- import type { Chapters } from 'footprint-narration';
4
+ import type { Clip } from 'footprint-story/voice';
5
+ import type { Chapters } from 'footprint-story';
6
6
 
7
- /** A cached clip as the video uses it (`words` is not read: a clip from before footprint-narration has none). */
7
+ /** A cached clip as the video uses it (`words` is not read: a clip from before footprint-story has none). */
8
8
  export type VideoClip = Omit<Clip, 'words'> & { words?: Clip['words'] };
9
9
  /** A click to render: its label, its words as written and as spoken, and its clip (or null). */
10
10
  export interface VideoStep { label?: string; written?: string; spoken?: string; clip?: VideoClip | null }
package/voice.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * storydeck · voice (voice.js) — a deck's clips inside one page. Node. The clip cache, the voice port and the kit
3
- * adapter are footprint-narration's: import them from 'footprint-narration/voice'.
3
+ * adapter are footprint-story's: import them from 'footprint-story/voice'.
4
4
  */
5
- import type { VoiceProfile } from 'footprint-narration/voice';
5
+ import type { VoiceProfile } from 'footprint-story/voice';
6
6
 
7
7
  type Run = (cmd: string, args: string[], options?: object) => { status: number | null };
8
8