@hraness/design-kit 0.35.3 → 0.36.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 (34) hide show
  1. package/MARKETING_COPY.md +1 -0
  2. package/README.md +53 -7
  3. package/dist/{chunk-vst6p4wd.js → chunk-sabcr66p.js} +68 -0
  4. package/dist/{chunk-fmeg5cxf.js → chunk-thv5t11w.js} +1 -1
  5. package/dist/{chunk-4tgt8egt.js → chunk-v8byd3hw.js} +106 -23
  6. package/dist/index.js +2 -2
  7. package/dist/mockups/client.js +169 -21
  8. package/dist/mockups/index.js +4 -0
  9. package/dist/provider-marks.js +1 -1
  10. package/dist/react/index.js +58 -3
  11. package/dist/react/server.js +2 -2
  12. package/dist/stylex-manifest.json +1 -1
  13. package/dist/stylex.css +30 -0
  14. package/package.json +1 -1
  15. package/portfolio-inventory.json +1 -1
  16. package/src/mockups/client.tsx +120 -7
  17. package/src/mockups/frames.tsx +4 -2
  18. package/src/mockups.css +43 -0
  19. package/src/product-marketing-foundation.css +12 -0
  20. package/src/product-marketing.css +59 -0
  21. package/src/provider-marks.generated.ts +20 -0
  22. package/src/provider-marks.ts +5 -0
  23. package/src/react/design-gallery.tsx +11 -0
  24. package/src/react/product-marketing.stylex.ts +50 -2
  25. package/src/react/product-marketing.tsx +38 -28
  26. package/vendor/provider-marks/BEEPER-NOTICE.md +16 -0
  27. package/vendor/provider-marks/SIMPLE-ICONS-LICENSE.md +30 -0
  28. package/vendor/provider-marks/UPSTREAM.md +24 -0
  29. package/vendor/provider-marks/beeper-glyph.svg +3 -0
  30. package/vendor/provider-marks/beeper.svg +16 -0
  31. package/vendor/provider-marks/lobehub/ollama.svg +1 -0
  32. package/vendor/provider-marks/lobehub/vercel.svg +1 -0
  33. package/vendor/provider-marks/simple-icons/imessage.svg +1 -0
  34. package/vendor/provider-marks/simple-icons/whatsapp.svg +1 -0
