@hraness/design-kit 0.36.5 → 0.39.0

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 (59) hide show
  1. package/MARKETING_COPY.md +2 -0
  2. package/README.md +77 -11
  3. package/dist/{chunk-xa28y4fg.js → chunk-1dgm85ta.js} +191 -55
  4. package/dist/chunk-6mx403v5.js +286 -0
  5. package/dist/{chunk-thv5t11w.js → chunk-dc8v05zh.js} +6 -137
  6. package/dist/{chunk-evb02bc1.js → chunk-fcs5qekr.js} +48 -1
  7. package/dist/chunk-fxwed3e5.js +775 -0
  8. package/dist/chunk-vy0pt84a.js +1807 -0
  9. package/dist/{chunk-sabcr66p.js → chunk-w1y1m3m7.js} +300 -1
  10. package/dist/index.js +24 -12
  11. package/dist/mockups/client.js +6 -654
  12. package/dist/mockups/index.js +29 -1797
  13. package/dist/provider-marks.js +1 -1
  14. package/dist/react/index.js +190 -5
  15. package/dist/react/server.js +5 -3
  16. package/dist/stylex-manifest.json +1 -1
  17. package/package.json +9 -5
  18. package/portfolio-inventory.json +1 -1
  19. package/src/compiler-foundation.css +2 -0
  20. package/src/design-gallery.css +12 -0
  21. package/src/index.ts +1 -0
  22. package/src/marketing-marquee.css +281 -0
  23. package/src/marketing-marquee.ts +206 -0
  24. package/src/mockups/client.tsx +157 -66
  25. package/src/mockups/core.tsx +30 -0
  26. package/src/mockups.css +158 -23
  27. package/src/provider-marks.generated.ts +92 -0
  28. package/src/provider-marks.ts +29 -4
  29. package/src/react/design-gallery.tsx +53 -0
  30. package/src/react/index.ts +1 -0
  31. package/src/react/marketing-marquee.tsx +71 -0
  32. package/src/react/server.ts +1 -0
  33. package/src/styles.css +2 -0
  34. package/vendor/provider-marks/BOOTSTRAP-ICONS-LICENSE +21 -0
  35. package/vendor/provider-marks/UPSTREAM.md +28 -0
  36. package/vendor/provider-marks/bootstrap-icons/calendar-event-fill.svg +3 -0
  37. package/vendor/provider-marks/bootstrap-icons/filetype-csv.svg +3 -0
  38. package/vendor/provider-marks/bootstrap-icons/globe2.svg +3 -0
  39. package/vendor/provider-marks/bootstrap-icons/linkedin.svg +3 -0
  40. package/vendor/provider-marks/bootstrap-icons/microsoft.svg +3 -0
  41. package/vendor/provider-marks/bootstrap-icons/person-vcard-fill.svg +3 -0
  42. package/vendor/provider-marks/bootstrap-icons/shop.svg +3 -0
  43. package/vendor/provider-marks/simple-icons/apple.svg +1 -0
  44. package/vendor/provider-marks/simple-icons/bluesky.svg +1 -0
  45. package/vendor/provider-marks/simple-icons/facebook.svg +1 -0
  46. package/vendor/provider-marks/simple-icons/github.svg +1 -0
  47. package/vendor/provider-marks/simple-icons/gmail.svg +1 -0
  48. package/vendor/provider-marks/simple-icons/google.svg +1 -0
  49. package/vendor/provider-marks/simple-icons/instagram.svg +1 -0
  50. package/vendor/provider-marks/simple-icons/reddit.svg +1 -0
  51. package/vendor/provider-marks/simple-icons/substack.svg +1 -0
  52. package/vendor/provider-marks/simple-icons/telegram.svg +1 -0
  53. package/vendor/provider-marks/simple-icons/threads.svg +1 -0
  54. package/vendor/provider-marks/simple-icons/tiktok.svg +1 -0
  55. package/vendor/provider-marks/simple-icons/twitch.svg +1 -0
  56. package/vendor/provider-marks/simple-icons/x.svg +1 -0
  57. package/vendor/provider-marks/simple-icons/ycombinator.svg +1 -0
  58. package/vendor/provider-marks/simple-icons/youtube.svg +1 -0
  59. package/dist/chunk-gmea6p0d.js +0 -13
