uhuu-components 0.3.7 → 0.3.8

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 DELETED
@@ -1,347 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to the `uhuu-components` Storybook workspace are documented here.
4
-
5
- ### 2026-10-08 — Toolchain declarations (ECO-04)
6
-
7
- - `package.json` declares the Node floor (>=24) and the package-manager pin (pnpm@10.30.3) the ecosystem
8
- toolchain policy measures. Metadata only; the dependency resolution is unchanged.
9
-
10
- ## 0.3.3 (unreleased)
11
-
12
- ### Added
13
-
14
- - The editor shell's chrome follows the host's light or dark appearance: the stage behind the
15
- pages, page labels and their menus, the floating page toolbar and zoom menu, the add and reorder
16
- sheets, notices and the stage's scrollbars. Pages, and the page thumbnails in the sheets, stay
17
- paper. Inside the uhuu document editor the shell takes `?appearance=light|dark` from the template
18
- URL before the first render, then `appearance` in the handshake answer (`shaked`, re-emitted by
19
- the SDK as `loaded`), then `{ action: 'appearance', data: { appearance } }` when the user
20
- switches; it reads the editor's messages itself, so templates that pin an older SDK follow too.
21
- Light by default; never a cookie or the OS preference; the renderer is always light. No new
22
- exports: `InteractiveModeProvider` takes `appearance` to force one, and `useInteractive()`
23
- reports the resolved `appearance` for template-owned chrome. Dark values mirror `@uhuuio/theme`
24
- (not a dependency). See `docs/editor-shell-appearance.md`.
25
-
26
- ### Changed
27
-
28
- - Chrome surfaces draw with a shell token instead of `bg-white` (`bg-(--uhuu-shell-surface)`, and
29
- `text-(--uhuu-shell-on-inverse)` on dark buttons); the light look is pixel-identical.
30
- - Chrome roots without `data-uhuu-editor` (the "added" notice, `Section`'s default header, the
31
- legacy `PageResizer` controls, the drag grid's debug box) now carry it.
32
-
33
- ## 0.3.2 (unreleased)
34
-
35
- ### Fixed
36
-
37
- - The shell now follows the user's interface language inside the uhuu document editor. The editor
38
- sends it once, in the handshake that the SDK re-emits as `loaded`, before the payload. Templates
39
- that render nothing until the payload arrives (go-expose) mounted `InteractiveModeProvider` after
40
- that event and fell back to `<html lang>`, the document's language. The package now subscribes
41
- when it is imported, so the language is kept for any provider that mounts later.
42
- - Per-language page and group labels now reach documents saved earlier. The page-editor state a
43
- document stores keeps each item's label as saved, so restored documents showed the old English
44
- string. On restore, a saved label that is one of the template label's values, or none, is replaced
45
- by the current template label; a user's rename is kept. Found migrating go-expose.
46
-
47
- ## 0.3.1 (unreleased)
48
-
49
- ### Added
50
-
51
- - The editor shell is translated: toolbar, page menus, add and reorder dialogs, zoom, image options
52
- and notices ship in German, French, Italian and English, and follow the user's interface language
53
- from the host handshake (`uhuuData.locale`, re-emitted by the SDK as `loaded`), then `?lang=`,
54
- `<html lang>` and the browser. No new exports: `InteractiveModeProvider` takes `locale` and
55
- `translations` (per-language overrides, or a whole catalog for another language, bundled or
56
- fetched), `useInteractive()` reports the resolved `locale`, and `label` in `templateConfig` and
57
- `pageOptions` (pages, groups, options, select and colour values) plus `reorderTitle` /
58
- `reorderDescription` accept one string per language (`{ en: 'Cover', de: 'Titelseite' }`).
59
- Plain string labels and developer tooling are unchanged. See `docs/editor-shell-translations.md`.
60
-
61
- ### Changed
62
-
63
- - Generate the delivery README from owning Storybook package documentation and include the source CHANGELOG with every release. Public documentation describes current static/flow, editor, image and BrandKit APIs.
64
- - Ship one UMD artifact, `uhuu-components.umd.cjs`, for CommonJS and browser-global use, with its matching source map. The duplicate `.umd.js` is removed from future releases; package-root ESM and CommonJS entries remain compatible. Direct `.umd.js` URLs must stay on their existing pinned release or migrate to `.umd.cjs`.
65
- - Replace the public handoff repository’s unrelated application ignore rules with package-specific rules. The published 0.3.0 artifacts remain immutable.
66
-
67
- ## 0.3.0
68
-
69
- ### Changed
70
-
71
- - `BrandKitProvider` applies a Brand Kit `brandkit.json.runtime` block (`version: 2`, brandkit.json
72
- "2.0") as published, with the token chains skipped:
73
- - shadcn/ui role variables, `--<role>` and `--color-<role>` (`--primary`, `--muted-foreground`,
74
- `--chart-1` …), so Tailwind v4's `bg-primary` / `text-muted-foreground` resolve with no bridge;
75
- - the `--color-kit-*` / `--font-kit-*` aliases with the meanings templates were written against
76
- (`kit-muted` is grey text, `kit-secondary` the strong second brand colour);
77
- - `--font-<role>`.
78
- Entries are re-validated against an allowlist of names (a published document cannot set
79
- `--radius` or `--spacing`) and the colour policy. Fonts come from `runtime.fontStylesheets` and
80
- `runtime.fontFaces` (through the same `@font-face` builder), so only what the four font roles use
81
- is loaded. Any other `runtime` (including the never-published `version: 1`) is ignored and the
82
- chains apply. `defaults` still fill gaps; the kit wins.
83
- - **Colour policy:** `oklch()` and `oklab()` with plain components pass as written (Brand Kit
84
- publishes oklch; Chrome prints it since 111). Relative colour syntax and nested functions are
85
- still skipped.
86
- - Kits without `runtime` (template-bundled kits) also land Brand Kit's
87
- `tokens.light.semantic` roles on `--<role>` / `--color-<role>`. Flat tokens never feed a role:
88
- extracted kits use some of these words with other meanings (`muted` is grey text there).
89
- - New `BrandKitJson.runtime` / `colorMode` types (`BrandKitRuntimeBlock`, `version: 2`).
90
- - **The provider loads the kit.** `src` fetches a brandkit.json (`brandKitSourceUrl(payload)` reads
91
- `brandKitUrl`, `kit-…` ids with `brandKitTeamId`, or `brandKit.storage.publicUrl`); the `brandKit`
92
- prop renders until it arrives and when the fetch fails. `status` (and `data-uhuu-brand-kit-status`
93
- on the scope element) is `idle`, `loading`, `ready` or `error`. `loadBrandKit` is exported.
94
- - **Assets from the kit.** `useBrandKit()` adds `logo({ kind, background })` (Brand Kit's
95
- `logoSystem` first, then `logos`, then template-bundled shapes), `collection(kind)` (the assigned
96
- media collection's promoted version, items resolved), `mapStyle(mode)`, `env(keys)` and
97
- `resolveUrl(path)`, all resolving relative paths against the kit's `baseUrl` or its source URL.
98
- The same functions are exported (`brandKitLogo`, `brandKitCollection`, `brandKitMapStyle`,
99
- `brandKitEnv`). Templates delete their copied `src/brandkit/brand-kit-client.js` and
100
- `BrandKitContext.jsx`; the registry-style.json request they made is gone (`runtime` carries the
101
- shadcn roles).
102
- - The Brand Kit Switcher story renders through `BrandKitProvider` and includes Brand Kit's golden
103
- kit (`test/fixtures/golden-brandkit.json`, copied from uhuu-brandkit, brandkit.json "2.0").
104
- - The copy of `dist/` into the open-source handoff checkout is an explicit step,
105
- `npm run release:handoff` (`scripts/handoffDist.js`, with `HANDOFF_DIR` / `--handoff-dir` and
106
- `--dry-run`). `npm run build` writes only under `dist/` and no longer overwrites the public
107
- package tree as a side effect. `scripts/release-components.sh` runs build → handoff → verify.
108
- Covered by `test/handoff-dist.test.mjs`.
109
- - Release pipeline (`.github/workflows/release-components.yml`): installs with pnpm
110
- (`pnpm/action-setup` reading the new `packageManager` pin, `pnpm install --frozen-lockfile`,
111
- pnpm store cache) instead of `npm ci` against the absent `package-lock.json`; runs the test suite
112
- after the build; reads `HANDOFF_REPO_TOKEN` / `NPM_TOKEN` only in the opt-in jobs through `env:`
113
- with fail-fast checks, publishes through `actions/setup-node`'s registry `.npmrc`, and keeps
114
- `GITHUB_TOKEN` read-only. `pnpm test` now builds first (`pnpm test:unit` skips the build).
115
-
116
- ## 0.2.98
117
-
118
- ### Added
119
-
120
- - `getDialogProps` is exported for binding a template's own element to a dialog, using the same
121
- renderer guard and markers as `Editable` and `ImageBlock`.
122
- - `imageUrl` provides shared thumbnail sizing, source encoding, SVG pass-through and empty-image
123
- handling. See [image-url.md](docs/image-url.md).
124
- - `BrandKitProvider` and `useBrandKit` share token mapping, font loading and CSS variable scoping.
125
- Brand fallbacks stay in templates. Font assets honor face-level stylesheets, root-relative URLs
126
- and declared Google Fonts styles. See [brand-kit-runtime.md](docs/brand-kit-runtime.md).
127
-
128
- ### Removed
129
-
130
- - The unused internal `imgOptim` / `imgBlank` helpers (`src/uhuu/utility/helpers.js`). They were
131
- never exported from the package root.
132
-
133
- ### Fixed
134
-
135
- - Empty `text`, `textarea` and `markdown` bindings stay clickable in the interactive editor.
136
- `Editable` and `Static.FlowDocument` mark empty text content; elements bound with `getDialogProps`
137
- use `data-uhuu-type` and `:empty`. The one-line affordance is scoped to editor screen output.
138
- Templates control whether empty fields preserve space or disappear in print.
139
- - The library continues to leave Markdown rendering and typography to templates; wrap the
140
- template's renderer in `Editable` and pass `null` for empty text. No Markdown runtime is bundled.
141
-
142
- ## 0.2.96
143
-
144
- ### Added
145
-
146
- - **Cover spread geometry for perfect binding (`Static.planCoverSpread`, `Static.resolveSheetSize`).**
147
- Pure, DOM-free planner in `src/uhuu/pagination-static/spread-core.js` that turns
148
- `pageFormat.binding = { spine, glue }` plus the filtered cover pages into the printer's two
149
- wide sheets — outer (back cover · spine · front cover) and inner (inside front · blank
150
- spine + glue zones · inside back) — with panel, spine and glue boxes in mm. Verified in
151
- `test/cover-spread-core.test.mjs` against the approved Lienhardt InDesign export
152
- (429 × 303 mm with 3 mm bleed) and the hand-coded Prozentbuch A6 cover sheet. `BindingConfig`
153
- is typed on `pageFormat` / `PrintConfig.pageFormat` (see `docs/printer-cover-spine-support.md`).
154
- - **`Static.CoverSpread` — one physical cover sheet for the Static-only template path.**
155
- `sheet="outer"` renders back cover · spine · front cover, `sheet="inner"` inside front · blank
156
- spine + glue masks · inside back. Panels are ordinary `Static.Sheet`s clipped at the spine, so
157
- ImageBlock/overlays keep their bleed contract and full-bleed artwork is cut cleanly at the spine;
158
- glue zones and the inner spine are masked to `--uhuu-paper-color`. With `showBleed` the spine and
159
- glue strips carry hatched, labelled screen-only guides. Without a binding the panels degrade to
160
- two plain sheets. Story: `Pagination/Cover Spread` (Lienhardt A4, Prozentbuch A6, outer only,
161
- saddle-stitch fallback, print preview). Rendered-DOM contract tested from the built package in
162
- `test/cover-spread-render.test.mjs`.
163
- - **`PageEditor` composes perfect-binding cover spreads.** With `pageFormat.binding = { type: 'perfect',
164
- spine, glue }` and `pageFilter.mode === 'cover'`, the filtered cover pages are laid out on the
165
- printer's sheets through `Static.CoverSpread`: outer (back cover · spine · front cover) and inner
166
- (inside front · blank spine + glue masks · inside back). `binding` is ignored in `all`/`text`
167
- modes, so it can live in the base page format or in `printConfigs.cover.pageFormat`; the dev
168
- print switcher picks it up unchanged. `two_pages` preview is coerced to single-page while a
169
- spread is active. Cover page components receive a `spread` prop (`{ sheet, side, spine, glue,
170
- bleed }`), and `templateConfig.spine.component` paints the outer spine strip (`SpineComponentProps`).
171
- Thumbnails, add/reorder dialogs and flow measurement always use a binding-less setup so the
172
- `@page` size never flips. Unsupported cases (coverPageCount other than 1 or 2, saddle stitch)
173
- fall back to plain cover pages with a one-time dev warning. Decision logic is pure
174
- (`cover-spread-layout.js`, tested in `test/cover-spread-layout.test.mjs`); stories under
175
- `EditorShell/Cover Spine`.
176
- - **Sheet-level CSS vars drive `@page`.** `--uhuu-sheet-width/height` default to page + bleed and
177
- become `2 × width + spine + 2 × bleed` while `setup.binding.spine > 0`, alongside
178
- `--uhuu-spine-width`, `--uhuu-glue-width` and `--uhuu-paper-color`. `PageSizeUtils.resolveCssVars`
179
- exposes the map; `config.page` now also carries `binding` and `sheet`.
180
-
181
- ### Changed
182
-
183
- - **`PageSizeUtils.pageParams` returns the page config without a DOM.** Root CSS vars are only
184
- written when `document` exists, but `ConfigContext` consumers (Sheet, CoverSpread, ImageBlock) now
185
- see the same `page` during server rendering as in the browser; previously the context was empty on
186
- the server.
187
-
188
- ### Fixed
189
-
190
- - **Editable nodes no longer override template layout utilities.** The indicator's fallback
191
- `position: relative` now lives in Tailwind's `components` layer, so a template's normal
192
- `absolute` utility wins in the editor as it does in renderer mode. Existing inline, important,
193
- and wrapper workarounds remain compatible but are no longer needed.
194
-
195
- ### Changed
196
-
197
- - **`PageEditor` now opens fitted to the page.** `defaultZoomMode` defaults to `fit-page`
198
- instead of `manual`, so a document loads with a whole sheet visible rather than at the
199
- numeric `defaultZoom` (80%), which left large formats cropped on both axes until the author
200
- opened the zoom menu. Fit keeps tracking layout and viewport changes until a manual zoom
201
- (toolbar entry, ± buttons, Ctrl/Cmd-wheel or pinch) clears it. Pass
202
- `defaultZoomMode="manual"` to restore the previous numeric-zoom start.
203
-
204
- ### Fixed
205
-
206
- - **A starting fit mode no longer flashes the unfitted page.** Fit cannot resolve during the
207
- first commit — `Section` publishes its natural size from an effect — so the editor painted
208
- `defaultZoom` for a frame or two before snapping to the fitted zoom. The sheet stack now stays
209
- `visibility: hidden` (still laid out, so the measurements fit needs keep arriving) until the
210
- first fit lands, with a 1s reveal fallback so an unmeasurable document can never stay hidden.
211
-
212
- - **Pointer-anchored zoom now holds the point it was given.** The focal correction measured the
213
- whole page stack, but the stack's height is *affine* in zoom rather than proportional — every
214
- section contributes a header and spacer that stay a fixed pixel size, so it measures
215
- `naturalTotal * zoom + chromeTotal`. `zoomFocalScrollDelta` maps a point by the ratio of the
216
- before/after rects, which only holds for a uniformly scaled box, so the anchor drifted by the
217
- accumulated chrome: **~64px going 122% → 200%**, and worse the deeper the target sat in the
218
- document. The correction now anchors on the individual sheet under the pointer
219
- (`pickZoomFocalAnchor`), which *is* uniformly scaled, making the mapping exact. Note the
220
- earlier "sub-pixel" verification of this path was circular — it re-derived the expected
221
- position with the same formula under test — so the drift went unnoticed.
222
-
223
- - **Fit to Page no longer behaves like Fit to Width, and Fit to Height no longer overshoots.**
224
- The pan viewport's geometry lived only in `section-page-resizer.css`, and not every consumer
225
- ends up with that stylesheet — Template Studio renders the editor in an iframe whose document
226
- never receives it. Without those rules the pane degraded to a plain block reporting its whole
227
- content height (measured: **4711px inside a 665px iframe**), so fit divided by a viewport
228
- seven times too tall: Fit to Height computed **413%** (clamped to `maxZoom`) and Fit to Page,
229
- being `min(width, height)`, always collapsed to the width value. The pane and its content box
230
- now carry their load-bearing geometry as inline styles, and `resolveFitPaneHeight` clamps the
231
- measured pane to the space actually visible in its own document, so a mis-sized pane can no
232
- longer corrupt fit. Verified in the Studio iframe: pane height now tracks the iframe viewport
233
- (665 → 851 on resize) and Fit to Width / Fit to Height resolve to distinct values
234
- (84.1% / 47.4%) where they were previously 90.2% / 413%.
235
-
236
- ## 0.2.90
237
-
238
- ### Fixed
239
-
240
- - **A zoomed-in page can be scrolled to on both sides.** `SectionPageResizer` had no scroll
241
- container of its own, so it rode the document scroller while centering the page stack with
242
- `items-center`. Horizontal overflow was therefore split evenly to the left and right, and
243
- because `scrollLeft` cannot go negative the left half was permanently unreachable —
244
- measured at 277px of a 554px overflow in a 716px viewport. The editor now renders its own
245
- pan viewport, so every pixel of the sheet is reachable at any zoom, in single-page and
246
- facing modes alike.
247
- - **Pointer-anchored zoom no longer drifts once zoomed in.** The scroll correction clamped at
248
- `scrollLeft: 0` whenever the anchor sat left of centre, silently losing the point under the
249
- cursor. With a real scroller on both axes the anchor now holds to sub-pixel accuracy
250
- wherever scroll range exists.
251
- - **The toolbar zoom controls keep their place.** `+` / `−` and the `%` input previously did
252
- no scroll correction at all and drifted the viewport; all zoom entry points now share one
253
- path and anchor on the centre of the visible pane.
254
- - **Fit to Width / Height / Page measure real space.** Fit used fixed `32px` / `120px` insets
255
- against the window; it now uses the pane's own box, its rendered gutters, and the measured
256
- section chrome.
257
-
258
- ### Changed
259
-
260
- - **`SectionPageResizer` scrolls itself rather than the document.** Hosts that already provide
261
- a scroller and want the previous behavior can pass `scrollMode="document"`. Note that the
262
- editor no longer grows the page, so host-level page scrolling is replaced by pane scrolling.
263
-
264
- ## 0.2.87
265
-
266
- ### Fixed
267
-
268
- - **The image options menu ("Edit image" / "Annotate") stays on the image in bleed and spread
269
- mode.** It was rendered as a sibling of the absolutely positioned image container, so it
270
- anchored to the page and landed in the page header — templates using `top`/`left`/`right`/
271
- `bottom` insets (satellite and cadastral spreads) had to reposition it by hand. It is now
272
- anchored to the image rect, clamped to the page the image renders on, so both halves of a
273
- spread get the menu on their own visible corner. Bleed, spread and the anchor share one
274
- width calculation (`image-anchor-geometry.ts`) so they cannot drift apart.
275
-
276
- ## 0.2.86
277
-
278
- ### Fixed
279
-
280
- - **Wheel / pinch zoom keeps the pointer-anchored location.** After Ctrl/Cmd-wheel or trackpad
281
- pinch, SectionPageResizer corrects document scroll so content under the pointer (e.g. a later
282
- page) does not jump toward the first page as sheet heights grow.
283
-
284
- ## 0.2.85
285
-
286
- ### Added
287
-
288
- - **PageEditor / SectionPageResizer support Ctrl/Cmd-wheel and trackpad-pinch zoom.** Plain wheel
289
- still scrolls; modifier or pinch zooms proportionally (~0.3%/px, rAF-batched) and clears fit
290
- mode the same way the toolbar ± controls do.
291
-
292
- ## 0.2.84
293
-
294
- ### Fixed
295
-
296
- - **Single-page Fit to Page no longer sums every stacked sheet width.** Fit targets the first
297
- sheet only (facing mode still fits the first pair). Multi-page documents no longer clamp to
298
- `minZoom` (e.g. 25%) on load when `defaultZoomMode="fit-page"`.
299
-
300
- ### Added
301
-
302
- - Added `Static.FlowColumns` and `Static.planFlowColumnChunks` for bounded, non-recursive
303
- sequential/parallel Flow stories. Each column advances its measured cursor independently on
304
- every output page, exhausted tracks preserve their geometry, and all leaf keep/break/header,
305
- oversized-item, and no-progress behavior continues to come from the existing Flow planner.
306
- - Added column-scoped render hooks for track width, distribution, stack alignment, and measured
307
- child spacing so authoring previews and published output can consume one authoritative plan.
308
-
309
- ### Fixed
310
-
311
- - Discarded short-page placement attempts no longer emit false unplaceable diagnostics when a
312
- whole column group is successfully retried on a fresh page.
313
- - Keep-with-next and avoid-break bundles now retry on a fresh physical page when the remaining
314
- space would orphan their first leaf; composite header height determines whether that retry can
315
- succeed.
316
- - Repeated group headers retain continuation and `repeatHeader: false` state independently inside
317
- each column, with header measurements scoped by column group, track, and group key.
318
- - Column plans now reject duplicate/missing layout identities and leaf indexes before rendering,
319
- terminate a page after controlled unplaceable output, and fail closed on any no-progress state.
320
- - Measurement and output now share a geometry-invariant track contract: page-dependent column
321
- styles and unmeasured vertical wrapper spacing are unsupported, while measured child margins
322
- remain the canonical stack-gap mechanism.
323
-
324
- - **PageEditor can now start in a layout-tracking fit mode.** `defaultZoomMode="fit-page"`
325
- measures the current preview viewport and sheets directly, then keeps fit current through content
326
- and viewport changes until the author chooses a manual zoom.
327
-
328
- - **The package root is now a real dual ESM/CommonJS public API.** `import { Static } from
329
- "uhuu-components"` resolves to the ESM bundle while CommonJS consumers resolve a `.cjs` copy of
330
- the UMD bundle. Consumers no longer need a distribution-file deep import.
331
-
332
- - Aligned `src/index.d.ts` with the actual minimal root runtime exports (`Static`, `EditorShell`,
333
- `ImageBlock`, `Editable`) so TypeScript consumers are not offered imports that fail at runtime.
334
- - Corrected the ImageBlock manifest to describe the existing root named import from
335
- `uhuu-components`, not an unpublished `uhuu-components/ImageBlock` subpath.
336
- - Made handoff checksum comparison explicit via `--compare-handoff` and kept CI npm publishing
337
- downstream of a synced handoff repo checkout.
338
- - Fixed the cross-repo Print Document Contract link in `docs/print-mode.md`.
339
-
340
- ### Added
341
-
342
- - GitHub Actions release workflow (`.github/workflows/release-components.yml`): tag `v*` or
343
- `workflow_dispatch` build, dist integrity checks, sha256 bundle output, version bump guard vs npm
344
- `latest`, optional handoff sync (`HANDOFF_REPO_TOKEN`) and npm publish (`NPM_TOKEN`).
345
- - Release verification script `scripts/verifyDistRelease.js`, manual fallback
346
- `scripts/release-components.sh`, and `npm run release:verify`.
347
- - Component manifest pilot for `ImageBlock` (`src/manifests/image-block.manifest.json`) with JSON Schema (`src/manifests/component-manifest.schema.json`), docs (`docs/component-manifests.md`), validation test (`test/manifests.test.mjs`), and Storybook manifest story (`stories/ImageBlockManifest.stories.tsx`).