Skip to content

v0.4.6

Choose a tag to compare

@moiku moiku released this 23 Oct 17:56
· 445 commits to main since this release

Egatari 1.0 WD 2026-10-24 に従う(§5.6 と、live §3 で足された決まりを除く) / Follows Egatari 1.0 Working Draft 2026-10-24, except §5.6 and the additions to live §3

Added

  • Quality check (command and editor): text inside an element with data-annot (a deliberately small annotation, such as the reading-order numbers of m-kanbun) is not reported as too small (smallText, smallSvgText). The attribute name lives in ANNOT_ATTR (kit/core/layout-scan.mjs); contract §10 is its rule. The mark only counts when a component sets it at mount; the player removes every data-annot written in the deck's own markup first. No other check and no rendering changes.
  • Colab notebook: an optional cell to use your own reference voice for Irodori (upload a WAV/MP3 of 3-30 s or give a Drive path). It is checked, converted to 48 kHz mono and used for every speaker; without it the built-in voices are used as before. Use only a voice you have the right to use; the file stays in your Colab runtime. Output format and inputHash are unchanged
  • Built decks now carry a tab icon: the Egatari mark (the optical 32 px version of assets/logo) as an inline data: SVG <link rel="icon"> at the top of <head>. The same single SVG switches to the dark colours under prefers-color-scheme: dark, so it reads on light and dark tabs and is used by browsers that ignore media on <link>. It is added to the normal build, --portable and --play-only files (and the dev preview), needs no network and no change to the Live CSP (img-src data: blob: already allows it)
  • Turn it off with <html data-favicon="off">; a deck that has its own <link rel="icon"> (or shortcut icon) keeps it and nothing is added. The icon is added only to the built document, so the source files, sourceSha256 and the theme hashes do not change
  • Live room results (draft, format extension live §3): Import live room results… in the Attachments panel and in Distribute with Live (questions and polls)… adds the results JSON downloaded from the Live server as a data attachment (attachments/live-<room>-<date>.json, title with only the room and date). It is checked against the vendored egatari/live schema (a630478; activities may carry a display label), the 10 MiB limit and the §3.8 content checks; a room that differs from the lesson's live settings is warned about but allowed; the first import shows a one-time privacy notice. The list marks it "Learners' responses" with the number of questions and the tally per poll, shown as text only
  • Results are never written into play-only copies. Exporting a single file that contains them warns and offers Export without the results; npm run portable -- attach <dir> <results.json> adds them from the command line, pack and build --portable print a warning, and --without-live-results leaves them out
  • Components get ctx.reset === true in render on the first step entry after the viewer touched them (docs/contract.md §3)
  • Editor app: a slide list (the Slides tab at the left edge of the preview) with thumbnails of each slide's final step. Add a slide after the selected one from a template (title and body, two columns, figure, section divider, quote), duplicate it (narration copied, audio not), delete it (a dialog names the slide and warns that its narration and audio go too) and reorder by dragging, the ↑ ↓ buttons or Alt+↑ / Alt+↓. The editor assigns new ids, keeps the narration sections in slide order and the audio bound by key, rebuilds and runs the quality check; Undo takes back the last action until the next one or the next save of a single file. The list is a keyboard listbox with live-region announcements
  • Template leftovers: the slide list's placeholder narration lines start with a fixed mark ✎ and the template text is listed once (kit/core/template-text.mjs). The quality check (npm run check and the editor) warns about a placeholder line (narrPlaceholder) and template text left on a slide (templateText); TTS (the editor's Audio panel and Colab job, tools/narration-json.mjs for tts/synth.py) and exports (--portable, --play-only, the editor's Egatari-file exports) list them first and still let you go on
  • Themes can declare their frame geometry with metrics in theme.json ("v": 1; body, conclusion line and vertical columns in stage pixels of 1280×720), inherited field by field through extends. The quality checker (command and editor) derives the body limits (bodyBottom, bodyRight, tateBodyHead, bodyConclCol, bodyLeft) from it, so a theme that moves these boxes is checked against the right limits; margins, minimum text sizes and title and conclusion lengths do not change. Without metrics the defaults reproduce the previous limits exactly. Players never use it to draw, and theme.json counts toward the theme's sha256 (contract §12, reference)
  • themeMetricsDrift (warning, once per deck): the rendered boxes differ from the theme's declaration by more than 1px, or a theme without metrics (or the deck's own <style>) moves them. Unusable metrics values are a build warning (theme-metrics: …, reported as themeMetricsInvalid) and fall back to the parent's value
  • The boxes are measured only with the bundled fonts loaded, never with a fallback font; otherwise the comparison is skipped with one metricsFontLoad warning
  • node tools/theme-metrics.mjs measure <theme> prints the metrics block measured from the rendered theme, in whole pixels rounded outward to the more lenient limit (check compares it with the declaration). tools/test-theme-metrics.mjs fails when a bundled theme renders differently from its declared (or default) geometry