@@ -0,0 +1,775 @@
1
+ import {
2
+ joinMockupClasses
3
+ } from "./chunk-fcs5qekr.js";
4
+
5
+ // src/mockups/client.tsx
6
+ import { useId, useLayoutEffect, useMemo, useRef, useState, version } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { createElement } from "react";
9
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? () => {
10
+ return;
11
+ } : useLayoutEffect;
12
+ var inertAttribute = {
13
+ inert: Number.parseInt(version, 10) >= 19 ? true : ""
14
+ };
15
+ var noInertAttribute = {};
16
+ function inertAttributes(inactive) {
17
+ return inactive ? inertAttribute : noInertAttribute;
18
+ }
19
+ function assertUniqueIds(items, what) {
20
+ if (items.length === 0)
21
+ throw new RangeError(`${what} needs at least one entry.`);
22
+ const seen = new Set;
23
+ for (const item of items) {
24
+ if (typeof item.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(item.id)) {
25
+ throw new TypeError(`${what} ids must be lowercase letters, digits, and dashes: ${JSON.stringify(item.id)}`);
26
+ }
27
+ if (seen.has(item.id))
28
+ throw new RangeError(`${what} ids must be unique: ${item.id}`);
29
+ seen.add(item.id);
30
+ }
31
+ }
32
+ function itemAt(items, index, what) {
33
+ const item = items[index];
34
+ if (item === undefined)
35
+ throw new RangeError(`${what} has no entry at ${String(index)}.`);
36
+ return item;
37
+ }
38
+ function optionalText(value, component) {
39
+ if (value === undefined)
40
+ return;
41
+ if (typeof value !== "string")
42
+ throw new TypeError(`${component} text must be a string.`);
43
+ return value.trim() || undefined;
44
+ }
45
+ function nextTabIndex(key, index, count) {
46
+ if (key === "ArrowRight" || key === "ArrowDown")
47
+ return (index + 1) % count;
48
+ if (key === "ArrowLeft" || key === "ArrowUp")
49
+ return (index - 1 + count) % count;
50
+ if (key === "Home")
51
+ return 0;
52
+ if (key === "End")
53
+ return count - 1;
54
+ return;
55
+ }
56
+ function FitToWidth({
57
+ children,
58
+ className,
59
+ minWidth = 400
60
+ }) {
61
+ if (!(minWidth > 0))
62
+ throw new RangeError("FitToWidth minWidth must be positive.");
63
+ const outer = useRef(null);
64
+ const inner = useRef(null);
65
+ const [fit, setFit] = useState(null);
66
+ useIsomorphicLayoutEffect(() => {
67
+ const stage = outer.current;
68
+ const content = inner.current;
69
+ if (stage === null || content === null || typeof ResizeObserver === "undefined")
70
+ return;
71
+ const measure = () => {
72
+ const available = stage.clientWidth;
73
+ if (available <= 0)
74
+ return;
75
+ const width = Math.max(available, minWidth);
76
+ const scale = available / width;
77
+ const height = content.offsetHeight * scale;
78
+ setFit((current) => current !== null && current.width === width && current.scale === scale && current.height === height ? current : {
79
+ height,
80
+ scale,
81
+ width
82
+ });
83
+ };
84
+ measure();
85
+ const observer = new ResizeObserver(measure);
86
+ observer.observe(stage);
87
+ observer.observe(content);
88
+ return () => observer.disconnect();
89
+ }, [minWidth]);
90
+ const scaled = fit !== null && fit.scale < 1;
91
+ return /* @__PURE__ */ jsx("div", {
92
+ className: joinMockupClasses("hkm-fit", className),
93
+ "data-hkm-min-width": minWidth,
94
+ ref: outer,
95
+ style: scaled ? {
96
+ height: fit.height
97
+ } : undefined,
98
+ children: /* @__PURE__ */ jsx("div", {
99
+ className: "hkm-fit-inner",
100
+ "data-hkm-scaled": scaled ? "" : undefined,
101
+ ref: inner,
102
+ style: scaled ? {
103
+ transform: `scale(${fit.scale})`,
104
+ width: fit.width
105
+ } : undefined,
106
+ children
107
+ })
108
+ });
109
+ }
110
+ function ModeShowcase({
111
+ caption,
112
+ className,
113
+ fit = "natural",
114
+ height = 440,
115
+ initial,
116
+ label,
117
+ minWidth = 400,
118
+ modeLabel = "Mode",
119
+ modes,
120
+ optionInactiveModes = [],
121
+ optionLabel = "Option",
122
+ options,
123
+ status,
124
+ surfaces,
125
+ theme
126
+ }) {
127
+ assertUniqueIds(surfaces, "ModeShowcase surface");
128
+ assertUniqueIds(modes, "ModeShowcase mode");
129
+ if (options !== undefined)
130
+ assertUniqueIds(options, "ModeShowcase option");
131
+ const captionText = optionalText(caption, "ModeShowcase");
132
+ if (!Number.isFinite(height) || !(height > 0))
133
+ throw new RangeError("ModeShowcase height must be finite and positive.");
134
+ if (fit !== "natural" && fit !== "fill")
135
+ throw new RangeError("ModeShowcase fit must be natural or fill.");
136
+ if (fit === "fill" && surfaces.length * modes.length * (options?.length ?? 1) > 128)
137
+ throw new RangeError("ModeShowcase fill supports up to 128 surface, mode, and option combinations.");
138
+ const id = useId();
139
+ const firstSurface = surfaces.find((surface2) => surface2.id === initial?.surface) ?? itemAt(surfaces, 0, "ModeShowcase surfaces");
140
+ const firstMode = modes.find((mode2) => mode2.id === initial?.mode) ?? itemAt(modes, 0, "ModeShowcase modes");
141
+ const firstOption = options === undefined ? undefined : options.find((option2) => option2.id === initial?.option) ?? itemAt(options, 0, "ModeShowcase options");
142
+ const [surfaceId, setSurfaceId] = useState(firstSurface.id);
143
+ const [mode, setMode] = useState(firstMode.id);
144
+ const [previousMode, setPreviousMode] = useState(firstMode.id);
145
+ const [option, setOption] = useState(firstOption?.id);
146
+ const [animated, setAnimated] = useState(false);
147
+ const tabs = useRef(new Map);
148
+ const surface = surfaces.find((entry) => entry.id === surfaceId) ?? itemAt(surfaces, 0, "ModeShowcase surfaces");
149
+ const modeChoice = modes.find((entry) => entry.id === mode) ?? itemAt(modes, 0, "ModeShowcase modes");
150
+ const optionChoice = options?.find((entry) => entry.id === option);
151
+ const optionInactive = optionInactiveModes.includes(mode);
152
+ const measurements = useMemo(() => fit !== "fill" ? null : surfaces.flatMap((entry) => modes.flatMap((choice) => (options ?? [{
153
+ id: undefined
154
+ }]).map((optionChoice2) => /* @__PURE__ */ createElement("div", {
155
+ "aria-hidden": "true",
156
+ className: "hkm-mode-surface",
157
+ "data-hkm-measurement": "",
158
+ hidden: true,
159
+ ...inertAttributes(true),
160
+ key: JSON.stringify([entry.id, choice.id, optionChoice2.id])
161
+ }, /* @__PURE__ */ jsx("div", {
162
+ className: "hkm-fit",
163
+ children: /* @__PURE__ */ jsx("div", {
164
+ className: "hkm-fit-inner",
165
+ children: entry.render({
166
+ animated: false,
167
+ mode: choice.id,
168
+ option: optionChoice2.id,
169
+ previousMode: choice.id,
170
+ theme
171
+ })
172
+ })
173
+ }))))), [fit, modes, options, surfaces, theme]);
174
+ const stage = useFittedShowcaseStage(fit, measurements, JSON.stringify([mode, option]), height);
175
+ const chooseMode = (next) => {
176
+ if (next === mode)
177
+ return;
178
+ setPreviousMode(mode);
179
+ setMode(next);
180
+ setAnimated(true);
181
+ };
182
+ const chooseOption = (next) => {
183
+ if (next === option)
184
+ return;
185
+ setOption(next);
186
+ setAnimated(true);
187
+ };
188
+ const onTabKey = (event) => {
189
+ const index = surfaces.findIndex((entry) => entry.id === surface.id);
190
+ const next = nextTabIndex(event.key, index, surfaces.length);
191
+ if (next === undefined)
192
+ return;
193
+ event.preventDefault();
194
+ const target = itemAt(surfaces, next, "ModeShowcase surfaces").id;
195
+ setSurfaceId(target);
196
+ tabs.current.get(target)?.focus();
197
+ };
198
+ const statusNode = status?.({
199
+ mode,
200
+ option,
201
+ surface: surface.id
202
+ });
203
+ const describeChoice = (surfaceHint, modeHint, optionHint) => [surfaceHint, modeHint, optionHint].map((text) => optionalText(text, "ModeShowcase hint")).filter((text) => text !== undefined).join(" ");
204
+ const hint = describeChoice(surface.hint, modeChoice.hint, optionInactive ? undefined : optionChoice?.hint);
205
+ const hints = [...new Set(surfaces.flatMap((entry) => modes.flatMap((choice) => (optionInactiveModes.includes(choice.id) ? [undefined] : options ?? [undefined]).map((optionEntry) => describeChoice(entry.hint, choice.hint, optionEntry?.hint)))))].filter((text) => text !== "");
206
+ const hasStatus = statusNode !== undefined && statusNode !== null && statusNode !== false && statusNode !== "";
207
+ return /* @__PURE__ */ jsxs("figure", {
208
+ "aria-label": label?.(surface) ?? `Illustration of ${surface.label.toLowerCase()}`,
209
+ className: joinMockupClasses("hkm-showcase", "hkm-modes", className),
210
+ "data-hkm-fit": fit === "fill" ? fit : undefined,
211
+ "data-hkm-theme": theme,
212
+ "data-nosnippet": "",
213
+ children: [
214
+ surfaces.length > 1 ? /* @__PURE__ */ jsx("div", {
215
+ "aria-label": "Surface",
216
+ className: "hkm-tabs hkm-folder-tabs",
217
+ role: "tablist",
218
+ children: surfaces.map((entry) => /* @__PURE__ */ jsx("button", {
219
+ "aria-controls": `${id}-panel`,
220
+ "aria-selected": entry.id === surface.id,
221
+ className: "hkm-tab",
222
+ id: `${id}-tab-${entry.id}`,
223
+ onClick: () => setSurfaceId(entry.id),
224
+ onKeyDown: onTabKey,
225
+ ref: (element) => {
226
+ if (element === null)
227
+ tabs.current.delete(entry.id);
228
+ else
229
+ tabs.current.set(entry.id, element);
230
+ },
231
+ role: "tab",
232
+ tabIndex: entry.id === surface.id ? 0 : -1,
233
+ type: "button",
234
+ children: entry.label
235
+ }, entry.id))
236
+ }) : null,
237
+ /* @__PURE__ */ jsxs("div", {
238
+ "aria-labelledby": surfaces.length > 1 ? `${id}-tab-${surface.id}` : undefined,
239
+ className: "hkm-showcase-stage hkm-mode-panel",
240
+ id: `${id}-panel`,
241
+ role: surfaces.length > 1 ? "tabpanel" : undefined,
242
+ style: {
243
+ "--hkm-page-height": `${height}px`
244
+ },
245
+ children: [
246
+ /* @__PURE__ */ jsxs("div", {
247
+ className: "hkm-showcase-controls",
248
+ children: [
249
+ /* @__PURE__ */ jsxs("div", {
250
+ className: "hkm-showcase-settings",
251
+ children: [
252
+ /* @__PURE__ */ jsxs("div", {
253
+ className: "hkm-showcase-row",
254
+ children: [
255
+ /* @__PURE__ */ jsx("span", {
256
+ className: "hkm-showcase-label",
257
+ id: `${id}-mode`,
258
+ children: modeLabel
259
+ }),
260
+ /* @__PURE__ */ jsx("div", {
261
+ "aria-labelledby": `${id}-mode`,
262
+ className: "hkm-segmented",
263
+ role: "group",
264
+ children: modes.map((entry) => /* @__PURE__ */ jsx("button", {
265
+ "aria-pressed": entry.id === mode,
266
+ onClick: () => chooseMode(entry.id),
267
+ type: "button",
268
+ children: entry.label
269
+ }, entry.id))
270
+ })
271
+ ]
272
+ }),
273
+ options === undefined ? null : /* @__PURE__ */ jsxs("div", {
274
+ className: "hkm-showcase-row",
275
+ children: [
276
+ /* @__PURE__ */ jsx("span", {
277
+ className: "hkm-showcase-label",
278
+ id: `${id}-option`,
279
+ children: optionLabel
280
+ }),
281
+ /* @__PURE__ */ jsx("div", {
282
+ "aria-labelledby": `${id}-option`,
283
+ className: "hkm-segmented",
284
+ role: "group",
285
+ children: options.map((entry) => /* @__PURE__ */ jsx("button", {
286
+ "aria-pressed": entry.id === option,
287
+ disabled: optionInactive,
288
+ onClick: () => chooseOption(entry.id),
289
+ type: "button",
290
+ children: entry.label
291
+ }, entry.id))
292
+ })
293
+ ]
294
+ })
295
+ ]
296
+ }),
297
+ hints.length === 0 ? null : /* @__PURE__ */ jsxs("div", {
298
+ className: "hkm-showcase-hints",
299
+ children: [
300
+ /* @__PURE__ */ jsx("p", {
301
+ className: "hkm-showcase-hint",
302
+ children: hint
303
+ }),
304
+ hints.filter((text) => text !== hint).map((text) => /* @__PURE__ */ createElement("p", {
305
+ "aria-hidden": "true",
306
+ className: "hkm-showcase-hint",
307
+ "data-hkm-reserving": "",
308
+ ...inertAttributes(true),
309
+ key: text
310
+ }, text))
311
+ ]
312
+ })
313
+ ]
314
+ }),
315
+ /* @__PURE__ */ jsxs("div", {
316
+ className: "hkm-mode-stage",
317
+ ref: stage,
318
+ children: [
319
+ measurements,
320
+ surfaces.map((entry) => /* @__PURE__ */ createElement("div", {
321
+ "aria-hidden": entry.id !== surface.id,
322
+ className: "hkm-mode-surface",
323
+ "data-hkm-animated": entry.id === surface.id && animated ? "" : undefined,
324
+ "data-hkm-from": entry.id === surface.id && animated ? previousMode : undefined,
325
+ ...inertAttributes(entry.id !== surface.id),
326
+ key: entry.id
327
+ }, /* @__PURE__ */ jsx(FitToWidth, {
328
+ minWidth: fit === "fill" ? 1 : minWidth,
329
+ children: entry.render({
330
+ animated: entry.id === surface.id && animated,
331
+ mode,
332
+ option,
333
+ previousMode,
334
+ theme
335
+ })
336
+ })))
337
+ ]
338
+ })
339
+ ]
340
+ }),
341
+ !hasStatus && captionText === undefined ? null : /* @__PURE__ */ jsxs("figcaption", {
342
+ className: "hkm-showcase-caption",
343
+ children: [
344
+ hasStatus ? /* @__PURE__ */ jsx("span", {
345
+ "aria-live": "polite",
346
+ className: "hkm-showcase-status",
347
+ children: statusNode
348
+ }) : null,
349
+ captionText === undefined ? null : /* @__PURE__ */ jsx("span", {
350
+ children: captionText
351
+ })
352
+ ]
353
+ })
354
+ ]
355
+ });
356
+ }
357
+ function fillFrames(stage) {
358
+ const frames = [];
359
+ for (const panel of stage.querySelectorAll(":scope > .hkm-step-panel, :scope > .hkm-mode-surface")) {
360
+ const fit = panel.querySelector(":scope > .hkm-fit");
361
+ if (fit === null)
362
+ continue;
363
+ for (const root of fit.querySelectorAll(".hkm-root")) {
364
+ const enclosingRoot = root.parentElement?.closest(".hkm-root");
365
+ if (root.closest(".hkm-fit") === fit && (enclosingRoot === null || enclosingRoot === undefined || !fit.contains(enclosingRoot)))
366
+ frames.push(root);
367
+ }
368
+ }
369
+ return frames;
370
+ }
371
+ function presentationBodies(stage) {
372
+ return fillFrames(stage).flatMap((frame) => [...frame.querySelectorAll(':scope > .hkm-window > [data-hkm-density="presentation"]')]);
373
+ }
374
+ function measureNestedFits(stage) {
375
+ const fits = [...stage.querySelectorAll("[data-hkm-min-width]")].filter((fit) => fit.parentElement?.classList.contains("hkm-step-panel") !== true && fit.parentElement?.classList.contains("hkm-mode-surface") !== true);
376
+ for (const fit of fits) {
377
+ const inner = fit.querySelector(":scope > .hkm-fit-inner");
378
+ if (inner === null)
379
+ continue;
380
+ fit.style.removeProperty("height");
381
+ inner.style.removeProperty("transform");
382
+ inner.style.width = `${Math.max(fit.clientWidth, Number(fit.dataset.hkmMinWidth))}px`;
383
+ }
384
+ for (const fit of fits.reverse()) {
385
+ const inner = fit.querySelector(":scope > .hkm-fit-inner");
386
+ if (inner === null || inner.offsetWidth <= 0)
387
+ continue;
388
+ const scale = Math.min(1, fit.clientWidth / inner.offsetWidth);
389
+ fit.style.height = `${inner.offsetHeight * scale}px`;
390
+ inner.style.transform = `scale(${scale})`;
391
+ }
392
+ }
393
+ function fitShowcaseStage(stage, minimumHeight) {
394
+ const owner = stage.parentElement;
395
+ if (owner === null || stage.clientWidth <= 0)
396
+ return;
397
+ for (const frame of fillFrames(stage))
398
+ frame.setAttribute("data-hkm-fill-frame", "");
399
+ const probe = stage.cloneNode(true);
400
+ probe.setAttribute("aria-hidden", "true");
401
+ probe.setAttribute("inert", "");
402
+ probe.setAttribute("data-hkm-measuring", "");
403
+ probe.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;inset:0 auto auto 0;inline-size:${stage.getBoundingClientRect().width}px;block-size:auto;`;
404
+ for (const sentinel of probe.querySelectorAll("[data-hkm-font-sentinel]"))
405
+ sentinel.remove();
406
+ for (const node of probe.querySelectorAll("[id]"))
407
+ node.removeAttribute("id");
408
+ for (const node of probe.querySelectorAll("[data-hkm-animated]"))
409
+ node.removeAttribute("data-hkm-animated");
410
+ for (const body of presentationBodies(probe))
411
+ body.style.removeProperty("--hkm-terminal-presentation-size");
412
+ for (const fixture of probe.querySelectorAll("[data-hkm-measurement]"))
413
+ fixture.hidden = false;
414
+ for (const node of [probe, ...probe.querySelectorAll("*")]) {
415
+ node.style.setProperty("transition", "none", "important");
416
+ node.style.setProperty("animation", "none", "important");
417
+ }
418
+ owner.append(probe);
419
+ try {
420
+ measureNestedFits(probe);
421
+ const naturalHeight = Math.max(minimumHeight, Math.ceil(probe.getBoundingClientRect().height));
422
+ stage.style.setProperty("--hkm-showcase-fill-height", `${naturalHeight}px`);
423
+ probe.style.blockSize = `${naturalHeight}px`;
424
+ probe.removeAttribute("data-hkm-measuring");
425
+ const bodies = presentationBodies(stage).filter((body) => body.closest("[data-hkm-measurement]") === null);
426
+ const copies = presentationBodies(probe);
427
+ let sharedSize = 4;
428
+ for (const copy of copies) {
429
+ const lines = [...copy.querySelectorAll(".hkm-terminal-line")];
430
+ const wrappedRows = (line) => Math.ceil((line.getBoundingClientRect().height - 0.5) / Number.parseFloat(getComputedStyle(line).lineHeight));
431
+ const baselineRows = lines.map(wrappedRows);
432
+ let low = 1;
433
+ let high = 4;
434
+ for (let attempt = 0;attempt < 9; attempt += 1) {
435
+ const candidate = (low + high) / 2;
436
+ copy.style.setProperty("--hkm-terminal-presentation-size", `${candidate}rem`);
437
+ if (copy.scrollHeight <= copy.clientHeight + 1 && copy.scrollWidth <= copy.clientWidth + 1 && lines.every((line, index) => {
438
+ const baseline = baselineRows[index];
439
+ return baseline !== undefined && wrappedRows(line) <= baseline;
440
+ }))
441
+ low = candidate;
442
+ else
443
+ high = candidate;
444
+ }
445
+ sharedSize = Math.min(sharedSize, low);
446
+ }
447
+ for (const body of bodies)
448
+ body.style.setProperty("--hkm-terminal-presentation-size", `${Math.floor(sharedSize * 1000) / 1000}rem`);
449
+ stage.setAttribute("data-hkm-fitted", "");
450
+ } finally {
451
+ probe.remove();
452
+ }
453
+ }
454
+ function useFittedShowcaseStage(fit, source, variation = "", minimumHeight = 0) {
455
+ const stage = useRef(null);
456
+ useIsomorphicLayoutEffect(() => {
457
+ const node = stage.current;
458
+ if (fit !== "fill" || node === null || typeof ResizeObserver === "undefined")
459
+ return;
460
+ const fontSentinel = document.createElement("span");
461
+ fontSentinel.setAttribute("data-hkm-font-sentinel", "");
462
+ fontSentinel.setAttribute("aria-hidden", "true");
463
+ fontSentinel.inert = true;
464
+ fontSentinel.style.cssText = "position:absolute;inline-size:1rem;block-size:1rem;visibility:hidden;pointer-events:none;inset:0 auto auto 0;";
465
+ node.append(fontSentinel);
466
+ let previous = "";
467
+ let disposed = false;
468
+ const measure = () => {
469
+ if (disposed)
470
+ return;
471
+ const key = `${node.clientWidth}/${getComputedStyle(document.documentElement).fontSize}`;
472
+ if (key === previous)
473
+ return;
474
+ previous = key;
475
+ fitShowcaseStage(node, minimumHeight);
476
+ };
477
+ measure();
478
+ const observer = new ResizeObserver(measure);
479
+ observer.observe(node);
480
+ observer.observe(document.documentElement);
481
+ observer.observe(fontSentinel);
482
+ document.fonts.ready.then(() => {
483
+ previous = "";
484
+ measure();
485
+ });
486
+ return () => {
487
+ disposed = true;
488
+ observer.disconnect();
489
+ fontSentinel.remove();
490
+ node.style.removeProperty("--hkm-showcase-fill-height");
491
+ node.removeAttribute("data-hkm-fitted");
492
+ for (const body of presentationBodies(node))
493
+ body.style.removeProperty("--hkm-terminal-presentation-size");
494
+ for (const frame of fillFrames(node))
495
+ frame.removeAttribute("data-hkm-fill-frame");
496
+ };
497
+ }, [fit, source, variation, minimumHeight]);
498
+ return stage;
499
+ }
500
+ function StepThrough({
501
+ caption,
502
+ className,
503
+ initial,
504
+ fit = "natural",
505
+ label = "Steps",
506
+ minWidth = 400,
507
+ steps,
508
+ theme
509
+ }) {
510
+ assertUniqueIds(steps, "StepThrough step");
511
+ if (fit !== "natural" && fit !== "fill")
512
+ throw new RangeError("StepThrough fit must be natural or fill.");
513
+ const stage = useFittedShowcaseStage(fit, steps);
514
+ const captionText = optionalText(caption, "StepThrough");
515
+ const id = useId();
516
+ const [index, setIndex] = useState(() => Math.max(0, steps.findIndex((step) => step.id === initial)));
517
+ const [animated, setAnimated] = useState(false);
518
+ const [vertical, setVertical] = useState(false);
519
+ const layout = useRef(null);
520
+ const tablist = useRef(null);
521
+ const tabs = useRef(new Map);
522
+ const current = Math.min(index, steps.length - 1);
523
+ const hints = steps.map((entry) => optionalText(entry.hint, "StepThrough hint"));
524
+ const currentHint = hints[current];
525
+ useIsomorphicLayoutEffect(() => {
526
+ const node = layout.current;
527
+ if (node === null || typeof ResizeObserver === "undefined")
528
+ return;
529
+ const measure = () => setVertical(node.clientWidth >= 720);
530
+ measure();
531
+ const observer = new ResizeObserver(measure);
532
+ observer.observe(node);
533
+ return () => observer.disconnect();
534
+ }, []);
535
+ useIsomorphicLayoutEffect(() => {
536
+ const list = tablist.current;
537
+ if (list === null || typeof ResizeObserver === "undefined")
538
+ return;
539
+ let disposed = false;
540
+ const measure = () => {
541
+ if (disposed)
542
+ return;
543
+ list.removeAttribute("data-hkm-compact");
544
+ if (vertical)
545
+ return;
546
+ const truncated = [...list.querySelectorAll(".hkm-step-label")].some((label2) => label2.scrollWidth > label2.clientWidth + 1);
547
+ if (truncated)
548
+ list.setAttribute("data-hkm-compact", "");
549
+ };
550
+ measure();
551
+ const observer = new ResizeObserver(measure);
552
+ observer.observe(list);
553
+ document.fonts.ready.then(measure);
554
+ return () => {
555
+ disposed = true;
556
+ observer.disconnect();
557
+ list.removeAttribute("data-hkm-compact");
558
+ };
559
+ }, [current, vertical]);
560
+ useIsomorphicLayoutEffect(() => {
561
+ const list = tablist.current;
562
+ const selected = tabs.current.get(current);
563
+ if (vertical || list === null || selected === undefined)
564
+ return;
565
+ const bounds = list.getBoundingClientRect();
566
+ const active = selected.getBoundingClientRect();
567
+ if (active.left < bounds.left)
568
+ list.scrollLeft += active.left - bounds.left;
569
+ else if (active.right > bounds.right)
570
+ list.scrollLeft += active.right - bounds.right;
571
+ }, [current, vertical]);
572
+ const go = (next, focus = false) => {
573
+ if (next < 0 || next >= steps.length || next === current)
574
+ return;
575
+ setIndex(next);
576
+ setAnimated(true);
577
+ if (focus)
578
+ tabs.current.get(next)?.focus({
579
+ preventScroll: true
580
+ });
581
+ };
582
+ const onTabKey = (event) => {
583
+ if (vertical && (event.key === "ArrowLeft" || event.key === "ArrowRight"))
584
+ return;
585
+ if (!vertical && (event.key === "ArrowUp" || event.key === "ArrowDown"))
586
+ return;
587
+ const next = nextTabIndex(event.key, current, steps.length);
588
+ if (next === undefined)
589
+ return;
590
+ event.preventDefault();
591
+ go(next, true);
592
+ };
593
+ return /* @__PURE__ */ jsxs("figure", {
594
+ "aria-label": `Illustration: ${label.toLowerCase()}`,
595
+ className: joinMockupClasses("hkm-showcase", "hkm-steps", className),
596
+ "data-hkm-fit": fit === "fill" ? fit : undefined,
597
+ "data-hkm-theme": theme,
598
+ "data-nosnippet": "",
599
+ children: [
600
+ /* @__PURE__ */ jsxs("div", {
601
+ className: "hkm-step-layout",
602
+ ref: layout,
603
+ children: [
604
+ /* @__PURE__ */ jsx("div", {
605
+ className: "hkm-showcase-controls hkm-step-controls",
606
+ children: /* @__PURE__ */ jsxs("div", {
607
+ className: "hkm-step-nav",
608
+ children: [
609
+ /* @__PURE__ */ jsx("button", {
610
+ "aria-label": "Back",
611
+ className: "hkm-step-button",
612
+ disabled: current === 0,
613
+ onClick: () => go(current - 1),
614
+ type: "button",
615
+ children: /* @__PURE__ */ jsx("svg", {
616
+ "aria-hidden": "true",
617
+ focusable: "false",
618
+ height: "20",
619
+ viewBox: "0 0 24 24",
620
+ width: "20",
621
+ children: /* @__PURE__ */ jsx("path", {
622
+ d: "m14.5 5-7 7 7 7",
623
+ fill: "none",
624
+ stroke: "currentColor",
625
+ strokeLinecap: "round",
626
+ strokeLinejoin: "round",
627
+ strokeWidth: "1.75"
628
+ })
629
+ })
630
+ }),
631
+ /* @__PURE__ */ jsxs("span", {
632
+ "aria-hidden": "true",
633
+ className: "hkm-step-count",
634
+ children: [
635
+ current + 1,
636
+ " / ",
637
+ steps.length
638
+ ]
639
+ }),
640
+ /* @__PURE__ */ jsx("div", {
641
+ "aria-label": label,
642
+ "aria-orientation": vertical ? "vertical" : "horizontal",
643
+ className: "hkm-tabs hkm-step-tabs",
644
+ ref: tablist,
645
+ role: "tablist",
646
+ children: steps.map((entry, position) => /* @__PURE__ */ jsxs("button", {
647
+ "aria-controls": `${id}-panel-${entry.id}`,
648
+ "aria-describedby": hints[position] === undefined ? undefined : `${id}-hint-${entry.id}`,
649
+ "aria-label": entry.label,
650
+ "aria-selected": position === current,
651
+ className: "hkm-tab",
652
+ "data-hkm-done": position < current ? "" : undefined,
653
+ id: `${id}-tab-${entry.id}`,
654
+ onClick: () => go(position),
655
+ onKeyDown: onTabKey,
656
+ ref: (element) => {
657
+ if (element === null)
658
+ tabs.current.delete(position);
659
+ else
660
+ tabs.current.set(position, element);
661
+ },
662
+ role: "tab",
663
+ tabIndex: position === current ? 0 : -1,
664
+ type: "button",
665
+ children: [
666
+ /* @__PURE__ */ jsx("span", {
667
+ "aria-hidden": "true",
668
+ className: "hkm-step-number",
669
+ children: position + 1
670
+ }),
671
+ /* @__PURE__ */ jsxs("span", {
672
+ className: "hkm-step-copy",
673
+ children: [
674
+ /* @__PURE__ */ jsx("span", {
675
+ className: "hkm-step-label",
676
+ children: entry.label
677
+ }),
678
+ hints[position] === undefined ? null : /* @__PURE__ */ jsx("span", {
679
+ className: "hkm-step-hint",
680
+ id: `${id}-hint-${entry.id}`,
681
+ children: hints[position]
682
+ })
683
+ ]
684
+ })
685
+ ]
686
+ }, entry.id))
687
+ }),
688
+ /* @__PURE__ */ jsx("button", {
689
+ "aria-label": "Next",
690
+ className: "hkm-step-button",
691
+ disabled: current === steps.length - 1,
692
+ onClick: () => go(current + 1),
693
+ type: "button",
694
+ children: /* @__PURE__ */ jsx("svg", {
695
+ "aria-hidden": "true",
696
+ focusable: "false",
697
+ height: "20",
698
+ viewBox: "0 0 24 24",
699
+ width: "20",
700
+ children: /* @__PURE__ */ jsx("path", {
701
+ d: "m9.5 5 7 7-7 7",
702
+ fill: "none",
703
+ stroke: "currentColor",
704
+ strokeLinecap: "round",
705
+ strokeLinejoin: "round",
706
+ strokeWidth: "1.75"
707
+ })
708
+ })
709
+ })
710
+ ]
711
+ })
712
+ }),
713
+ /* @__PURE__ */ jsx("div", {
714
+ className: "hkm-showcase-stage hkm-step-stage",
715
+ ref: stage,
716
+ children: steps.map((entry, position) => /* @__PURE__ */ createElement("div", {
717
+ "aria-hidden": position !== current,
718
+ "aria-labelledby": `${id}-tab-${entry.id}`,
719
+ className: "hkm-step-panel",
720
+ "data-hkm-animated": position === current && animated ? "" : undefined,
721
+ id: `${id}-panel-${entry.id}`,
722
+ ...inertAttributes(position !== current),
723
+ key: entry.id,
724
+ role: "tabpanel",
725
+ tabIndex: position === current ? 0 : -1
726
+ }, fit === "fill" ? /* @__PURE__ */ jsx("div", {
727
+ className: "hkm-fit",
728
+ children: /* @__PURE__ */ jsx("div", {
729
+ className: "hkm-fit-inner",
730
+ children: entry.render({
731
+ animated: position === current && animated,
732
+ theme
733
+ })
734
+ })
735
+ }) : /* @__PURE__ */ jsx(FitToWidth, {
736
+ minWidth,
737
+ children: entry.render({
738
+ animated: position === current && animated,
739
+ theme
740
+ })
741
+ })))
742
+ }),
743
+ hints.some((hint) => hint !== undefined) ? /* @__PURE__ */ jsx("div", {
744
+ "aria-hidden": "true",
745
+ className: "hkm-step-descriptions",
746
+ children: steps.map((entry, position) => /* @__PURE__ */ jsx("p", {
747
+ className: "hkm-step-description",
748
+ "data-hkm-active": position === current ? "" : undefined,
749
+ children: hints[position]
750
+ }, entry.id))
751
+ }) : null
752
+ ]
753
+ }),
754
+ /* @__PURE__ */ jsxs("span", {
755
+ "aria-live": "polite",
756
+ className: "hkm-showcase-status hkm-step-announcement",
757
+ children: [
758
+ "Step ",
759
+ current + 1,
760
+ " of ",
761
+ steps.length,
762
+ currentHint === undefined ? "" : `. ${currentHint}`
763
+ ]
764
+ }),
765
+ captionText === undefined ? null : /* @__PURE__ */ jsx("figcaption", {
766
+ className: "hkm-showcase-caption",
767
+ children: /* @__PURE__ */ jsx("span", {
768
+ children: captionText
769
+ })
770
+ })
771
+ ]
772
+ });
773
+ }
774
+
775
+ export { FitToWidth, ModeShowcase, StepThrough };