@dataimago/ui 0.1.0-alpha.4 → 0.1.0-alpha.43

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/dist/index.d.cts CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as React from 'react';
2
+ import React__default from 'react';
2
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { LogoId, LogoVariant, AnimationId, NavbarLogoId } from '@dataimago/brand';
3
5
 
4
6
  type ButtonVariant = 'primary' | 'secondary' | 'ghost';
5
7
  type ButtonSize = 'sm' | 'md' | 'lg';
@@ -124,4 +126,387 @@ interface LogoWordmarkProps extends React.SVGAttributes<SVGSVGElement> {
124
126
  }
125
127
  declare function LogoWordmark({ width, title, ...rest }: LogoWordmarkProps): react_jsx_runtime.JSX.Element;
126
128
 
127
- export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, LogoMonogram, LogoMonogramBg, type LogoMonogramBgProps, type LogoMonogramProps, LogoWordmark, type LogoWordmarkProps };
129
+ interface LogoProps extends React.HTMLAttributes<HTMLSpanElement> {
130
+ /** Mark id, e.g. `dissertation_ai_OVERLAP`. */
131
+ name: LogoId;
132
+ /** PATH (default), CUTOUT, BG, or LETTERS. See @dataimago/brand. */
133
+ variant?: LogoVariant;
134
+ /**
135
+ * Render width (px number or any CSS length). Height scales proportionally
136
+ * via the SVG viewBox. Omit to use the mark's intrinsic size.
137
+ */
138
+ width?: number | string;
139
+ }
140
+ /**
141
+ * Manifest-driven brand mark from `@dataimago/brand`. Inlines the SVG string so
142
+ * `currentColor` (PATH/LETTERS), the `--color-interactive-accent` badge fill
143
+ * (CUTOUT/BG), and the per-letter interaction classes (`.glyph`, `.logo-*`) all
144
+ * work — an `<img src>` can't receive those interior styles.
145
+ *
146
+ * Color flows from CSS: set `color` (PATH/LETTERS) on this element or an
147
+ * ancestor. Size via `width` (or CSS on the wrapper).
148
+ */
149
+ declare function Logo({ name, variant, width, style, ...rest }: LogoProps): react_jsx_runtime.JSX.Element | null;
150
+
151
+ /** A link the lockup renders; pass one to route through a framework link (e.g. next/link). */
152
+ interface CopulaLinkProps {
153
+ href: string;
154
+ 'aria-label': string;
155
+ className?: string;
156
+ style?: React.CSSProperties;
157
+ children: React.ReactNode;
158
+ }
159
+ interface CopulaLockupProps extends React.HTMLAttributes<HTMLSpanElement> {
160
+ /** The property's wordmark id, e.g. `dissertation_ai`. Its PATH variant is drawn. */
161
+ property: LogoId;
162
+ /** Accessible name of the property link, e.g. `dissertation-ai`. */
163
+ propertyLabel: string;
164
+ /** Where the property mark links. Defaults to `/`. */
165
+ propertyHref?: string;
166
+ /**
167
+ * `shown` (default): "<property> is dataimago". `hidden`: the property mark
168
+ * alone, with no badge, no dataimago wordmark and no link to dataimago, for
169
+ * apps that keep dataimago out of view while it is in stealth.
170
+ */
171
+ dataimago?: 'shown' | 'hidden';
172
+ /** Where the dataimago wordmark links. Defaults to https://dataimago.ai. */
173
+ brandHref?: string;
174
+ /** Wordmark height in px. Defaults to 32; the badge and spacing scale with it. */
175
+ height?: number;
176
+ /** Class for each link (colour and hover); both links share one treatment. */
177
+ linkClassName?: string;
178
+ /**
179
+ * Class for the `is` disc. It is not a link, so `linkClassName` does not reach
180
+ * it: give it the links' rest colour here (e.g. the same `text-*` class
181
+ * without the hover). Without it the disc takes the lockup's colour.
182
+ */
183
+ badgeClassName?: string;
184
+ /** Render the links with a framework component. Defaults to a plain `<a>`. */
185
+ renderLink?: (props: CopulaLinkProps) => React.ReactNode;
186
+ }
187
+ /**
188
+ * The copula lockup, "dissertation-ai **is** dataimago": the property wordmark,
189
+ * the `is_badge` disc and the dataimago wordmark, each wordmark its own link
190
+ * (wiki/connections/is-copula-coupling.md). The badge is a mark, not a link: at
191
+ * 16px it is below the 24px pointer-target floor (WCAG 2.5.8), so it is read as
192
+ * "is" and the philosophy page stays reachable from the nav.
193
+ *
194
+ * Spacing is ink-symmetric about the badge centre, computed from each mark's
195
+ * `ink` box in the @dataimago/brand manifest, so any property mark lines up
196
+ * without hand-tuned margins. Geometry is inline style (no Tailwind), so a
197
+ * consumer on plain CSS can use it too. Every piece is `currentColor`: the
198
+ * wordmarks take their link's colour (`linkClassName`, or the page's link
199
+ * colour), the disc takes `badgeClassName`'s, or the lockup's.
200
+ */
201
+ declare function CopulaLockup({ property, propertyLabel, propertyHref, dataimago, brandHref, height, linkClassName, badgeClassName, renderLink, style, ...rest }: CopulaLockupProps): react_jsx_runtime.JSX.Element | null;
202
+
203
+ interface BrandAnimationProps extends React.HTMLAttributes<HTMLSpanElement> {
204
+ /** Animation id, e.g. `dissertation-ai`. See @dataimago/brand animations.json. */
205
+ name: AnimationId;
206
+ /**
207
+ * Render width (px number or any CSS length). Height scales via the SVG
208
+ * viewBox. Omit to use the animation's intrinsic size.
209
+ */
210
+ width?: number | string;
211
+ }
212
+ /**
213
+ * Decorative brand animation from `@dataimago/brand` (the animation-foundry).
214
+ * Inlines the pure-SMIL SVG string so its interior `<style>` works — an
215
+ * `<img src>` can't receive those styles (same rationale as `<Logo>`).
216
+ *
217
+ * Motion-dignity: decorative brand animation is a distinct category from the
218
+ * 300ms UI-transition ceiling, but it is contractually gated on
219
+ * `prefers-reduced-motion`. When the user prefers reduced motion this renders
220
+ * the static brand mark the animation settles to (its `poster`) via `<Logo>`,
221
+ * so no consumer can ship the motion without the fallback.
222
+ */
223
+ declare function BrandAnimation({ name, width, style, ...rest }: BrandAnimationProps): react_jsx_runtime.JSX.Element | null;
224
+
225
+ interface NavbarLogoProps extends React.HTMLAttributes<HTMLSpanElement> {
226
+ /** Navbar mark id, e.g. `dissertation-ai`. See @dataimago/brand navbar.json. */
227
+ name: NavbarLogoId;
228
+ /**
229
+ * Render width (px number or any CSS length). Height scales via the SVG
230
+ * viewBox. Omit to use the mark's intrinsic size.
231
+ */
232
+ width?: number | string;
233
+ }
234
+ /**
235
+ * Animated brand mark for an app navbar, from `@dataimago/brand` (the
236
+ * animation-foundry's navbar set). Inlines the self-contained CSS-animated SVG
237
+ * so its interior `<style>` works — an `<img src>` can't receive those styles.
238
+ *
239
+ * Behaviour by mark kind (from the navbar manifest):
240
+ * - `assemble` (L2: dissertation-ai, rpkg-ai, SGPc-ai): the trailing `a`/`i`
241
+ * start apart and the `i` slides onto the `a`. Plays once on mount; replays
242
+ * on pointer-enter via a remount (`replayKey`).
243
+ * - `shimmer` (L1: dataimago-ai): the monogram is always superimposed; an
244
+ * accent shimmer sweeps on `:hover` from the SVG's own CSS (no JS trigger).
245
+ * - `dream` (promoted dream marks, `tools/dream-foundry/drop.mjs promote`):
246
+ * a self-contained CSS hover gesture like liftwash; the poster is the mark's
247
+ * own static form (`dream_<slug>` / `DREAM`). Ink is `currentColor`.
248
+ *
249
+ * Motion-dignity: the embedded CSS animation is <= 300ms and gated on
250
+ * `@media (prefers-reduced-motion: no-preference)`. When the user prefers
251
+ * reduced motion this renders the static settled mark (the manifest `poster`,
252
+ * the superimposed monogram) via `<Logo>` — so no consumer can ship the motion
253
+ * without the fallback.
254
+ */
255
+ declare function NavbarLogo({ name, width, style, ...rest }: NavbarLogoProps): react_jsx_runtime.JSX.Element | null;
256
+
257
+ /**
258
+ * Light/dark theme preference for dataimago apps.
259
+ *
260
+ * The house tokens (`@dataimago/tokens`) follow the operating system through
261
+ * `prefers-color-scheme` and yield to an explicit `data-bs-theme` on the root:
262
+ * `light` or `dark` forces that theme, no attribute means "follow the system".
263
+ * This module owns that attribute: it reads and stores the reader's choice,
264
+ * applies it, and reports the theme actually in effect (for canvas and WebGL
265
+ * surfaces that cannot read CSS variables).
266
+ *
267
+ * The choice is a per-browser convenience in localStorage. Every read and write
268
+ * is guarded: in a private window or with storage blocked the page simply
269
+ * follows the system.
270
+ */
271
+ type ThemePreference = 'system' | 'light' | 'dark';
272
+ type ResolvedTheme = 'light' | 'dark';
273
+ declare const THEME_STORAGE_KEY = "dataimago-theme";
274
+ declare const THEME_ATTRIBUTE = "data-bs-theme";
275
+ /**
276
+ * Inline this in `<head>` before any stylesheet paints, so a stored choice is
277
+ * on the root before first paint (no flash of the other theme). The server
278
+ * markup cannot know the choice, so the consumer also puts
279
+ * `suppressHydrationWarning` on `<html>`. From a React server layout, render
280
+ * `<ThemeScript />` instead: this package is a client bundle, so a server
281
+ * component cannot import the string itself.
282
+ *
283
+ * ```tsx
284
+ * <html lang="en" suppressHydrationWarning>
285
+ * <head><ThemeScript /></head>
286
+ * ```
287
+ */
288
+ declare const themeInitScript: string;
289
+ /** The stored choice, or `'system'` when there is none (or storage is unavailable). */
290
+ declare function readThemePreference(): ThemePreference;
291
+ /** Store, apply and announce a choice. `'system'` clears the stored choice. */
292
+ declare function setThemePreference(preference: ThemePreference): void;
293
+ /** The theme in effect: the root attribute when set, otherwise the system's. */
294
+ declare function resolveTheme(): ResolvedTheme;
295
+ /**
296
+ * The reader's choice and a setter. Server render and hydration see
297
+ * `'system'`; the stored choice arrives right after.
298
+ */
299
+ declare function useThemePreference(): [ThemePreference, (p: ThemePreference) => void];
300
+ /**
301
+ * The theme in effect, for surfaces that cannot read CSS variables (canvas,
302
+ * WebGL maps). Follows the toggle and, under "system", the operating system.
303
+ * Server render and hydration see `'light'`.
304
+ */
305
+ declare function useResolvedTheme(): ResolvedTheme;
306
+
307
+ interface ThemeToggleLabels {
308
+ /** What the control sets, read before the theme in effect ("Theme: Light"). */
309
+ name: string;
310
+ /** Added when the theme follows the operating system ("Theme: Light (System)"). */
311
+ system: string;
312
+ light: string;
313
+ dark: string;
314
+ }
315
+ interface ThemeToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children' | 'type'> {
316
+ /** Accessible and tooltip text; pass the property's own wording. */
317
+ labels?: Partial<ThemeToggleLabels>;
318
+ }
319
+ /**
320
+ * The choice one click makes: the other theme, stored as "system" when that is
321
+ * the operating system's own, so the page goes back to following it.
322
+ */
323
+ declare function nextThemePreference(shown: ResolvedTheme, system: ResolvedTheme): ThemePreference;
324
+ /**
325
+ * Light/dark toggle for an app navbar: one button, and one click always flips
326
+ * the theme on screen. The page follows the operating system until the first
327
+ * click; a click that lands on the system's own theme goes back to following
328
+ * it (no stored choice), and the other theme is stored and set as
329
+ * `data-bs-theme` on the root, which the house tokens obey. The icon shows the
330
+ * theme in effect: a sun in light, a moon in dark.
331
+ *
332
+ * A 48px target (the dataimago touch floor). The icon is `currentColor`.
333
+ * Inline styles would beat a consumer's `:hover` rule, so the two colours a
334
+ * navbar restyles read custom properties instead: set `--di-theme-toggle-fg`
335
+ * and `--di-theme-toggle-bg` on `.di-ui-theme-toggle` (and its `:hover`).
336
+ * Pair it with `<ThemeScript />` in `<head>` so a stored choice applies before
337
+ * first paint.
338
+ */
339
+ declare function ThemeToggle({ labels, className, style, ...rest }: ThemeToggleProps): react_jsx_runtime.JSX.Element;
340
+
341
+ interface ThemeScriptProps {
342
+ /** Content-Security-Policy nonce, when the app sets one. */
343
+ nonce?: string;
344
+ }
345
+ /**
346
+ * Renders `themeInitScript` as an inline `<script>`, so a stored choice is on
347
+ * the root before first paint. Use this from a server layout: this package is
348
+ * a client bundle, so a server component cannot import the plain string, but
349
+ * it can render this component. Put it in `<head>` and add
350
+ * `suppressHydrationWarning` to `<html>` (the script changes its attributes
351
+ * before React hydrates).
352
+ */
353
+ declare function ThemeScript({ nonce }: ThemeScriptProps): react_jsx_runtime.JSX.Element;
354
+
355
+ /** The captions file a video release carries beside its WebVTT fallback. */
356
+ declare const CAPTIONS_SCHEMA = "dataimago.video_captions.v1";
357
+ type CaptionTreatment = 'editorial' | 'frosted' | 'boxed';
358
+ interface CaptionWord {
359
+ /** The word as the script spells it; inline math stays a `$…$` span. */
360
+ text: string;
361
+ /** Spoken start, in the page's time unit: the word tones from inkMute to ink from here. */
362
+ at: number;
363
+ /** The line of the page the word sits on. */
364
+ line: number;
365
+ /** MathML for the word's `$…$` span, rendered by the foundry (Temml) at export. */
366
+ math?: string;
367
+ }
368
+ interface CaptionPage {
369
+ start: number;
370
+ end: number;
371
+ lines: string[];
372
+ words: CaptionWord[];
373
+ }
374
+ interface CaptionTrack {
375
+ schema: typeof CAPTIONS_SCHEMA;
376
+ work: string;
377
+ version: string;
378
+ lang: string;
379
+ style: {
380
+ profile: string;
381
+ treatment: CaptionTreatment;
382
+ /** Font weight of the caption text (400 when boxed). */
383
+ weight: number;
384
+ /** How long a word takes to tone from inkMute to ink, in seconds. */
385
+ toneSeconds: number;
386
+ lineHeight: number;
387
+ /** The profile's colour tokens (`color.palette.<scale>.<step>`); the player maps them to CSS variables. */
388
+ colors?: {
389
+ paper: string;
390
+ ink: string;
391
+ inkMute: string;
392
+ };
393
+ };
394
+ /** The canvas the captions were laid out on, in pixels: scale everything by displayed width ÷ width. */
395
+ frame: {
396
+ width: number;
397
+ height: number;
398
+ fontSize: number;
399
+ safe: {
400
+ top: number;
401
+ right: number;
402
+ bottom: number;
403
+ left: number;
404
+ };
405
+ };
406
+ /** In seconds from the start of the video, in order, never overlapping. */
407
+ pages: CaptionPage[];
408
+ }
409
+ /** The page on screen at time t (start ≤ t < end), if any. Pages must be in order. */
410
+ declare function pageAt<P extends {
411
+ start: number;
412
+ end: number;
413
+ }>(pages: readonly P[], t: number): P | undefined;
414
+ /** How far a word has toned toward ink at time t: 0 before it is spoken, 1 once it has settled. */
415
+ declare function wordTone(at: number, t: number, span: number): number;
416
+ /** The colour for a tone between inkMute (0) and ink (1). */
417
+ declare function toneColor(tone: number, ink: string, inkMute: string): string;
418
+ /** A word's text split around its `$…$` span, or null when it carries no math. */
419
+ declare function splitMath(text: string): {
420
+ before: string;
421
+ tex: string;
422
+ after: string;
423
+ } | null;
424
+ /** A palette token path as a CSS variable (`color.palette.stone.50` → `var(--color-palette-stone-50)`), or null if it is not one. */
425
+ declare function paletteVar(token: string): string | null;
426
+ type CaptionStyle = Record<string, string | number>;
427
+ interface CaptionLook {
428
+ /** The panel around the lines. */
429
+ panel: CaptionStyle;
430
+ /** The text block: family, weight, size, line height. */
431
+ text: CaptionStyle;
432
+ /** Extra lift above the safe area, in pixels, so a panel's soft shadow stays inside it too. */
433
+ lift: number;
434
+ /** Whether words tone from inkMute to ink (boxed captions are one colour). */
435
+ toned: boolean;
436
+ }
437
+ /**
438
+ * The treatment at one size. `scale` multiplies the profile's fixed pixel measures
439
+ * (blur, border, radius on a boxed panel) and is 1 on the foundry's canvas.
440
+ */
441
+ declare function captionLook(treatment: CaptionTreatment, o: {
442
+ size: number;
443
+ scale: number;
444
+ weight: number;
445
+ lineHeight: number;
446
+ family: string;
447
+ ink: string;
448
+ paper: string;
449
+ }): CaptionLook;
450
+
451
+ interface CaptionedVideoProps {
452
+ src: string;
453
+ /** The release's poster frame: required, as the player shows it until the reader presses play (explanatory-video-carve-out, decision 2). */
454
+ poster: string;
455
+ /** The release's captions file (dataimago.video_captions.v1): pages, word timing, MathML. */
456
+ captions: string;
457
+ /** The release's WebVTT captions: the native track, and the text drawn if `captions` fails to load. */
458
+ fallbackCaptions: string;
459
+ /** The release's WebVTT chapters. */
460
+ chapters?: string;
461
+ /** A reduced-motion cut of the same video (same timing, so the same captions): played instead of `src` under `prefers-reduced-motion: reduce`. */
462
+ reducedSrc?: string;
463
+ /** The release's static storyboard: offered first, beside the video, under `prefers-reduced-motion: reduce` when there is no `reducedSrc`. */
464
+ storyboard?: string;
465
+ /** The video's accessible name. */
466
+ title: string;
467
+ /** BCP 47 language of the captions. */
468
+ lang?: string;
469
+ className?: string;
470
+ style?: React__default.CSSProperties;
471
+ }
472
+ /** Where the reader's captions choice is kept (per browser). */
473
+ declare const CAPTIONS_STORAGE_KEY = "dataimago:video-captions";
474
+ /** A captions file the layer can draw; anything else takes the WebVTT fallback. */
475
+ declare function isCaptionTrack(x: unknown): x is CaptionTrack;
476
+ /**
477
+ * A video with the dataimago caption treatment drawn over it: the foundry's fonts,
478
+ * word tone and mathematics, from the release's captions file. The native WebVTT
479
+ * track stays hidden underneath, for assistive technology and as the fallback text.
480
+ * Fullscreen takes the whole player, so the captions come along.
481
+ */
482
+ declare const CaptionedVideo: React__default.FC<CaptionedVideoProps>;
483
+
484
+ interface CaptionLayerProps {
485
+ track: CaptionTrack;
486
+ /** Seconds into the video. */
487
+ time: number;
488
+ /** The video's displayed width in CSS pixels; size and insets scale from the track's canvas. */
489
+ width: number;
490
+ /**
491
+ * Show each page in full ink, as the foundry's reduced-motion render does. Defaults to
492
+ * true, the safe side; pass the reader's `prefers-reduced-motion` (CaptionedVideo does).
493
+ */
494
+ reducedMotion?: boolean;
495
+ /**
496
+ * Keep the captions at least this far above the bottom edge, in CSS pixels (the
497
+ * player's controls). They move up only when their own place would sit lower.
498
+ */
499
+ clearance?: number;
500
+ /** A page to show instead of the track's (the WebVTT fallback, already in full ink). */
501
+ page?: CaptionPage | null;
502
+ }
503
+ /**
504
+ * The captions over a video, in the foundry's treatment, at time `time`. Pure
505
+ * presentation: the caller supplies the clock and the size. The drawn layer is hidden
506
+ * from assistive technology; the current page is also present as visually hidden plain
507
+ * text, not a live region, so a braille or screen-reader user can find it without every
508
+ * line being spoken over the narration.
509
+ */
510
+ declare const CaptionLayer: React__default.FC<CaptionLayerProps>;
511
+
512
+ export { BrandAnimation, type BrandAnimationProps, Button, type ButtonProps, type ButtonSize, type ButtonVariant, CAPTIONS_SCHEMA, CAPTIONS_STORAGE_KEY, CaptionLayer, type CaptionLayerProps, type CaptionLook, type CaptionPage, type CaptionTrack, type CaptionTreatment, type CaptionWord, CaptionedVideo, type CaptionedVideoProps, type CopulaLinkProps, CopulaLockup, type CopulaLockupProps, Logo, LogoMonogram, LogoMonogramBg, type LogoMonogramBgProps, type LogoMonogramProps, type LogoProps, LogoWordmark, type LogoWordmarkProps, NavbarLogo, type NavbarLogoProps, type ResolvedTheme, THEME_ATTRIBUTE, THEME_STORAGE_KEY, type ThemePreference, ThemeScript, type ThemeScriptProps, ThemeToggle, type ThemeToggleLabels, type ThemeToggleProps, captionLook, isCaptionTrack, nextThemePreference, pageAt, paletteVar, readThemePreference, resolveTheme, setThemePreference, splitMath, themeInitScript, toneColor, useResolvedTheme, useThemePreference, wordTone };