Changed

  • Test and inspection screenshots no longer depend on the time between page load and capture: kit/browser.mjs gains stableCapture(page, opts) (hides the talk-mode control bar and toast, waits two animation frames, captures with animations: 'disabled', caret: 'hide'), prepareForCapture(page), releaseCapture(page) and captureUrl(url, mode = 'video'). tools/shot.mjs, smoke.mjs, sheet.mjs, comment-shots.mjs, flake.mjs and the seek-determinism checks use it; new tools/test-capture.mjs checks that a talk-mode capture at 0.5 s and 3 s after load is identical
  • Colab notebook: the packages are pinned in two sets chosen from the CUDA libraries cell 1 finds (one place, COLAB_PINS in tools/make-colab-notebook.mjs): cuda13 when libcublasLt.so.13 is there or there is no GPU (onnxruntime-node 1.30.0, @huggingface/transformers 4.3.1, onnxruntime-gpu and onnxruntime 1.31.0, kokoro-onnx 0.6.1: the set that ran on 2026-10-21), cuda12 when only libcublasLt.so.12 is (onnxruntime-node 1.24.3, @huggingface/transformers 4.2.0, onnxruntime-gpu and onnxruntime 1.26.0, kokoro-onnx 0.6.1), so a CUDA 12 image uses its GPU again. Cell 1 prints the environment (Python, Node, GPU and driver CUDA, the CUDA libraries found, the chosen set and its versions) and cell 3 the installed onnxruntime-node and the providers in use, to include in a problem report. The verified note now says: CPU verified on 2026-10-23; the GPU path failed that day and falls back to the CPU; GPU with the pinned versions not yet verified; Kokoro not yet
  • Colab: every record's recipe carries runtime, the pin set and the execution provider that actually ran (cuda12/cuda, cuda13/cpu, …; after the CPU fallback, not the requested list), for Irodori and Kokoro. The editor's zip import keeps it in the audio manifest's recipe; it is not part of inputHash, so existing audio does not go stale
  • Live player: the pane starts collapsed as a small Live marker at the top right (a button with aria-expanded), and the slides use the whole window. Tapping the marker opens the notice and Join before joining, and the question box once connected, on any slide. On a step with a poll the pane opens by itself (announced in a live region, without taking the focus) and collapses again when that step is left, unless the learner pinned it (Keep open) or opened it from the marker. While connected the collapsed marker shows the state (a green dot, and text for screen readers); Disconnect is in the opened pane. When open, the pane is docked beside or below the slides as before, so it never covers them. The fold strip is gone; × or Esc collapses the pane. Reduced motion is respected, and the zero-network rules are unchanged
  • Live player: where it cannot connect (inside a frame such as an LMS, a viewer in a chat or file app, another kind of address), the pane offers Join in the browser, a link that opens the room's join page (https://<endpoint host>/join#room=…&key=…, the key only in the fragment) in a new tab, with a short explanation and the room. Nothing is loaded before it is tapped. The editor's preview still shows only the room's address as text
  • Live room: polls are shown by a label such as “Slide 4 'XML の壁', step 1” (「4 枚目『XML の壁』・段階 1」) instead of the raw step key (s-cjatth:0) in the editor's step suggestions and poll list, with the key in the tooltip. The editor writes it as the optional label of each poll (display text, at most 80 characters; keys unchanged), and Copy activities JSON includes it, so egatari/live's teacher and join pages show it too. Older polls get a label when the panel opens if their slide is still in the deck; otherwise, and in rooms without labels, the key is shown as s-cjatth · step 1 (* as “whole lesson”). The vendored protocol is updated to egatari/live a630478
  • Live screens use plain words: ライブ, 参加者, 待機室, ステップ, 参加コード, ホスト用リンク, ホストのページ, 全員に表示 (in English: Live, participants, waiting room, step, join code, host link, host page, show to everyone). Export ▾ → Distribute with Live (questions and polls)… (before: Distribute with a live room…), Export Egatari file (editable) and Export Egatari file (play only). Strings only; no behaviour changes
  • Distribute with Live: To fix before exporting shows plain messages in the editor's language (for example 「投票 p1:設問を入れてください(300 字まで)」) instead of the validator's developer text. LivePolicy().validate returns problems as {code, path, …params, text} (text and toString() are the former English message, so the command line prints the same), and app/i18n.js (liveProblem) maps each code to ja/en
  • Editor app: one way to record. The Audio panel's ● Record (r) opens recording mode at the panel's step (Space, speak, Space, Esc);
    the panel stays open behind it and then shows the new take. Parity of the old panel recording: docs/recording-parity.md
  • Editor app: recording mode reports a stop at the 600 s limit, stops there also in a background tab, and reports a recorder that cannot start
  • Build: the theme's CSS (tokens.css, parent themes first) now comes after kit/layouts.css in <style id="deck-base"> (before: after the fonts and KaTeX, before base.css), in every build path (CLI, --dev and the dev server, portable and play-only exports, the editor app). A theme rule now overrides a layout rule of the same specificity (the cover title's position, the .body inset, the heading rule, .concl, .pn) without an html prefix. latin.css stays last. The bundled themes (default, business, kokugo) render the same as before (backlog #75)
  • New optional tokens with the old values as fallbacks: --marker (list markers; default --term), --bar and --bar-h (the progress bar; default --emph, 3px). Proposed with K1 by @totasuko-lab

Removed

  • Editor app: the Audio panel's deck-wide list “TTS steps made with an older text pre-processing”. The pre-processing and
    recipe.textNormalize stay; old audio keeps playing and is regenerated only with “Regenerate with TTS”
  • Editor app: recording inside the Audio panel (its button, meter and “Record again”); use recording mode

Fixed

  • Colab notebook: when the GPU's CUDA libraries do not match onnxruntime (on 2026-10-23 Colab had no libcublasLt.so.13, which onnxruntime-node 1.30 needs, and Irodori stopped while loading text_encoder), the Node worker now rebuilds the session on the CPU, uses the CPU for the rest of the job and prints one line in Japanese and English with the reason; Kokoro does the same. Only a failure on the CPU as well still stops the job
  • b now blacks out the screen: a full-window black cover goes over the slide, ink, captions, the control bar and the key help (before, the current slide stayed visible). b again, Esc, a click, the arrows, Space, PageUp/PageDown or Home/End bring the screen back without moving the slide, a faint “Blacked out — press b or Esc to return” hint shows in the corner for 2 s (and again on mouse move), Esc leaves blackout before it closes the key help, and entering blackout cancels the outside-link box. Print and video mode are unchanged
  • Keys reach the slides after using the pen or clicking the editor: a click on the slide with the pen or laser on (or on the control bar) moves focus into the deck; in the editor app, after a click on the toolbar, header or status bar, the arrows, PageUp/PageDown, Home/End, b, a and f go to the preview (never Space or Enter, and never from a text field, the narration drawer or an open dialog), and toolbar buttons clicked with the mouse no longer keep focus
  • Editor app: while unsaved narration holds the preview, a click or key on it shows why, with Save and Revert next to it; the first click on the preview after leaving a narration field now focuses the preview
  • The outside-link box keeps focus when you click its text, and Esc and Tab work even if focus has left it
  • Presenter view: a click on the "next" pane no longer swallows the keys
  • Presenter view: Esc (when no drawing tool is on), p or the new Close presenter view (p) button close it and return focus to the main window (in fullscreen they only leave fullscreen), also from the current-slide pane and in the editor app; p in the main window closes an open presenter view; while the window has no focus, a “Click here to control” overlay takes the first click; the key help (?) and the docs give the cmd+Tab / Alt+Tab workaround and the recommended order (presenter view first, then the audience window fullscreen)
  • Live player: a play-only copy opened on Android from the Files app in Chrome (a content:// address) can now join the live room. It counts as a local file the learner opened, like file:; the other conditions are unchanged (not framed, inside the window, exactly one CSP <meta> whose connect-src is the endpoint, one tap on Join, not in video, print or the presenter view)
  • The runtime calls component mount only after the stage is laid out: a deck started inside a display:none iframe or a hidden tab waits for the stage to get a size and its fonts to load, then mounts and draws, so figures (e.g. m-kanbun's 書き下し文) look the same as after a normal start; DECK.ready comes after the mount in both cases
  • Editor app: recording mode shows a failed take's error (an empty or undecodable take, a recorder that cannot start) instead of failing silently
  • Printing and PDF: the section divider (sec) kept its text but lost its background (white text on white paper) unless the print dialog's “Background graphics” was on. Slides and dividers now carry print-color-adjust: exact (and -webkit-print-color-adjust) in kit/base.css's print rules, so every slide background prints in its screen color; the page itself (html, body) stays white, and the screen rendering is unchanged. tools/test-print-backgrounds.mjs checks the computed styles in print media and the pixels of a PDF made without background graphics (editor)
    A section divider printed to PDF, background kept
  • Editor app: a Space pressed right after opening recording mode (r or ● Record (r)) is no longer lost. The screen appears and takes the focus at once; until the step is loaded its Space button reads “準備中… / Getting ready…” (aria-busy, announced in the live region). A press in that time (key or button) is kept and starts one take as soon as the screen is ready (once, even if pressed twice), and is dropped if opening fails (a declined rights check, no narration, Esc). A refused microphone or a recorder that cannot start is reported as before
  • Editor app: opening recording mode again while it is opening or open (r held down, Space on the still-focused panel button) no longer adds a second recording screen that Esc leaves behind
  • Editor app: a Space pressed in the preview at the same moment as r no longer reaches the deck (it advanced the slide)
  • State the viewer changes by hand in a component (an open m-kobun conjugation card) no longer carries across steps: entering any step,
    also the same one again (DECK.go, DECK.seek, a slide change and back), redraws a touched component as render(k, t) (format spec §8.4)

Security

  • Dependency updates for two Dependabot alerts. KaTeX 0.16.47 to 0.18.2 (exact pin), which fixes a prototype pollution in KaTeX's settings that could bypass the trust restrictions. Egatari builds decks from the author's own source and only trusts \htmlClass, \htmlId and \htmlData, so exposure was low; formulas in the examples render pixel-for-pixel the same (the generated class names gain a katex- prefix, e.g. base is now katex-base; nothing in Egatari depends on them). certifi 2026.7.22 to 2026.10.19 in tts/uv.lock, which drops root CAs whose intermediate keys were compromised
  • Links that leave the lesson (format spec §11): a click on a link to another site or file no longer navigates. The player shows the destination's host and full address and opens it only on Open, in a new tab with noopener,noreferrer; it asks every time when the link text names another host, the destination is not https:, the link downloads, the address has a user name or password, or the host is punycode, and once per host otherwise. javascript:, data:, file: and every scheme but http(s), mailto and tel are refused. The box is keyboard-operable (focus on Cancel, Tab cycles, Esc cancels) and makes the lesson behind it inert. Middle and modified clicks are covered; video mode does nothing; the editor preview and dev server frame tell where the link goes but open nothing. The rule is a pure module, kit/core/link-policy.mjs (DECK.LinkPolicy), for the future play page
  • Links the player makes itself (the live room's join link, attachment saves) skip the box, as spec §11 (h) allows: they carry a JavaScript-only mark that lesson markup cannot set, and the live pane now shows the join page's host ("Goes to: …") next to the link
  • SECURITY.md and the README: use a Node.js release with the latest security fixes (the 2026-10-21 releases fixed Brotli and TLS certificate checking issues)

editor-site-v0.4.6.tar.gz sha256: 86318a04df97f5bd24604c3cd6ee41b10c0e096878defcbb1de73bc925697140