@@ -5,7 +5,7 @@ import {
5
5
  import"../chunk-5gtx3pza.js";
6
6
 
7
7
  // src/mockups/client.tsx
8
- import { useId, useLayoutEffect, useRef, useState } from "react";
8
+ import { useId, useLayoutEffect, useMemo, useRef, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  var useIsomorphicLayoutEffect = typeof document === "undefined" ? () => {
11
11
  return;
@@ -103,6 +103,7 @@ function FitToWidth({
103
103
  function ModeShowcase({
104
104
  caption,
105
105
  className,
106
+ fit = "natural",
106
107
  height = 440,
107
108
  initial,
108
109
  label,
@@ -121,8 +122,12 @@ function ModeShowcase({
121
122
  if (options !== undefined)
122
123
  assertUniqueIds(options, "ModeShowcase option");
123
124
  const captionText = optionalText(caption, "ModeShowcase");
124
- if (!(height > 0))
125
- throw new RangeError("ModeShowcase height must be positive.");
125
+ if (!Number.isFinite(height) || !(height > 0))
126
+ throw new RangeError("ModeShowcase height must be finite and positive.");
127
+ if (fit !== "natural" && fit !== "fill")
128
+ throw new RangeError("ModeShowcase fit must be natural or fill.");
129
+ if (fit === "fill" && surfaces.length * modes.length * (options?.length ?? 1) > 128)
130
+ throw new RangeError("ModeShowcase fill supports up to 128 surface, mode, and option combinations.");
126
131
  const id = useId();
127
132
  const firstSurface = surfaces.find((surface2) => surface2.id === initial?.surface) ?? itemAt(surfaces, 0, "ModeShowcase surfaces");
128
133
  const firstMode = modes.find((mode2) => mode2.id === initial?.mode) ?? itemAt(modes, 0, "ModeShowcase modes");
@@ -137,6 +142,29 @@ function ModeShowcase({
137
142
  const modeChoice = modes.find((entry) => entry.id === mode) ?? itemAt(modes, 0, "ModeShowcase modes");
138
143
  const optionChoice = options?.find((entry) => entry.id === option);
139
144
  const optionInactive = optionInactiveModes.includes(mode);
145
+ const measurements = useMemo(() => fit !== "fill" ? null : surfaces.flatMap((entry) => modes.flatMap((choice) => (options ?? [{
146
+ id: undefined
147
+ }]).map((optionChoice2) => /* @__PURE__ */ jsx("div", {
148
+ "aria-hidden": "true",
149
+ className: "hkm-mode-surface",
150
+ "data-hkm-measurement": "",
151
+ hidden: true,
152
+ inert: true,
153
+ children: /* @__PURE__ */ jsx("div", {
154
+ className: "hkm-fit",
155
+ children: /* @__PURE__ */ jsx("div", {
156
+ className: "hkm-fit-inner",
157
+ children: entry.render({
158
+ animated: false,
159
+ mode: choice.id,
160
+ option: optionChoice2.id,
161
+ previousMode: choice.id,
162
+ theme
163
+ })
164
+ })
165
+ })
166
+ }, JSON.stringify([entry.id, choice.id, optionChoice2.id]))))), [fit, modes, options, surfaces, theme]);
167
+ const stage = useFittedShowcaseStage(fit, measurements, JSON.stringify([mode, option]), height);
140
168
  const chooseMode = (next) => {
141
169
  if (next === mode)
142
170
  return;
@@ -170,6 +198,7 @@ function ModeShowcase({
170
198
  return /* @__PURE__ */ jsxs("figure", {
171
199
  "aria-label": label?.(surface) ?? `Illustration of ${surface.label.toLowerCase()}`,
172
200
  className: joinMockupClasses("hkm-showcase", "hkm-modes", className),
201
+ "data-hkm-fit": fit === "fill" ? fit : undefined,
173
202
  "data-hkm-theme": theme,
174
203
  "data-nosnippet": "",
175
204
  children: [
@@ -262,25 +291,29 @@ function ModeShowcase({
262
291
  })
263
292
  ]
264
293
  }),
265
- /* @__PURE__ */ jsx("div", {
294
+ /* @__PURE__ */ jsxs("div", {
266
295
  className: "hkm-mode-stage",
267
- children: surfaces.map((entry) => /* @__PURE__ */ jsx("div", {
268
- "aria-hidden": entry.id !== surface.id,
269
- className: "hkm-mode-surface",
270
- "data-hkm-animated": entry.id === surface.id && animated ? "" : undefined,
271
- "data-hkm-from": entry.id === surface.id && animated ? previousMode : undefined,
272
- inert: entry.id !== surface.id,
273
- children: /* @__PURE__ */ jsx(FitToWidth, {
274
- minWidth,
275
- children: entry.render({
276
- animated: entry.id === surface.id && animated,
277
- mode,
278
- option,
279
- previousMode,
280
- theme
296
+ ref: stage,
297
+ children: [
298
+ measurements,
299
+ surfaces.map((entry) => /* @__PURE__ */ jsx("div", {
300
+ "aria-hidden": entry.id !== surface.id,
301
+ className: "hkm-mode-surface",
302
+ "data-hkm-animated": entry.id === surface.id && animated ? "" : undefined,
303
+ "data-hkm-from": entry.id === surface.id && animated ? previousMode : undefined,
304
+ inert: entry.id !== surface.id,
305
+ children: /* @__PURE__ */ jsx(FitToWidth, {
306
+ minWidth: fit === "fill" ? 1 : minWidth,
307
+ children: entry.render({
308
+ animated: entry.id === surface.id && animated,
309
+ mode,
310
+ option,
311
+ previousMode,
312
+ theme
313
+ })
281
314
  })
282
- })
283
- }, entry.id))
315
+ }, entry.id))
316
+ ]
284
317
  })
285
318
  ]
286
319
  }),
@@ -300,16 +333,120 @@ function ModeShowcase({
300
333
  ]
301
334
  });
302
335
  }
336
+ function fitShowcaseStage(stage, minimumHeight) {
337
+ const owner = stage.parentElement;
338
+ if (owner === null || stage.clientWidth <= 0)
339
+ return;
340
+ const probe = stage.cloneNode(true);
341
+ probe.setAttribute("aria-hidden", "true");
342
+ probe.setAttribute("inert", "");
343
+ probe.setAttribute("data-hkm-measuring", "");
344
+ probe.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;inset:0 auto auto 0;inline-size:${stage.getBoundingClientRect().width}px;block-size:auto;`;
345
+ for (const sentinel of probe.querySelectorAll("[data-hkm-font-sentinel]"))
346
+ sentinel.remove();
347
+ for (const node of probe.querySelectorAll("[id]"))
348
+ node.removeAttribute("id");
349
+ for (const node of probe.querySelectorAll("[data-hkm-animated]"))
350
+ node.removeAttribute("data-hkm-animated");
351
+ for (const body of probe.querySelectorAll('[data-hkm-density="presentation"]'))
352
+ body.style.removeProperty("--hkm-terminal-presentation-size");
353
+ for (const fixture of probe.querySelectorAll("[data-hkm-measurement]"))
354
+ fixture.hidden = false;
355
+ owner.append(probe);
356
+ try {
357
+ const naturalHeight = Math.max(minimumHeight, Math.ceil(probe.getBoundingClientRect().height));
358
+ stage.style.setProperty("--hkm-showcase-fill-height", `${naturalHeight}px`);
359
+ for (const fixture of probe.querySelectorAll("[data-hkm-measurement]"))
360
+ fixture.remove();
361
+ probe.style.blockSize = `${naturalHeight}px`;
362
+ probe.removeAttribute("data-hkm-measuring");
363
+ const bodies = [...stage.querySelectorAll('[data-hkm-density="presentation"]')].filter((body) => body.closest("[data-hkm-measurement]") === null);
364
+ const copies = [...probe.querySelectorAll('[data-hkm-density="presentation"]')];
365
+ for (const [index, copy] of copies.entries()) {
366
+ const body = bodies[index];
367
+ if (body === undefined)
368
+ continue;
369
+ const lines = [...copy.querySelectorAll(".hkm-terminal-line")];
370
+ const wrappedRows = (line) => Math.ceil((line.getBoundingClientRect().height - 0.5) / Number.parseFloat(getComputedStyle(line).lineHeight));
371
+ const baselineRows = lines.map(wrappedRows);
372
+ let low = 1;
373
+ let high = 4;
374
+ for (let attempt = 0;attempt < 9; attempt += 1) {
375
+ const candidate = (low + high) / 2;
376
+ copy.style.setProperty("--hkm-terminal-presentation-size", `${candidate}rem`);
377
+ if (copy.scrollHeight <= copy.clientHeight + 1 && copy.scrollWidth <= copy.clientWidth + 1 && lines.every((line, index2) => {
378
+ const baseline = baselineRows[index2];
379
+ return baseline !== undefined && wrappedRows(line) <= baseline;
380
+ }))
381
+ low = candidate;
382
+ else
383
+ high = candidate;
384
+ }
385
+ body.style.setProperty("--hkm-terminal-presentation-size", `${Math.floor(low * 1000) / 1000}rem`);
386
+ }
387
+ stage.setAttribute("data-hkm-fitted", "");
388
+ } finally {
389
+ probe.remove();
390
+ }
391
+ }
392
+ function useFittedShowcaseStage(fit, source, variation = "", minimumHeight = 0) {
393
+ const stage = useRef(null);
394
+ useIsomorphicLayoutEffect(() => {
395
+ const node = stage.current;
396
+ if (fit !== "fill" || node === null || typeof ResizeObserver === "undefined")
397
+ return;
398
+ const fontSentinel = document.createElement("span");
399
+ fontSentinel.setAttribute("data-hkm-font-sentinel", "");
400
+ fontSentinel.setAttribute("aria-hidden", "true");
401
+ fontSentinel.inert = true;
402
+ fontSentinel.style.cssText = "position:absolute;inline-size:1rem;block-size:1rem;visibility:hidden;pointer-events:none;inset:0 auto auto 0;";
403
+ node.append(fontSentinel);
404
+ let previous = "";
405
+ let disposed = false;
406
+ const measure = () => {
407
+ if (disposed)
408
+ return;
409
+ const key = `${node.clientWidth}/${getComputedStyle(document.documentElement).fontSize}`;
410
+ if (key === previous)
411
+ return;
412
+ previous = key;
413
+ fitShowcaseStage(node, minimumHeight);
414
+ };
415
+ measure();
416
+ const observer = new ResizeObserver(measure);
417
+ observer.observe(node);
418
+ observer.observe(document.documentElement);
419
+ observer.observe(fontSentinel);
420
+ document.fonts.ready.then(() => {
421
+ previous = "";
422
+ measure();
423
+ });
424
+ return () => {
425
+ disposed = true;
426
+ observer.disconnect();
427
+ fontSentinel.remove();
428
+ node.style.removeProperty("--hkm-showcase-fill-height");
429
+ node.removeAttribute("data-hkm-fitted");
430
+ for (const body of node.querySelectorAll('[data-hkm-density="presentation"]'))
431
+ body.style.removeProperty("--hkm-terminal-presentation-size");
432
+ };
433
+ }, [fit, source, variation, minimumHeight]);
434
+ return stage;
435
+ }
303
436
  function StepThrough({
304
437
  caption,
305
438
  className,
306
439
  initial,
440
+ fit = "natural",
307
441
  label = "Steps",
308
442
  minWidth = 400,
309
443
  steps,
310
444
  theme
311
445
  }) {
312
446
  assertUniqueIds(steps, "StepThrough step");
447
+ if (fit !== "natural" && fit !== "fill")
448
+ throw new RangeError("StepThrough fit must be natural or fill.");
449
+ const stage = useFittedShowcaseStage(fit, steps);
313
450
  const captionText = optionalText(caption, "StepThrough");
314
451
  const id = useId();
315
452
  const [index, setIndex] = useState(() => Math.max(0, steps.findIndex((step2) => step2.id === initial)));
@@ -335,6 +472,7 @@ function StepThrough({
335
472
  return /* @__PURE__ */ jsxs("figure", {
336
473
  "aria-label": `Illustration: ${label.toLowerCase()}`,
337
474
  className: joinMockupClasses("hkm-showcase", "hkm-steps", className),
475
+ "data-hkm-fit": fit === "fill" ? fit : undefined,
338
476
  "data-hkm-theme": theme,
339
477
  "data-nosnippet": "",
340
478
  children: [
@@ -374,6 +512,7 @@ function StepThrough({
374
512
  }),
375
513
  /* @__PURE__ */ jsx("div", {
376
514
  className: "hkm-showcase-stage hkm-step-stage",
515
+ ref: stage,
377
516
  children: steps.map((entry, position) => /* @__PURE__ */ jsx("div", {
378
517
  "aria-hidden": position !== current,
379
518
  "aria-labelledby": `${id}-tab-${entry.id}`,
@@ -382,7 +521,16 @@ function StepThrough({
382
521
  id: `${id}-panel-${entry.id}`,
383
522
  inert: position !== current,
384
523
  role: "tabpanel",
385
- children: /* @__PURE__ */ jsx(FitToWidth, {
524
+ children: fit === "fill" ? /* @__PURE__ */ jsx("div", {
525
+ className: "hkm-fit",
526
+ children: /* @__PURE__ */ jsx("div", {
527
+ className: "hkm-fit-inner",
528
+ children: entry.render({
529
+ animated: position === current && animated,
530
+ theme
531
+ })
532
+ })
533
+ }) : /* @__PURE__ */ jsx(FitToWidth, {
386
534
  minWidth,
387
535
  children: entry.render({
388
536
  animated: position === current && animated,
@@ -141,6 +141,7 @@ function BrowserWindow({
141
141
  }
142
142
  var TERMINAL_KINDS = new Set(["input", "output", "comment"]);
143
143
  function TerminalFrame({
144
+ density = "standard",
144
145
  fade,
145
146
  height,
146
147
  lines,
@@ -148,6 +149,8 @@ function TerminalFrame({
148
149
  title = "Terminal",
149
150
  ...root
150
151
  }) {
152
+ if (density !== "standard" && density !== "presentation")
153
+ throw new RangeError("Terminal density must be standard or presentation.");
151
154
  for (const line of lines) {
152
155
  if (!TERMINAL_KINDS.has(line.kind))
153
156
  throw new TypeError(`Unknown terminal line kind ${JSON.stringify(line.kind)}.`);
@@ -173,6 +176,7 @@ function TerminalFrame({
173
176
  }),
174
177
  /* @__PURE__ */ jsx("div", {
175
178
  className: "hkm-page hkm-terminal-body",
179
+ "data-hkm-density": density === "presentation" ? density : undefined,
176
180
  "data-hkm-fade": fades ? "" : undefined,
177
181
  style: pageStyle(height),
178
182
  children: /* @__PURE__ */ jsxs("div", {
@@ -6,7 +6,7 @@ import {
6
6
  providerMarkMonogram,
7
7
  providerMarkOnAccent,
8
8
  providerMarks
9
- } from "./chunk-vst6p4wd.js";
9
+ } from "./chunk-sabcr66p.js";
10
10
  import"./chunk-5gtx3pza.js";
11
11
  export {
12
12
  providerMarks,
@@ -89,7 +89,7 @@ import {
89
89
  marketingProofFrameAddress,
90
90
  proceduralBackdropVariants,
91
91
  proceduralRecipeVersion
92
- } from "../chunk-4tgt8egt.js";
92
+ } from "../chunk-v8byd3hw.js";
93
93
  import {
94
94
  SyntaxCode
95
95
  } from "../chunk-6ts4955n.js";
@@ -121,7 +121,7 @@ import {
121
121
  resolveStatusPage,
122
122
  statusPageRoutesAttribute,
123
123
  suggestStatusRoute
124
- } from "../chunk-fmeg5cxf.js";
124
+ } from "../chunk-thv5t11w.js";
125
125
  import"../chunk-gmea6p0d.js";
126
126
  import"../chunk-wzvdn8ey.js";
127
127
  import"../chunk-he8eznb1.js";
@@ -132,7 +132,7 @@ import {
132
132
  characterLength,
133
133
  xPostLength
134
134
  } from "../chunk-cejpzyfh.js";
135
- import"../chunk-vst6p4wd.js";
135
+ import"../chunk-sabcr66p.js";
136
136
  import {
137
137
  __require
138
138
  } from "../chunk-5gtx3pza.js";
@@ -6219,6 +6219,36 @@ function DesignSystemGallery({
6219
6219
  mark: "nvidia",
6220
6220
  label: "NVIDIA",
6221
6221
  size: 40
6222
+ }),
6223
+ /* @__PURE__ */ jsx17(ProviderMark, {
6224
+ mark: "imessage",
6225
+ label: "iMessage",
6226
+ size: 40,
6227
+ tone: "solid"
6228
+ }),
6229
+ /* @__PURE__ */ jsx17(ProviderMark, {
6230
+ mark: "whatsapp",
6231
+ label: "WhatsApp",
6232
+ size: 40,
6233
+ tone: "solid"
6234
+ }),
6235
+ /* @__PURE__ */ jsx17(ProviderMark, {
6236
+ mark: "beeper",
6237
+ label: "Beeper",
6238
+ size: 40,
6239
+ tone: "solid"
6240
+ }),
6241
+ /* @__PURE__ */ jsx17(ProviderMark, {
6242
+ mark: "ollama",
6243
+ label: "Ollama",
6244
+ size: 40,
6245
+ tone: "solid"
6246
+ }),
6247
+ /* @__PURE__ */ jsx17(ProviderMark, {
6248
+ mark: "vercel",
6249
+ label: "Vercel",
6250
+ size: 40,
6251
+ tone: "solid"
6222
6252
  })
6223
6253
  ]
6224
6254
  })
@@ -6398,6 +6428,8 @@ function DesignSystemGallery({
6398
6428
  /* @__PURE__ */ jsxs15(MarketingMain, {
6399
6429
  children: [
6400
6430
  /* @__PURE__ */ jsx17(ProductHero, {
6431
+ align: "start",
6432
+ layout: "split",
6401
6433
  actions: [{
6402
6434
  href: "#gallery-install",
6403
6435
  label: "Install Relay"
@@ -6677,6 +6709,29 @@ Run the job, then show me its log.`,
6677
6709
  meta: "First observed 26 August 2026. Early DeepSWE coverage on OpenRouter."
6678
6710
  }]
6679
6711
  }),
6712
+ /* @__PURE__ */ jsx17(MarketingCardRow, {
6713
+ ariaLabel: "Agent choices",
6714
+ columns: 2,
6715
+ cards: [{
6716
+ icon: /* @__PURE__ */ jsx17(ProviderMark, {
6717
+ mark: "claudecode",
6718
+ size: 56,
6719
+ tone: "solid"
6720
+ }),
6721
+ href: "#gallery-install",
6722
+ title: "Claude Code",
6723
+ meta: "Use Relay from your terminal agent."
6724
+ }, {
6725
+ icon: /* @__PURE__ */ jsx17(ProviderMark, {
6726
+ mark: "codex",
6727
+ size: 56,
6728
+ tone: "solid"
6729
+ }),
6730
+ href: "#gallery-install",
6731
+ title: "Codex",
6732
+ meta: "Keep the same commands across your projects."
6733
+ }]
6734
+ }),
6680
6735
  /* @__PURE__ */ jsx17(MarketingTrustBoundary, {
6681
6736
  columns: 2,
6682
6737
  heading: "What leaves your machine.",
@@ -68,7 +68,7 @@ import {
68
68
  marketingProofFrameAddress,
69
69
  proceduralBackdropVariants,
70
70
  proceduralRecipeVersion
71
- } from "../chunk-4tgt8egt.js";
71
+ } from "../chunk-v8byd3hw.js";
72
72
  import {
73
73
  SyntaxCode
74
74
  } from "../chunk-6ts4955n.js";
@@ -77,7 +77,7 @@ import"../chunk-wzvdn8ey.js";
77
77
  import"../chunk-he8eznb1.js";
78
78
  import"../chunk-77391vmq.js";
79
79
  import"../chunk-cejpzyfh.js";
80
- import"../chunk-vst6p4wd.js";
80
+ import"../chunk-sabcr66p.js";
81
81
  import"../chunk-5gtx3pza.js";
82
82
  export {
83
83
  proceduralRecipeVersion,