@krizaka/ui 3.36.0 → 3.38.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 (115) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/activity-feed.js.map +1 -1
  3. package/dist/app-shell.js +3 -3
  4. package/dist/app-shell.js.map +1 -1
  5. package/dist/badge.d.ts +1 -1
  6. package/dist/badge.js +1 -1
  7. package/dist/badge.js.map +1 -1
  8. package/dist/blocks.css +30 -30
  9. package/dist/card.d.ts +6 -6
  10. package/dist/card.js +2 -2
  11. package/dist/card.js.map +1 -1
  12. package/dist/chip-row.js +1 -1
  13. package/dist/chip-row.js.map +1 -1
  14. package/dist/chip.js +1 -1
  15. package/dist/chip.js.map +1 -1
  16. package/dist/{chunk-QDXV544C.js → chunk-4QH6CP2Y.js} +2 -2
  17. package/dist/chunk-4QH6CP2Y.js.map +1 -0
  18. package/dist/{chunk-CKSHLSDH.js → chunk-AAP3U66V.js} +2 -2
  19. package/dist/{chunk-7UCPGXKN.js → chunk-C3NF7V5Y.js} +2 -2
  20. package/dist/{chunk-7UCPGXKN.js.map → chunk-C3NF7V5Y.js.map} +1 -1
  21. package/dist/{chunk-K2ZYB4HP.js → chunk-S5LP7KKW.js} +2 -2
  22. package/dist/{chunk-63KCDMX2.js → chunk-VPO27AFS.js} +4 -4
  23. package/dist/chunk-VPO27AFS.js.map +1 -0
  24. package/dist/{chunk-YOTQD7JD.js → chunk-WYQ277F2.js} +2 -2
  25. package/dist/{chunk-2DGLWTPS.js → chunk-YKOLME7R.js} +3 -3
  26. package/dist/{chunk-2DGLWTPS.js.map → chunk-YKOLME7R.js.map} +1 -1
  27. package/dist/code-block.js +4 -4
  28. package/dist/code-block.js.map +1 -1
  29. package/dist/combobox.js +2 -2
  30. package/dist/data-table.js +3 -3
  31. package/dist/file-upload.js +3 -3
  32. package/dist/file-upload.js.map +1 -1
  33. package/dist/filter-bar.js +2 -2
  34. package/dist/genui.js +4 -4
  35. package/dist/landing-page.js +1 -6
  36. package/dist/landing-page.js.map +1 -1
  37. package/dist/language-switch.js +1 -1
  38. package/dist/marketing-layout.js +1 -1
  39. package/dist/marketing-layout.js.map +1 -1
  40. package/dist/media-grid.js +1 -1
  41. package/dist/meter.js +2 -2
  42. package/dist/meter.js.map +1 -1
  43. package/dist/motion.css +2 -2
  44. package/dist/native.js +4 -4
  45. package/dist/native.js.map +1 -1
  46. package/dist/nav-bar.js +7 -7
  47. package/dist/nav-bar.js.map +1 -1
  48. package/dist/product-page.js +1 -6
  49. package/dist/product-page.js.map +1 -1
  50. package/dist/progress.js +3 -3
  51. package/dist/progress.js.map +1 -1
  52. package/dist/rail-nav.js +1 -1
  53. package/dist/rail-nav.js.map +1 -1
  54. package/dist/service-card.js +2 -2
  55. package/dist/service-card.js.map +1 -1
  56. package/dist/settings-layout.js +1 -1
  57. package/dist/settings-layout.js.map +1 -1
  58. package/dist/site-footer.js +2 -2
  59. package/dist/site-footer.js.map +1 -1
  60. package/dist/skip-link.js +1 -1
  61. package/dist/slot-picker.js +1 -1
  62. package/dist/slot-picker.js.map +1 -1
  63. package/dist/step-progress.js +1 -1
  64. package/dist/step-progress.js.map +1 -1
  65. package/dist/tabs.js +4 -4
  66. package/dist/tabs.js.map +1 -1
  67. package/dist/tag-input.js +2 -2
  68. package/dist/tag-selector.js +1 -1
  69. package/dist/toast.js +1 -1
  70. package/dist/toast.js.map +1 -1
  71. package/package.json +5 -5
  72. package/registry/activity-feed.json +1 -1
  73. package/registry/app-shell.json +1 -1
  74. package/registry/badge.json +1 -1
  75. package/registry/card.json +1 -1
  76. package/registry/chip-row.json +1 -1
  77. package/registry/chip.json +1 -1
  78. package/registry/examples/badge/native/dot.dark.png +0 -0
  79. package/registry/examples/badge/native/dot.light.png +0 -0
  80. package/registry/examples/badge/native/scrim.dark.png +0 -0
  81. package/registry/examples/badge/native/scrim.light.png +0 -0
  82. package/registry/examples/badge/native/tones.dark.png +0 -0
  83. package/registry/examples/badge/native/tones.light.png +0 -0
  84. package/registry/examples/card/native/default.dark.png +0 -0
  85. package/registry/examples/card/native/default.light.png +0 -0
  86. package/registry/examples/card/native/elevated.dark.png +0 -0
  87. package/registry/examples/card/native/elevated.light.png +0 -0
  88. package/registry/examples/description-list/native/job-details.dark.png +0 -0
  89. package/registry/examples/description-list/native/job-details.light.png +0 -0
  90. package/registry/examples/meta-row/native/video.dark.png +0 -0
  91. package/registry/examples/meta-row/native/video.light.png +0 -0
  92. package/registry/examples/swipe-deck/native/discover.dark.png +0 -0
  93. package/registry/examples/swipe-deck/native/discover.light.png +0 -0
  94. package/registry/index.json +5 -5
  95. package/registry/landing-page.json +1 -1
  96. package/registry/language-switch.json +1 -1
  97. package/registry/map.json +1 -1
  98. package/registry/meter.json +1 -1
  99. package/registry/nav-bar.json +2 -2
  100. package/registry/product-page.json +1 -1
  101. package/registry/progress.json +1 -1
  102. package/registry/rail-nav.json +2 -2
  103. package/registry/service-card.json +1 -1
  104. package/registry/settings-layout.json +1 -1
  105. package/registry/site-footer.json +2 -2
  106. package/registry/skip-link.json +1 -1
  107. package/registry/slot-picker.json +1 -1
  108. package/registry/step-progress.json +1 -1
  109. package/registry/tabs.json +1 -1
  110. package/registry/toast.json +1 -1
  111. package/dist/chunk-63KCDMX2.js.map +0 -1
  112. package/dist/chunk-QDXV544C.js.map +0 -1
  113. /package/dist/{chunk-CKSHLSDH.js.map → chunk-AAP3U66V.js.map} +0 -0
  114. /package/dist/{chunk-K2ZYB4HP.js.map → chunk-S5LP7KKW.js.map} +0 -0
  115. /package/dist/{chunk-YOTQD7JD.js.map → chunk-WYQ277F2.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/file-upload/file-upload.tsx","../src/progress/progress.tsx"],"sourcesContent":["// Client. Three parts that work alone or together:\n// - `Dropzone`: drop files on it or pick them with its button (a real `<input type=\"file\">`, so the keyboard and the\n// phone's camera / files sheet work); it checks the type, the size and the count, and hands over the accepted files\n// and the refused ones with their reason;\n// - `UploadList`: each file with its preview, size, progress, speed, and the actions its state allows (pause, resume,\n// retry, cancel, remove); the progress is a `progressbar`, a failure is announced;\n// - `useUploadQueue`: runs the app's `upload(file, { signal, onProgress, resumed })` with a concurrency, pauses by\n// aborting the signal and resumes by calling it again with `resumed: true` — what a Tus client (tus-js-client's\n// `abort()` / `start()`) or a multipart upload needs, without depending on either. The line (concurrency, retries\n// with backoff, pause per file) is `createQueue` of @krizaka/pacer.\nimport { getNumberFormat } from \"@krizaka/intl\";\nimport { type QueueOptions } from \"@krizaka/pacer\";\nimport { useQueue, useQueueSnapshot } from \"@krizaka/pacer/react\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CloseGlyph, FileGlyph, PauseGlyph, PlayGlyph, RetryGlyph, UploadGlyph } from \"../glyphs\";\nimport { useLatest } from \"../media\";\nimport { Progress } from \"../progress\";\n\nexport const fileUpload = tv({\n slots: {\n zone:\n \"relative flex w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed border-border-default bg-surface-1 px-6 py-10 text-center \" +\n \"transition-colors data-[dragging]:border-accent data-[dragging]:bg-accent-soft data-[disabled]:opacity-50 has-[:focus-visible]:border-accent\",\n zoneIcon: \"flex h-12 w-12 items-center justify-center rounded-full bg-surface-3 text-fg-secondary\",\n zoneTitle: \"text-sm font-semibold text-fg\",\n zoneHint: \"text-xs text-fg-secondary\",\n browse:\n \"inline-flex h-9 items-center justify-center rounded-lg bg-accent px-4 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none\",\n refusals: \"w-full text-left text-xs font-medium text-fg-danger\",\n list: \"flex w-full flex-col gap-2\",\n item: \"flex items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-3 data-[status=error]:border-danger/60\",\n preview: \"flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-surface-3 text-fg-secondary\",\n body: \"flex min-w-0 flex-1 flex-col gap-1.5\",\n name: \"truncate text-sm font-semibold text-fg\",\n meta: \"flex flex-wrap gap-x-2 text-xs tabular-nums text-fg-secondary\",\n error: \"text-xs font-medium text-fg-danger\",\n actions: \"flex shrink-0 items-center gap-1\",\n action:\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n});\n\n/** Why a file was refused. */\nexport type FileRejection = { file: File; reason: \"type\" | \"size\" | \"count\" };\n\n/** Whether a file matches an `accept` list (`\"video/*,.mov,image/png\"`). */\nexport function acceptsFile(file: File, accept?: string): boolean {\n if (!accept) return true;\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return accept\n .split(\",\")\n .map((rule) => rule.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => (rule.startsWith(\".\") ? name.endsWith(rule) : rule.endsWith(\"/*\") ? type.startsWith(rule.slice(0, -1)) : type === rule));\n}\n\n/** A size in the locale's words (`\"1.4 GB\"`, `\"820 kB\"`), through `@krizaka/intl`. */\nexport function formatBytes(bytes: number, locale = \"en\"): string {\n const units = [\"byte\", \"kilobyte\", \"megabyte\", \"gigabyte\", \"terabyte\"] as const;\n let value = Math.max(0, bytes);\n let unit = 0;\n while (value >= 1000 && unit < units.length - 1) {\n value /= 1000;\n unit += 1;\n }\n return getNumberFormat(locale, { style: \"unit\", unit: units[unit], unitDisplay: \"short\", maximumFractionDigits: value < 10 && unit > 0 ? 1 : 0 }).format(value);\n}\n\nexport type DropzoneProps = {\n /** The title in the zone (\"Drop your video here\"), passed translated. */\n title: string;\n /** The button that opens the file picker (\"Choose a file\"), passed translated. */\n browseLabel: string;\n /** What is accepted, in words (\"MP4 or MOV, up to 4 GB\"). */\n hint?: string;\n /** The types accepted, as the `accept` attribute (`\"video/*\"`, `\"image/png,.pdf\"`). */\n accept?: string;\n /** The largest file, in bytes. */\n maxSize?: number;\n /** The most files at once (1 unless `multiple`). */\n maxFiles?: number;\n /** Several files at once. */\n multiple?: boolean;\n /** Not available (an upload is running). */\n disabled?: boolean;\n /** On a phone, opens the camera directly (`\"environment\"` the back one, `\"user\"` the front one). */\n capture?: \"user\" | \"environment\";\n /** Called with the files accepted (never empty). */\n onFiles: (files: File[]) => void;\n /** Called with the files refused and why (show `refusalLabel`'s words, or your own). */\n onReject?: (rejections: FileRejection[]) => void;\n /** The words of a refusal, shown in the zone: `(r) => r.file.name + \" is too large\"`. Without it, nothing is shown. */\n refusalLabel?: (rejection: FileRejection) => string;\n /** An icon instead of the upload glyph. */\n icon?: React.ReactNode;\n /** Classes merged on the zone. */\n className?: string;\n};\n\n/** A zone to drop files on, with a button that opens the picker: checks type, size and count. */\nexport function Dropzone({\n title,\n browseLabel,\n hint,\n accept,\n maxSize,\n maxFiles,\n multiple,\n disabled,\n capture,\n onFiles,\n onReject,\n refusalLabel,\n icon,\n className,\n}: DropzoneProps) {\n const s = fileUpload();\n const input = React.useRef<HTMLInputElement>(null);\n const [dragging, setDragging] = React.useState(false);\n const [refused, setRefused] = React.useState<FileRejection[]>([]);\n const depth = React.useRef(0);\n const id = React.useId();\n const limit = maxFiles ?? (multiple ? Infinity : 1);\n\n const take = (list: FileList | File[]) => {\n const files = [...list];\n const accepted: File[] = [];\n const rejections: FileRejection[] = [];\n for (const file of files) {\n if (!acceptsFile(file, accept)) rejections.push({ file, reason: \"type\" });\n else if (maxSize !== undefined && file.size > maxSize) rejections.push({ file, reason: \"size\" });\n else if (accepted.length >= limit) rejections.push({ file, reason: \"count\" });\n else accepted.push(file);\n }\n setRefused(rejections);\n if (accepted.length) onFiles(accepted);\n if (rejections.length) onReject?.(rejections);\n };\n\n return (\n <div\n className={s.zone({ className })}\n data-dragging={dragging ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n onDragEnter={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current += 1;\n setDragging(true);\n }}\n onDragOver={(event) => {\n if (disabled) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"copy\";\n }}\n onDragLeave={() => {\n depth.current = Math.max(0, depth.current - 1);\n if (depth.current === 0) setDragging(false);\n }}\n onDrop={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current = 0;\n setDragging(false);\n take(event.dataTransfer.files);\n }}\n >\n <span className={s.zoneIcon()} aria-hidden>\n {icon ?? <UploadGlyph className=\"h-6 w-6\" />}\n </span>\n <p id={`${id}-title`} className={s.zoneTitle()}>\n {title}\n </p>\n {hint ? (\n <p id={`${id}-hint`} className={s.zoneHint()}>\n {hint}\n </p>\n ) : null}\n <button\n type=\"button\"\n className={s.browse()}\n disabled={disabled}\n aria-describedby={[`${id}-title`, hint ? `${id}-hint` : null].filter(Boolean).join(\" \")}\n onClick={() => input.current?.click()}\n >\n {browseLabel}\n </button>\n <input\n ref={input}\n type=\"file\"\n tabIndex={-1}\n aria-hidden\n className=\"sr-only\"\n accept={accept}\n multiple={multiple}\n capture={capture}\n disabled={disabled}\n onChange={(event) => {\n if (event.target.files?.length) take(event.target.files);\n event.target.value = \"\";\n }}\n />\n {refusalLabel && refused.length > 0 ? (\n <ul role=\"alert\" className={s.refusals()}>\n {refused.map((r) => (\n <li key={`${r.file.name}-${r.reason}`}>{refusalLabel(r)}</li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\nexport type UploadStatus = \"queued\" | \"uploading\" | \"paused\" | \"done\" | \"error\";\n\nexport type UploadItem = {\n /** A stable id. */\n id: string;\n /** The file's name. */\n name: string;\n /** Its size, in bytes. */\n size: number;\n /** Its MIME type (`video/mp4`): picks the preview. */\n type?: string;\n /** Sent so far, between 0 and 1. */\n progress: number;\n /** Where it stands. */\n status: UploadStatus;\n /** Why it failed, passed translated. */\n error?: string;\n /** A preview (an object URL of the file, a thumbnail from the server). */\n previewUrl?: string;\n /** Bytes per second, for the speed and the time left. */\n speed?: number;\n};\n\nexport type UploadListLabels = {\n /** The list's accessible name (\"Uploads\"). */\n list: string;\n pause: (name: string) => string;\n resume: (name: string) => string;\n retry: (name: string) => string;\n cancel: (name: string) => string;\n remove: (name: string) => string;\n /** A state in words: `(status) => \"Paused\"`… */\n status: (status: UploadStatus) => string;\n /** The time left: `(seconds) => \"2 min left\"`. */\n remaining: (seconds: number) => string;\n};\n\nexport const uploadListLabels: UploadListLabels = {\n list: \"Uploads\",\n pause: (name) => `Pause ${name}`,\n resume: (name) => `Resume ${name}`,\n retry: (name) => `Retry ${name}`,\n cancel: (name) => `Cancel ${name}`,\n remove: (name) => `Remove ${name}`,\n status: (status) => ({ queued: \"Waiting\", uploading: \"Uploading\", paused: \"Paused\", done: \"Uploaded\", error: \"Failed\" })[status],\n remaining: (seconds) => (seconds < 60 ? `${Math.ceil(seconds)} s left` : `${Math.ceil(seconds / 60)} min left`),\n};\n\nexport type UploadListProps = {\n /** The uploads, as `useUploadQueue` gives them (or your own state). */\n items: readonly UploadItem[];\n /** Pauses an upload (shown while it uploads, when given). */\n onPause?: (id: string) => void;\n /** Resumes a paused one. */\n onResume?: (id: string) => void;\n /** Tries a failed one again. */\n onRetry?: (id: string) => void;\n /** Stops an upload under way and drops it. */\n onCancel?: (id: string) => void;\n /** Drops a finished or failed one from the list. */\n onRemove?: (id: string) => void;\n /** The locale of sizes and speeds. Default `\"en\"`. */\n locale?: string;\n /** The words, passed translated; English by default. */\n labels?: Partial<UploadListLabels>;\n /** Classes merged on the list. */\n className?: string;\n};\n\nfunction Preview({ item }: { item: UploadItem }) {\n if (item.previewUrl && item.type?.startsWith(\"image/\")) return <img src={item.previewUrl} alt=\"\" className=\"h-full w-full object-cover\" />;\n if (item.previewUrl && item.type?.startsWith(\"video/\")) return <video src={item.previewUrl} muted playsInline preload=\"metadata\" className=\"h-full w-full object-cover\" aria-hidden />;\n return <FileGlyph className=\"h-5 w-5\" />;\n}\n\n/** The files being sent: preview, size, progress, speed and time left, and the actions each state allows. */\nexport function UploadList({ items, onPause, onResume, onRetry, onCancel, onRemove, locale = \"en\", labels: given, className }: UploadListProps) {\n const labels = { ...uploadListLabels, ...given };\n const s = fileUpload();\n const action = (label: string, run: () => void, glyph: React.ReactNode) => (\n <button type=\"button\" className={s.action()} aria-label={label} title={label} onClick={run}>\n {glyph}\n </button>\n );\n return (\n <ul aria-label={labels.list} className={s.list({ className })}>\n {items.map((item) => {\n const left = item.status === \"uploading\" && item.speed ? ((1 - item.progress) * item.size) / item.speed : null;\n return (\n <li key={item.id} className={s.item()} data-status={item.status}>\n <span className={s.preview()}>\n <Preview item={item} />\n </span>\n <div className={s.body()}>\n <p className={s.name()}>{item.name}</p>\n {item.status !== \"done\" && item.status !== \"error\" ? (\n <Progress size=\"sm\" label={item.name} value={item.status === \"queued\" ? null : Math.round(item.progress * 100)} />\n ) : null}\n <p className={s.meta()}>\n <span>{labels.status(item.status)}</span>\n <span>{item.status === \"done\" ? formatBytes(item.size, locale) : `${formatBytes(item.progress * item.size, locale)} / ${formatBytes(item.size, locale)}`}</span>\n {item.status === \"uploading\" && item.speed ? <span>{formatBytes(item.speed, locale)}/s</span> : null}\n {left !== null ? <span>{labels.remaining(left)}</span> : null}\n </p>\n {item.status === \"error\" && item.error ? (\n <p role=\"alert\" className={s.error()}>\n {item.error}\n </p>\n ) : null}\n </div>\n <div className={s.actions()}>\n {item.status === \"uploading\" && onPause && action(labels.pause(item.name), () => onPause(item.id), <PauseGlyph className=\"h-4 w-4\" />)}\n {item.status === \"paused\" && onResume && action(labels.resume(item.name), () => onResume(item.id), <PlayGlyph className=\"h-4 w-4\" />)}\n {item.status === \"error\" && onRetry && action(labels.retry(item.name), () => onRetry(item.id), <RetryGlyph className=\"h-4 w-4\" />)}\n {(item.status === \"done\" || item.status === \"error\") && onRemove\n ? action(labels.remove(item.name), () => onRemove(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : item.status !== \"done\" && onCancel\n ? action(labels.cancel(item.name), () => onCancel(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : null}\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n\nexport type UploadContext = {\n /** Aborted on pause and cancel: stop sending (`tusUpload.abort()`, `xhr.abort()`). */\n signal: AbortSignal;\n /** Report the bytes sent so far. */\n onProgress: (sent: number) => void;\n /** `true` when this call resumes a paused or failed upload (a Tus client then continues from its offset). */\n resumed: boolean;\n};\n\nexport type UploadQueueOptions = {\n /** Sends one file; resolves when it is done, rejects on failure (its `message` becomes the item's error). */\n upload: (file: File, context: UploadContext) => Promise<void>;\n /** How many files go at once. Default 2. */\n concurrency?: number;\n /** How many times a failed upload is tried again by itself (with `backoff`) before it shows its error. Default 0. */\n retries?: number;\n /** The wait between retries: exponential from 1 s by default (`@krizaka/pacer`). */\n backoff?: QueueOptions[\"backoff\"];\n /** Whether an error deserves another try (a 4xx never does). Default: every error does, up to `retries`. */\n shouldRetry?: QueueOptions[\"shouldRetry\"];\n /** Makes an object URL for each file to preview (revoked when it leaves). Default `true`. */\n previews?: boolean;\n};\n\n/** What the screen reads of an upload besides its status: the file, how far it got, how fast. */\ntype UploadMeta = { file: File; progress: number; speed?: number; previewUrl?: string; last?: { at: number; sent: number } };\n\n/**\n * The state of an upload queue around the app's `upload` function: `add`, `pause`, `resume`, `retry`, `cancel`, `remove`.\n * The line itself — concurrency, retries with backoff, pause by aborting the signal — is `createQueue` of\n * `@krizaka/pacer`; this hook adds the files, their previews, their progress and their speed.\n */\nexport function useUploadQueue(options: UploadQueueOptions) {\n const { concurrency, retries, backoff, shouldRetry } = options;\n const latest = useLatest(options);\n const queue = useQueue<UploadMeta>({ concurrency: concurrency ?? 2, retries, backoff, shouldRetry });\n const jobs = useQueueSnapshot(queue);\n const metas = useLatest(new Map(jobs.map((job) => [job.id, job.meta])));\n\n const add = React.useCallback(\n (files: readonly File[]) => {\n const { previews = true } = latest.current;\n for (const file of files) {\n const id = `${file.name}-${file.size}-${Math.random().toString(36).slice(2, 8)}`;\n const previewUrl = previews && typeof URL.createObjectURL === \"function\" ? URL.createObjectURL(file) : undefined;\n queue.add(\n async ({ signal, resumed }) => {\n queue.update(id, (m) => ({ ...m, last: { at: Date.now(), sent: m.progress * file.size } }));\n try {\n await latest.current.upload(file, {\n signal,\n resumed,\n onProgress: (sent) => {\n if (signal.aborted) return;\n queue.update(id, (m) => {\n const now = Date.now();\n const last = m.last ?? { at: now, sent };\n const fresh = now - last.at > 400;\n return { ...m, progress: file.size ? Math.min(1, sent / file.size) : 0, speed: fresh ? ((sent - last.sent) / (now - last.at)) * 1000 : m.speed, last: fresh ? { at: now, sent } : last };\n });\n },\n });\n if (!signal.aborted) queue.update(id, (m) => ({ ...m, progress: 1, speed: undefined }));\n } catch (error) {\n if (!signal.aborted) queue.update(id, (m) => ({ ...m, speed: undefined }));\n throw error;\n }\n },\n { id, meta: { file, progress: 0, previewUrl } },\n );\n }\n },\n [latest, queue],\n );\n const forget = React.useCallback(\n (id: string) => {\n const url = metas.current.get(id)?.previewUrl;\n if (url) URL.revokeObjectURL(url);\n queue.cancel(id);\n },\n [metas, queue],\n );\n React.useEffect(\n () => () => {\n for (const meta of metas.current.values()) if (meta.previewUrl) URL.revokeObjectURL(meta.previewUrl);\n },\n [metas],\n );\n const pause = React.useCallback(\n (id: string) => {\n queue.pause(id);\n queue.update(id, (m) => ({ ...m, speed: undefined }));\n },\n [queue],\n );\n\n const items = React.useMemo<UploadItem[]>(\n () =>\n jobs.map(({ id, status, error, meta }) => ({\n id,\n name: meta.file.name,\n size: meta.file.size,\n type: meta.file.type,\n progress: meta.progress,\n status: status === \"running\" ? \"uploading\" : status,\n error: error?.message,\n previewUrl: meta.previewUrl,\n speed: status === \"running\" ? meta.speed : undefined,\n })),\n [jobs],\n );\n return { items, add, pause, resume: queue.resume, retry: queue.retry, cancel: forget, remove: forget };\n}\n","// Server-safe: no hook, no context. role=\"progressbar\" with aria-valuenow / aria-valuemax (none when indeterminate),\n// named by `label`. A bar or a ring; the ring can hold content in its centre (an amount, a percentage).\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const progress = tv({\n slots: {\n root: \"relative\",\n track: \"\",\n indicator: \"\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n buffer: \"absolute inset-y-0 left-0 -z-10 rounded-full bg-accent/25 transition-[width] duration-500 motion-reduce:transition-none\",\n frame: \"flex w-full flex-col gap-1.5\",\n header: \"flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-xs\",\n caption: \"max-w-full shrink-0 grow truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n },\n variants: {\n variant: {\n bar: {\n root: \"relative isolate w-full overflow-hidden rounded-full bg-surface-3\",\n indicator:\n \"kz-progress h-full rounded-full bg-linear-to-r from-accent to-accent-2 transition-[width] duration-500 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:w-2/5\",\n },\n ring: {\n root: \"inline-flex shrink-0 items-center justify-center\",\n track: \"stroke-border-default\",\n indicator:\n \"stroke-accent transition-[stroke-dashoffset] duration-1000 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:origin-center data-[state=indeterminate]:animate-spin motion-reduce:data-[state=indeterminate]:animate-none\",\n },\n },\n size: { sm: {}, md: {}, lg: {} },\n status: {\n active: {},\n // Paused: the fill rests, striped — the task is waiting for someone, not broken.\n paused: { indicator: \"kz-progress-paused opacity-70\" },\n // Failed: the fill turns to the danger colour where it stopped.\n error: { indicator: \"from-danger to-danger stroke-danger\" },\n },\n },\n compoundVariants: [\n { variant: \"bar\", size: \"sm\", class: { root: \"h-1\" } },\n { variant: \"bar\", size: \"md\", class: { root: \"h-2\" } },\n { variant: \"bar\", size: \"lg\", class: { root: \"h-3\" } },\n { variant: \"ring\", size: \"sm\", class: { root: \"h-10 w-10\" } },\n { variant: \"ring\", size: \"md\", class: { root: \"h-20 w-20\" } },\n { variant: \"ring\", size: \"lg\", class: { root: \"h-36 w-36\" } },\n ],\n defaultVariants: { variant: \"bar\", size: \"md\", status: \"active\" },\n});\n\nexport type ProgressVariants = VariantProps<typeof progress>;\n\nexport type ProgressProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n ProgressVariants & {\n /** bar · ring (a ring holds its `children` in the centre). */\n variant?: ProgressVariants[\"variant\"];\n /** sm · md · lg: the bar's thickness, the ring's diameter. */\n size?: ProgressVariants[\"size\"];\n /** How far, between 0 and `max`. `null` or absent: indeterminate (a wait of unknown length). */\n value?: number | null;\n /** The value that means complete. */\n max?: number;\n /** The accessible name — passed translated (\"Upload\", \"Raised towards the goal\"). */\n label: string;\n /** The value in words for assistive technology (\"$420 of $1,000\") — default: the percentage. */\n valueText?: string;\n /** A ring's centre: an amount, a percentage, an icon. */\n children?: React.ReactNode;\n /** How far is already available beyond the value (a video buffered ahead), between 0 and `max` — bar only. */\n buffer?: number;\n /** Splits the bar or the ring into this many segments: the steps of a task, the parts of a goal. */\n segments?: number;\n /**\n * active · paused (the fill rests, striped: waiting for someone) · error (the fill turns to the danger colour where it stopped).\n * @default \"active\"\n */\n status?: ProgressVariants[\"status\"];\n /** Shows the label above the bar, with the percentage (or `valueText`) and `eta` — bar only. */\n showLabel?: boolean;\n /** The time left, in words (“About 2 min left”), shown with `showLabel` and read with the value — passed translated. */\n eta?: string;\n };\n\nconst STROKE = { sm: 12, md: 9, lg: 7 } as const;\n\n/** A progress bar or ring: `value` / `max`, indeterminate without a value. State: `data-state` (loading · complete · indeterminate). */\nexport function Progress({\n variant = \"bar\",\n size = \"md\",\n value,\n max = 100,\n label,\n valueText,\n className,\n children,\n buffer,\n segments,\n status = \"active\",\n showLabel,\n eta,\n ...props\n}: ProgressProps) {\n const s = progress({ variant, size, status });\n const maskId = `kz-seg-${React.useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n const known = typeof value === \"number\" && Number.isFinite(value);\n const ratio = known ? Math.min(1, Math.max(0, value / (max || 1))) : 0;\n const percent = Math.round(ratio * 100);\n const state = !known ? \"indeterminate\" : ratio >= 1 ? \"complete\" : \"loading\";\n const aria = {\n role: \"progressbar\" as const,\n \"aria-label\": label,\n \"aria-valuemin\": known ? 0 : undefined,\n \"aria-valuemax\": known ? max : undefined,\n \"aria-valuenow\": known ? Math.min(max, Math.max(0, value)) : undefined,\n \"aria-valuetext\": [known ? (valueText ?? `${percent}%`) : valueText, eta].filter(Boolean).join(\", \") || undefined,\n \"data-state\": state,\n \"data-status\": status === \"active\" ? undefined : status,\n \"data-value\": known ? value : undefined,\n \"data-variant\": variant,\n };\n if (variant === \"ring\") {\n const stroke = STROKE[size];\n const r = 50 - stroke / 2;\n return (\n <div {...aria} className={s.root({ className })} {...props}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n {segments && segments > 1 && (\n <mask id={maskId}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" stroke=\"white\" strokeWidth={stroke + 2} pathLength={100} strokeDasharray={`${100 / segments - 2} 2`} />\n </mask>\n )}\n <g mask={segments && segments > 1 ? `url(#${maskId})` : undefined}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth={stroke} className={s.track()} />\n <circle\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth={stroke}\n strokeLinecap=\"round\"\n pathLength={100}\n strokeDasharray={100}\n strokeDashoffset={known ? 100 - percent : 75}\n data-state={state}\n className={s.indicator()}\n />\n </g>\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n );\n }\n const segmented = segments && segments > 1 ? ({ \"--kz-segments\": segments } as React.CSSProperties) : undefined;\n const bufferRatio = typeof buffer === \"number\" && Number.isFinite(buffer) ? Math.min(1, Math.max(0, buffer / (max || 1))) : null;\n const bar = (\n <div {...aria} className={s.root({ className: [segmented && \"kz-progress-segmented\", className].filter(Boolean).join(\" \") || undefined })} style={segmented} {...props}>\n <div data-state={state} className={s.indicator()} style={known ? { width: `${ratio * 100}%` } : undefined} />\n {bufferRatio !== null && <div aria-hidden className={s.buffer()} style={{ width: `${bufferRatio * 100}%` }} />}\n </div>\n );\n if (!showLabel) return bar;\n const figure = [known ? (valueText ?? `${percent} %`) : valueText, eta].filter(Boolean).join(\" · \");\n return (\n <div className={s.frame()}>\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {figure && <span className={s.figure()}>{figure}</span>}\n </div>\n {bar}\n </div>\n );\n}\n"],"mappings":"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","names":["React","tv","jsx","jsxs","tv"]}
1
+ {"version":3,"sources":["../src/file-upload/file-upload.tsx","../src/progress/progress.tsx"],"sourcesContent":["// Client. Three parts that work alone or together:\n// - `Dropzone`: drop files on it or pick them with its button (a real `<input type=\"file\">`, so the keyboard and the\n// phone's camera / files sheet work); it checks the type, the size and the count, and hands over the accepted files\n// and the refused ones with their reason;\n// - `UploadList`: each file with its preview, size, progress, speed, and the actions its state allows (pause, resume,\n// retry, cancel, remove); the progress is a `progressbar`, a failure is announced;\n// - `useUploadQueue`: runs the app's `upload(file, { signal, onProgress, resumed })` with a concurrency, pauses by\n// aborting the signal and resumes by calling it again with `resumed: true` — what a Tus client (tus-js-client's\n// `abort()` / `start()`) or a multipart upload needs, without depending on either. The line (concurrency, retries\n// with backoff, pause per file) is `createQueue` of @krizaka/pacer.\nimport { getNumberFormat } from \"@krizaka/intl\";\nimport { type QueueOptions } from \"@krizaka/pacer\";\nimport { useQueue, useQueueSnapshot } from \"@krizaka/pacer/react\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CloseGlyph, FileGlyph, PauseGlyph, PlayGlyph, RetryGlyph, UploadGlyph } from \"../glyphs\";\nimport { useLatest } from \"../media\";\nimport { Progress } from \"../progress\";\n\nexport const fileUpload = tv({\n slots: {\n zone:\n \"relative flex w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed border-border-default bg-surface-1 px-6 py-10 text-center \" +\n \"transition-colors data-[dragging]:border-accent data-[dragging]:bg-accent-soft data-[disabled]:opacity-50 has-[:focus-visible]:border-accent\",\n zoneIcon: \"flex h-12 w-12 items-center justify-center rounded-full bg-surface-3 text-fg-secondary\",\n zoneTitle: \"text-sm font-semibold text-fg\",\n zoneHint: \"text-xs text-fg-secondary\",\n browse:\n \"inline-flex h-9 items-center justify-center rounded-lg bg-accent px-4 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none\",\n refusals: \"w-full text-left text-xs font-medium text-fg-danger\",\n list: \"flex w-full flex-col gap-2\",\n item: \"flex items-center gap-3 rounded-xl border border-border-default bg-surface-1 p-3 data-[status=error]:border-danger/60\",\n preview: \"flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-surface-3 text-fg-secondary\",\n body: \"flex min-w-0 flex-1 flex-col gap-1.5\",\n name: \"truncate text-sm font-semibold text-fg\",\n meta: \"flex flex-wrap gap-x-2 text-xs tabular-nums text-fg-secondary\",\n error: \"text-xs font-medium text-fg-danger\",\n actions: \"flex shrink-0 items-center gap-1\",\n action:\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n});\n\n/** Why a file was refused. */\nexport type FileRejection = { file: File; reason: \"type\" | \"size\" | \"count\" };\n\n/** Whether a file matches an `accept` list (`\"video/*,.mov,image/png\"`). */\nexport function acceptsFile(file: File, accept?: string): boolean {\n if (!accept) return true;\n const name = file.name.toLowerCase();\n const type = file.type.toLowerCase();\n return accept\n .split(\",\")\n .map((rule) => rule.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => (rule.startsWith(\".\") ? name.endsWith(rule) : rule.endsWith(\"/*\") ? type.startsWith(rule.slice(0, -1)) : type === rule));\n}\n\n/** A size in the locale's words (`\"1.4 GB\"`, `\"820 kB\"`), through `@krizaka/intl`. */\nexport function formatBytes(bytes: number, locale = \"en\"): string {\n const units = [\"byte\", \"kilobyte\", \"megabyte\", \"gigabyte\", \"terabyte\"] as const;\n let value = Math.max(0, bytes);\n let unit = 0;\n while (value >= 1000 && unit < units.length - 1) {\n value /= 1000;\n unit += 1;\n }\n return getNumberFormat(locale, { style: \"unit\", unit: units[unit], unitDisplay: \"short\", maximumFractionDigits: value < 10 && unit > 0 ? 1 : 0 }).format(value);\n}\n\nexport type DropzoneProps = {\n /** The title in the zone (\"Drop your video here\"), passed translated. */\n title: string;\n /** The button that opens the file picker (\"Choose a file\"), passed translated. */\n browseLabel: string;\n /** What is accepted, in words (\"MP4 or MOV, up to 4 GB\"). */\n hint?: string;\n /** The types accepted, as the `accept` attribute (`\"video/*\"`, `\"image/png,.pdf\"`). */\n accept?: string;\n /** The largest file, in bytes. */\n maxSize?: number;\n /** The most files at once (1 unless `multiple`). */\n maxFiles?: number;\n /** Several files at once. */\n multiple?: boolean;\n /** Not available (an upload is running). */\n disabled?: boolean;\n /** On a phone, opens the camera directly (`\"environment\"` the back one, `\"user\"` the front one). */\n capture?: \"user\" | \"environment\";\n /** Called with the files accepted (never empty). */\n onFiles: (files: File[]) => void;\n /** Called with the files refused and why (show `refusalLabel`'s words, or your own). */\n onReject?: (rejections: FileRejection[]) => void;\n /** The words of a refusal, shown in the zone: `(r) => r.file.name + \" is too large\"`. Without it, nothing is shown. */\n refusalLabel?: (rejection: FileRejection) => string;\n /** An icon instead of the upload glyph. */\n icon?: React.ReactNode;\n /** Classes merged on the zone. */\n className?: string;\n};\n\n/** A zone to drop files on, with a button that opens the picker: checks type, size and count. */\nexport function Dropzone({\n title,\n browseLabel,\n hint,\n accept,\n maxSize,\n maxFiles,\n multiple,\n disabled,\n capture,\n onFiles,\n onReject,\n refusalLabel,\n icon,\n className,\n}: DropzoneProps) {\n const s = fileUpload();\n const input = React.useRef<HTMLInputElement>(null);\n const [dragging, setDragging] = React.useState(false);\n const [refused, setRefused] = React.useState<FileRejection[]>([]);\n const depth = React.useRef(0);\n const id = React.useId();\n const limit = maxFiles ?? (multiple ? Infinity : 1);\n\n const take = (list: FileList | File[]) => {\n const files = [...list];\n const accepted: File[] = [];\n const rejections: FileRejection[] = [];\n for (const file of files) {\n if (!acceptsFile(file, accept)) rejections.push({ file, reason: \"type\" });\n else if (maxSize !== undefined && file.size > maxSize) rejections.push({ file, reason: \"size\" });\n else if (accepted.length >= limit) rejections.push({ file, reason: \"count\" });\n else accepted.push(file);\n }\n setRefused(rejections);\n if (accepted.length) onFiles(accepted);\n if (rejections.length) onReject?.(rejections);\n };\n\n return (\n <div\n className={s.zone({ className })}\n data-dragging={dragging ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n onDragEnter={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current += 1;\n setDragging(true);\n }}\n onDragOver={(event) => {\n if (disabled) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"copy\";\n }}\n onDragLeave={() => {\n depth.current = Math.max(0, depth.current - 1);\n if (depth.current === 0) setDragging(false);\n }}\n onDrop={(event) => {\n if (disabled) return;\n event.preventDefault();\n depth.current = 0;\n setDragging(false);\n take(event.dataTransfer.files);\n }}\n >\n <span className={s.zoneIcon()} aria-hidden>\n {icon ?? <UploadGlyph className=\"h-6 w-6\" />}\n </span>\n <p id={`${id}-title`} className={s.zoneTitle()}>\n {title}\n </p>\n {hint ? (\n <p id={`${id}-hint`} className={s.zoneHint()}>\n {hint}\n </p>\n ) : null}\n <button\n type=\"button\"\n className={s.browse()}\n disabled={disabled}\n aria-describedby={[`${id}-title`, hint ? `${id}-hint` : null].filter(Boolean).join(\" \")}\n onClick={() => input.current?.click()}\n >\n {browseLabel}\n </button>\n <input\n ref={input}\n type=\"file\"\n tabIndex={-1}\n aria-hidden\n className=\"sr-only\"\n accept={accept}\n multiple={multiple}\n capture={capture}\n disabled={disabled}\n onChange={(event) => {\n if (event.target.files?.length) take(event.target.files);\n event.target.value = \"\";\n }}\n />\n {refusalLabel && refused.length > 0 ? (\n <ul role=\"alert\" className={s.refusals()}>\n {refused.map((r) => (\n <li key={`${r.file.name}-${r.reason}`}>{refusalLabel(r)}</li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\nexport type UploadStatus = \"queued\" | \"uploading\" | \"paused\" | \"done\" | \"error\";\n\nexport type UploadItem = {\n /** A stable id. */\n id: string;\n /** The file's name. */\n name: string;\n /** Its size, in bytes. */\n size: number;\n /** Its MIME type (`video/mp4`): picks the preview. */\n type?: string;\n /** Sent so far, between 0 and 1. */\n progress: number;\n /** Where it stands. */\n status: UploadStatus;\n /** Why it failed, passed translated. */\n error?: string;\n /** A preview (an object URL of the file, a thumbnail from the server). */\n previewUrl?: string;\n /** Bytes per second, for the speed and the time left. */\n speed?: number;\n};\n\nexport type UploadListLabels = {\n /** The list's accessible name (\"Uploads\"). */\n list: string;\n pause: (name: string) => string;\n resume: (name: string) => string;\n retry: (name: string) => string;\n cancel: (name: string) => string;\n remove: (name: string) => string;\n /** A state in words: `(status) => \"Paused\"`… */\n status: (status: UploadStatus) => string;\n /** The time left: `(seconds) => \"2 min left\"`. */\n remaining: (seconds: number) => string;\n};\n\nexport const uploadListLabels: UploadListLabels = {\n list: \"Uploads\",\n pause: (name) => `Pause ${name}`,\n resume: (name) => `Resume ${name}`,\n retry: (name) => `Retry ${name}`,\n cancel: (name) => `Cancel ${name}`,\n remove: (name) => `Remove ${name}`,\n status: (status) => ({ queued: \"Waiting\", uploading: \"Uploading\", paused: \"Paused\", done: \"Uploaded\", error: \"Failed\" })[status],\n remaining: (seconds) => (seconds < 60 ? `${Math.ceil(seconds)} s left` : `${Math.ceil(seconds / 60)} min left`),\n};\n\nexport type UploadListProps = {\n /** The uploads, as `useUploadQueue` gives them (or your own state). */\n items: readonly UploadItem[];\n /** Pauses an upload (shown while it uploads, when given). */\n onPause?: (id: string) => void;\n /** Resumes a paused one. */\n onResume?: (id: string) => void;\n /** Tries a failed one again. */\n onRetry?: (id: string) => void;\n /** Stops an upload under way and drops it. */\n onCancel?: (id: string) => void;\n /** Drops a finished or failed one from the list. */\n onRemove?: (id: string) => void;\n /** The locale of sizes and speeds. Default `\"en\"`. */\n locale?: string;\n /** The words, passed translated; English by default. */\n labels?: Partial<UploadListLabels>;\n /** Classes merged on the list. */\n className?: string;\n};\n\nfunction Preview({ item }: { item: UploadItem }) {\n if (item.previewUrl && item.type?.startsWith(\"image/\")) return <img src={item.previewUrl} alt=\"\" className=\"h-full w-full object-cover\" />;\n if (item.previewUrl && item.type?.startsWith(\"video/\")) return <video src={item.previewUrl} muted playsInline preload=\"metadata\" className=\"h-full w-full object-cover\" aria-hidden />;\n return <FileGlyph className=\"h-5 w-5\" />;\n}\n\n/** The files being sent: preview, size, progress, speed and time left, and the actions each state allows. */\nexport function UploadList({ items, onPause, onResume, onRetry, onCancel, onRemove, locale = \"en\", labels: given, className }: UploadListProps) {\n const labels = { ...uploadListLabels, ...given };\n const s = fileUpload();\n const action = (label: string, run: () => void, glyph: React.ReactNode) => (\n <button type=\"button\" className={s.action()} aria-label={label} title={label} onClick={run}>\n {glyph}\n </button>\n );\n return (\n <ul aria-label={labels.list} className={s.list({ className })}>\n {items.map((item) => {\n const left = item.status === \"uploading\" && item.speed ? ((1 - item.progress) * item.size) / item.speed : null;\n return (\n <li key={item.id} className={s.item()} data-status={item.status}>\n <span className={s.preview()}>\n <Preview item={item} />\n </span>\n <div className={s.body()}>\n <p className={s.name()}>{item.name}</p>\n {item.status !== \"done\" && item.status !== \"error\" ? (\n <Progress size=\"sm\" label={item.name} value={item.status === \"queued\" ? null : Math.round(item.progress * 100)} />\n ) : null}\n <p className={s.meta()}>\n <span>{labels.status(item.status)}</span>\n <span>{item.status === \"done\" ? formatBytes(item.size, locale) : `${formatBytes(item.progress * item.size, locale)} / ${formatBytes(item.size, locale)}`}</span>\n {item.status === \"uploading\" && item.speed ? <span>{formatBytes(item.speed, locale)}/s</span> : null}\n {left !== null ? <span>{labels.remaining(left)}</span> : null}\n </p>\n {item.status === \"error\" && item.error ? (\n <p role=\"alert\" className={s.error()}>\n {item.error}\n </p>\n ) : null}\n </div>\n <div className={s.actions()}>\n {item.status === \"uploading\" && onPause && action(labels.pause(item.name), () => onPause(item.id), <PauseGlyph className=\"h-4 w-4\" />)}\n {item.status === \"paused\" && onResume && action(labels.resume(item.name), () => onResume(item.id), <PlayGlyph className=\"h-4 w-4\" />)}\n {item.status === \"error\" && onRetry && action(labels.retry(item.name), () => onRetry(item.id), <RetryGlyph className=\"h-4 w-4\" />)}\n {(item.status === \"done\" || item.status === \"error\") && onRemove\n ? action(labels.remove(item.name), () => onRemove(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : item.status !== \"done\" && onCancel\n ? action(labels.cancel(item.name), () => onCancel(item.id), <CloseGlyph className=\"h-4 w-4\" />)\n : null}\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n\nexport type UploadContext = {\n /** Aborted on pause and cancel: stop sending (`tusUpload.abort()`, `xhr.abort()`). */\n signal: AbortSignal;\n /** Report the bytes sent so far. */\n onProgress: (sent: number) => void;\n /** `true` when this call resumes a paused or failed upload (a Tus client then continues from its offset). */\n resumed: boolean;\n};\n\nexport type UploadQueueOptions = {\n /** Sends one file; resolves when it is done, rejects on failure (its `message` becomes the item's error). */\n upload: (file: File, context: UploadContext) => Promise<void>;\n /** How many files go at once. Default 2. */\n concurrency?: number;\n /** How many times a failed upload is tried again by itself (with `backoff`) before it shows its error. Default 0. */\n retries?: number;\n /** The wait between retries: exponential from 1 s by default (`@krizaka/pacer`). */\n backoff?: QueueOptions[\"backoff\"];\n /** Whether an error deserves another try (a 4xx never does). Default: every error does, up to `retries`. */\n shouldRetry?: QueueOptions[\"shouldRetry\"];\n /** Makes an object URL for each file to preview (revoked when it leaves). Default `true`. */\n previews?: boolean;\n};\n\n/** What the screen reads of an upload besides its status: the file, how far it got, how fast. */\ntype UploadMeta = { file: File; progress: number; speed?: number; previewUrl?: string; last?: { at: number; sent: number } };\n\n/**\n * The state of an upload queue around the app's `upload` function: `add`, `pause`, `resume`, `retry`, `cancel`, `remove`.\n * The line itself — concurrency, retries with backoff, pause by aborting the signal — is `createQueue` of\n * `@krizaka/pacer`; this hook adds the files, their previews, their progress and their speed.\n */\nexport function useUploadQueue(options: UploadQueueOptions) {\n const { concurrency, retries, backoff, shouldRetry } = options;\n const latest = useLatest(options);\n const queue = useQueue<UploadMeta>({ concurrency: concurrency ?? 2, retries, backoff, shouldRetry });\n const jobs = useQueueSnapshot(queue);\n const metas = useLatest(new Map(jobs.map((job) => [job.id, job.meta])));\n\n const add = React.useCallback(\n (files: readonly File[]) => {\n const { previews = true } = latest.current;\n for (const file of files) {\n const id = `${file.name}-${file.size}-${Math.random().toString(36).slice(2, 8)}`;\n const previewUrl = previews && typeof URL.createObjectURL === \"function\" ? URL.createObjectURL(file) : undefined;\n queue.add(\n async ({ signal, resumed }) => {\n queue.update(id, (m) => ({ ...m, last: { at: Date.now(), sent: m.progress * file.size } }));\n try {\n await latest.current.upload(file, {\n signal,\n resumed,\n onProgress: (sent) => {\n if (signal.aborted) return;\n queue.update(id, (m) => {\n const now = Date.now();\n const last = m.last ?? { at: now, sent };\n const fresh = now - last.at > 400;\n return { ...m, progress: file.size ? Math.min(1, sent / file.size) : 0, speed: fresh ? ((sent - last.sent) / (now - last.at)) * 1000 : m.speed, last: fresh ? { at: now, sent } : last };\n });\n },\n });\n if (!signal.aborted) queue.update(id, (m) => ({ ...m, progress: 1, speed: undefined }));\n } catch (error) {\n if (!signal.aborted) queue.update(id, (m) => ({ ...m, speed: undefined }));\n throw error;\n }\n },\n { id, meta: { file, progress: 0, previewUrl } },\n );\n }\n },\n [latest, queue],\n );\n const forget = React.useCallback(\n (id: string) => {\n const url = metas.current.get(id)?.previewUrl;\n if (url) URL.revokeObjectURL(url);\n queue.cancel(id);\n },\n [metas, queue],\n );\n React.useEffect(\n () => () => {\n for (const meta of metas.current.values()) if (meta.previewUrl) URL.revokeObjectURL(meta.previewUrl);\n },\n [metas],\n );\n const pause = React.useCallback(\n (id: string) => {\n queue.pause(id);\n queue.update(id, (m) => ({ ...m, speed: undefined }));\n },\n [queue],\n );\n\n const items = React.useMemo<UploadItem[]>(\n () =>\n jobs.map(({ id, status, error, meta }) => ({\n id,\n name: meta.file.name,\n size: meta.file.size,\n type: meta.file.type,\n progress: meta.progress,\n status: status === \"running\" ? \"uploading\" : status,\n error: error?.message,\n previewUrl: meta.previewUrl,\n speed: status === \"running\" ? meta.speed : undefined,\n })),\n [jobs],\n );\n return { items, add, pause, resume: queue.resume, retry: queue.retry, cancel: forget, remove: forget };\n}\n","// Server-safe: no hook, no context. role=\"progressbar\" with aria-valuenow / aria-valuemax (none when indeterminate),\n// named by `label`. A bar or a ring; the ring can hold content in its centre (an amount, a percentage).\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const progress = tv({\n slots: {\n root: \"relative\",\n track: \"\",\n indicator: \"\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n buffer: \"absolute inset-y-0 left-0 -z-10 rounded-sm bg-accent/25 transition-[width] duration-500 motion-reduce:transition-none\",\n frame: \"flex w-full flex-col gap-1.5\",\n header: \"flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-xs\",\n caption: \"max-w-full shrink-0 grow truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n },\n variants: {\n variant: {\n bar: {\n root: \"relative isolate w-full overflow-hidden rounded-sm bg-surface-3\",\n indicator:\n \"kz-progress h-full rounded-sm bg-linear-to-r from-accent to-accent-2 transition-[width] duration-500 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:w-2/5\",\n },\n ring: {\n root: \"inline-flex shrink-0 items-center justify-center\",\n track: \"stroke-border-default\",\n indicator:\n \"stroke-accent transition-[stroke-dashoffset] duration-1000 motion-reduce:transition-none \" +\n \"data-[state=indeterminate]:origin-center data-[state=indeterminate]:animate-spin motion-reduce:data-[state=indeterminate]:animate-none\",\n },\n },\n size: { sm: {}, md: {}, lg: {} },\n status: {\n active: {},\n // Paused: the fill rests, striped — the task is waiting for someone, not broken.\n paused: { indicator: \"kz-progress-paused opacity-70\" },\n // Failed: the fill turns to the danger colour where it stopped.\n error: { indicator: \"from-danger to-danger stroke-danger\" },\n },\n },\n compoundVariants: [\n { variant: \"bar\", size: \"sm\", class: { root: \"h-1\" } },\n { variant: \"bar\", size: \"md\", class: { root: \"h-2\" } },\n { variant: \"bar\", size: \"lg\", class: { root: \"h-3\" } },\n { variant: \"ring\", size: \"sm\", class: { root: \"h-10 w-10\" } },\n { variant: \"ring\", size: \"md\", class: { root: \"h-20 w-20\" } },\n { variant: \"ring\", size: \"lg\", class: { root: \"h-36 w-36\" } },\n ],\n defaultVariants: { variant: \"bar\", size: \"md\", status: \"active\" },\n});\n\nexport type ProgressVariants = VariantProps<typeof progress>;\n\nexport type ProgressProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n ProgressVariants & {\n /** bar · ring (a ring holds its `children` in the centre). */\n variant?: ProgressVariants[\"variant\"];\n /** sm · md · lg: the bar's thickness, the ring's diameter. */\n size?: ProgressVariants[\"size\"];\n /** How far, between 0 and `max`. `null` or absent: indeterminate (a wait of unknown length). */\n value?: number | null;\n /** The value that means complete. */\n max?: number;\n /** The accessible name — passed translated (\"Upload\", \"Raised towards the goal\"). */\n label: string;\n /** The value in words for assistive technology (\"$420 of $1,000\") — default: the percentage. */\n valueText?: string;\n /** A ring's centre: an amount, a percentage, an icon. */\n children?: React.ReactNode;\n /** How far is already available beyond the value (a video buffered ahead), between 0 and `max` — bar only. */\n buffer?: number;\n /** Splits the bar or the ring into this many segments: the steps of a task, the parts of a goal. */\n segments?: number;\n /**\n * active · paused (the fill rests, striped: waiting for someone) · error (the fill turns to the danger colour where it stopped).\n * @default \"active\"\n */\n status?: ProgressVariants[\"status\"];\n /** Shows the label above the bar, with the percentage (or `valueText`) and `eta` — bar only. */\n showLabel?: boolean;\n /** The time left, in words (“About 2 min left”), shown with `showLabel` and read with the value — passed translated. */\n eta?: string;\n };\n\nconst STROKE = { sm: 12, md: 9, lg: 7 } as const;\n\n/** A progress bar or ring: `value` / `max`, indeterminate without a value. State: `data-state` (loading · complete · indeterminate). */\nexport function Progress({\n variant = \"bar\",\n size = \"md\",\n value,\n max = 100,\n label,\n valueText,\n className,\n children,\n buffer,\n segments,\n status = \"active\",\n showLabel,\n eta,\n ...props\n}: ProgressProps) {\n const s = progress({ variant, size, status });\n const maskId = `kz-seg-${React.useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n const known = typeof value === \"number\" && Number.isFinite(value);\n const ratio = known ? Math.min(1, Math.max(0, value / (max || 1))) : 0;\n const percent = Math.round(ratio * 100);\n const state = !known ? \"indeterminate\" : ratio >= 1 ? \"complete\" : \"loading\";\n const aria = {\n role: \"progressbar\" as const,\n \"aria-label\": label,\n \"aria-valuemin\": known ? 0 : undefined,\n \"aria-valuemax\": known ? max : undefined,\n \"aria-valuenow\": known ? Math.min(max, Math.max(0, value)) : undefined,\n \"aria-valuetext\": [known ? (valueText ?? `${percent}%`) : valueText, eta].filter(Boolean).join(\", \") || undefined,\n \"data-state\": state,\n \"data-status\": status === \"active\" ? undefined : status,\n \"data-value\": known ? value : undefined,\n \"data-variant\": variant,\n };\n if (variant === \"ring\") {\n const stroke = STROKE[size];\n const r = 50 - stroke / 2;\n return (\n <div {...aria} className={s.root({ className })} {...props}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n {segments && segments > 1 && (\n <mask id={maskId}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" stroke=\"white\" strokeWidth={stroke + 2} pathLength={100} strokeDasharray={`${100 / segments - 2} 2`} />\n </mask>\n )}\n <g mask={segments && segments > 1 ? `url(#${maskId})` : undefined}>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth={stroke} className={s.track()} />\n <circle\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth={stroke}\n strokeLinecap=\"round\"\n pathLength={100}\n strokeDasharray={100}\n strokeDashoffset={known ? 100 - percent : 75}\n data-state={state}\n className={s.indicator()}\n />\n </g>\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n );\n }\n const segmented = segments && segments > 1 ? ({ \"--kz-segments\": segments } as React.CSSProperties) : undefined;\n const bufferRatio = typeof buffer === \"number\" && Number.isFinite(buffer) ? Math.min(1, Math.max(0, buffer / (max || 1))) : null;\n const bar = (\n <div {...aria} className={s.root({ className: [segmented && \"kz-progress-segmented\", className].filter(Boolean).join(\" \") || undefined })} style={segmented} {...props}>\n <div data-state={state} className={s.indicator()} style={known ? { width: `${ratio * 100}%` } : undefined} />\n {bufferRatio !== null && <div aria-hidden className={s.buffer()} style={{ width: `${bufferRatio * 100}%` }} />}\n </div>\n );\n if (!showLabel) return bar;\n const figure = [known ? (valueText ?? `${percent} %`) : valueText, eta].filter(Boolean).join(\" · \");\n return (\n <div className={s.frame()}>\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {figure && <span className={s.figure()}>{figure}</span>}\n </div>\n {bar}\n </div>\n );\n}\n"],"mappings":"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","names":["React","tv","jsx","jsxs","tv"]}
@@ -5,8 +5,8 @@ import {
5
5
  filterBar,
6
6
  readFilterParams,
7
7
  writeFilterParams
8
- } from "./chunk-YOTQD7JD.js";
9
- import "./chunk-7UCPGXKN.js";
8
+ } from "./chunk-WYQ277F2.js";
9
+ import "./chunk-C3NF7V5Y.js";
10
10
  import "./chunk-D3T54RSL.js";
11
11
  import "./chunk-J3NC6ER4.js";
12
12
  import "./chunk-NSLXEDIS.js";
package/dist/genui.js CHANGED
@@ -4,16 +4,16 @@ import {
4
4
  CardRoot,
5
5
  CardTitle,
6
6
  Skeleton
7
- } from "./chunk-2DGLWTPS.js";
7
+ } from "./chunk-YKOLME7R.js";
8
8
  import {
9
9
  DataTable
10
- } from "./chunk-K2ZYB4HP.js";
10
+ } from "./chunk-S5LP7KKW.js";
11
11
  import "./chunk-YGIN2YIY.js";
12
12
  import "./chunk-JXLDUDTD.js";
13
13
  import "./chunk-3DHNN5V7.js";
14
14
  import "./chunk-KVDB4GXV.js";
15
- import "./chunk-YOTQD7JD.js";
16
- import "./chunk-7UCPGXKN.js";
15
+ import "./chunk-WYQ277F2.js";
16
+ import "./chunk-C3NF7V5Y.js";
17
17
  import "./chunk-D3T54RSL.js";
18
18
  import "./chunk-J3NC6ER4.js";
19
19
  import {
@@ -13,12 +13,7 @@ function LandingPage({ eyebrow, title, lead, actions, visual, visualLabel, proof
13
13
  actions && /* @__PURE__ */ jsx("div", { className: "kz-tpl-actions kz-hero-extra", children: actions })
14
14
  ] }),
15
15
  /* @__PURE__ */ jsxs("figure", { className: "kz-tpl-stage", children: [
16
- /* @__PURE__ */ jsxs("div", { className: "kz-tpl-stage-bar", "aria-hidden": true, children: [
17
- /* @__PURE__ */ jsx("i", {}),
18
- /* @__PURE__ */ jsx("i", {}),
19
- /* @__PURE__ */ jsx("i", {}),
20
- visualLabel && /* @__PURE__ */ jsx("span", { children: visualLabel })
21
- ] }),
16
+ /* @__PURE__ */ jsx("div", { className: "kz-tpl-stage-bar", "aria-hidden": true, children: visualLabel && /* @__PURE__ */ jsx("span", { children: visualLabel }) }),
22
17
  visual
23
18
  ] })
24
19
  ] }) }) }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/landing-page/landing-page.tsx"],"sourcesContent":["// Server-safe. A landing page (blocks.css `.kz-tpl-*`): a split hero — the promise and its actions beside the product\n// alive (a recording, a demo) — a band of proofs, the features as a bento where the main one takes the room, then the\n// closing call. The blocks come through their package entries, so their client parts keep their boundary.\nimport { CtaPanel } from \"@krizaka/ui/cta-panel\";\nimport { Page, Section } from \"@krizaka/ui/page\";\nimport { SectionBackdrop } from \"@krizaka/ui/section-backdrop\";\nimport type * as React from \"react\";\n\nexport interface LandingFeature {\n /** A stable key. */\n id: string;\n /** Its name. */\n title: React.ReactNode;\n /** One or two sentences. */\n text: React.ReactNode;\n /** A signature icon above the title. */\n icon?: React.ReactNode;\n /** A visual at the bottom of the tile (a chart, a screenshot, a code line). */\n visual?: React.ReactNode;\n /**\n * Its weight in the bento: `lg` (four columns, two rows — the main one), `md` (three), `sm` (two).\n * @default \"sm\"\n */\n size?: \"lg\" | \"md\" | \"sm\";\n}\n\nexport type LandingPageProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** A mono line above the title. */\n eyebrow?: React.ReactNode;\n /** The promise: the page's `h1`. */\n title: React.ReactNode;\n /** One or two sentences under it. */\n lead?: React.ReactNode;\n /** The hero's actions (the main one cut, with shimmer). */\n actions?: React.ReactNode;\n /** The product, alive, beside the words: a recording, a live demo (framed as a window). */\n visual: React.ReactNode;\n /** The window's caption (`orochia.app — studio`). */\n visualLabel?: React.ReactNode;\n /** Figures or facts in a band under the hero: `{ value, label }`. */\n proofs?: readonly { value: React.ReactNode; label: React.ReactNode }[];\n /** The features' section: its eyebrow, title and lead. */\n featuresHeading?: { eyebrow?: React.ReactNode; title: React.ReactNode; lead?: React.ReactNode };\n /** The features, as a bento. */\n features: readonly LandingFeature[];\n /** Between the features and the closing call: a story, a quote, testimonials. */\n children?: React.ReactNode;\n /** The closing call: its title, body and actions. */\n closing?: { title: React.ReactNode; body?: React.ReactNode; actions: React.ReactNode };\n};\n\n/** A landing page: split hero with the product alive, proofs, a bento of features, the closing call. */\nexport function LandingPage({ eyebrow, title, lead, actions, visual, visualLabel, proofs, featuresHeading, features, closing, children, ...props }: LandingPageProps) {\n return (\n <div {...props}>\n <SectionBackdrop as=\"header\" grid className=\"kz-hero\">\n <Page className=\"kz-tpl-hero-frame\">\n <div className=\"kz-tpl-split\">\n <div className=\"kz-hero-text\">\n {eyebrow && <p className=\"kz-eyebrow\">{eyebrow}</p>}\n <h1 className=\"kz-hero-title\">{title}</h1>\n {lead && <p className=\"kz-hero-lead\">{lead}</p>}\n {actions && <div className=\"kz-tpl-actions kz-hero-extra\">{actions}</div>}\n </div>\n <figure className=\"kz-tpl-stage\">\n <div className=\"kz-tpl-stage-bar\" aria-hidden>\n <i />\n <i />\n <i />\n {visualLabel && <span>{visualLabel}</span>}\n </div>\n {visual}\n </figure>\n </div>\n </Page>\n </SectionBackdrop>\n {proofs && proofs.length > 0 && (\n <Page>\n <dl className=\"kz-tpl-band\">\n {proofs.map((p, i) => (\n <div key={i}>\n <dt>{p.value}</dt>\n <dd>{p.label}</dd>\n </div>\n ))}\n </dl>\n </Page>\n )}\n <Section id=\"features\" eyebrow={featuresHeading?.eyebrow} title={featuresHeading?.title} lead={featuresHeading?.lead} reveal aria-label={featuresHeading ? undefined : \"Features\"}>\n <ul className=\"kz-tpl-bento\">\n {features.map((f) => (\n <li key={f.id} data-size={f.size === \"sm\" ? undefined : f.size} data-reveal=\"\">\n {f.icon && <span className=\"kz-block-icon\" aria-hidden>{f.icon}</span>}\n <h3>{f.title}</h3>\n <p>{f.text}</p>\n {f.visual && <div className=\"kz-tpl-bento-visual\">{f.visual}</div>}\n </li>\n ))}\n </ul>\n </Section>\n {children}\n {closing && (\n <Section spacing=\"sm\" aria-labelledby=\"landing-closing\">\n <CtaPanel id=\"landing-closing\" title={closing.title} body={closing.body} reveal>\n {closing.actions}\n </CtaPanel>\n </Section>\n )}\n </div>\n );\n}\n"],"mappings":";AAGA,SAAS,gBAAgB;AACzB,SAAS,MAAM,eAAe;AAC9B,SAAS,uBAAuB;AAqDpB,SACc,KADd;AANL,SAAS,YAAY,EAAE,SAAS,OAAO,MAAM,SAAS,QAAQ,aAAa,QAAQ,iBAAiB,UAAU,SAAS,UAAU,GAAG,MAAM,GAAqB;AACpK,SACE,qBAAC,SAAK,GAAG,OACP;AAAA,wBAAC,mBAAgB,IAAG,UAAS,MAAI,MAAC,WAAU,WAC1C,8BAAC,QAAK,WAAU,qBACd,+BAAC,SAAI,WAAU,gBACb;AAAA,2BAAC,SAAI,WAAU,gBACZ;AAAA,mBAAW,oBAAC,OAAE,WAAU,cAAc,mBAAQ;AAAA,QAC/C,oBAAC,QAAG,WAAU,iBAAiB,iBAAM;AAAA,QACpC,QAAQ,oBAAC,OAAE,WAAU,gBAAgB,gBAAK;AAAA,QAC1C,WAAW,oBAAC,SAAI,WAAU,gCAAgC,mBAAQ;AAAA,SACrE;AAAA,MACA,qBAAC,YAAO,WAAU,gBAChB;AAAA,6BAAC,SAAI,WAAU,oBAAmB,eAAW,MAC3C;AAAA,8BAAC,OAAE;AAAA,UACH,oBAAC,OAAE;AAAA,UACH,oBAAC,OAAE;AAAA,UACF,eAAe,oBAAC,UAAM,uBAAY;AAAA,WACrC;AAAA,QACC;AAAA,SACH;AAAA,OACF,GACF,GACF;AAAA,IACC,UAAU,OAAO,SAAS,KACzB,oBAAC,QACC,8BAAC,QAAG,WAAU,eACX,iBAAO,IAAI,CAAC,GAAG,MACd,qBAAC,SACC;AAAA,0BAAC,QAAI,YAAE,OAAM;AAAA,MACb,oBAAC,QAAI,YAAE,OAAM;AAAA,SAFL,CAGV,CACD,GACH,GACF;AAAA,IAEF,oBAAC,WAAQ,IAAG,YAAW,SAAS,iBAAiB,SAAS,OAAO,iBAAiB,OAAO,MAAM,iBAAiB,MAAM,QAAM,MAAC,cAAY,kBAAkB,SAAY,YACrK,8BAAC,QAAG,WAAU,gBACX,mBAAS,IAAI,CAAC,MACb,qBAAC,QAAc,aAAW,EAAE,SAAS,OAAO,SAAY,EAAE,MAAM,eAAY,IACzE;AAAA,QAAE,QAAQ,oBAAC,UAAK,WAAU,iBAAgB,eAAW,MAAE,YAAE,MAAK;AAAA,MAC/D,oBAAC,QAAI,YAAE,OAAM;AAAA,MACb,oBAAC,OAAG,YAAE,MAAK;AAAA,MACV,EAAE,UAAU,oBAAC,SAAI,WAAU,uBAAuB,YAAE,QAAO;AAAA,SAJrD,EAAE,EAKX,CACD,GACH,GACF;AAAA,IACC;AAAA,IACA,WACC,oBAAC,WAAQ,SAAQ,MAAK,mBAAgB,mBACpC,8BAAC,YAAS,IAAG,mBAAkB,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,QAAM,MAC5E,kBAAQ,SACX,GACF;AAAA,KAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/landing-page/landing-page.tsx"],"sourcesContent":["// Server-safe. A landing page (blocks.css `.kz-tpl-*`): a split hero — the promise and its actions beside the product\n// alive (a recording, a demo) — a band of proofs, the features as a bento where the main one takes the room, then the\n// closing call. The blocks come through their package entries, so their client parts keep their boundary.\nimport { CtaPanel } from \"@krizaka/ui/cta-panel\";\nimport { Page, Section } from \"@krizaka/ui/page\";\nimport { SectionBackdrop } from \"@krizaka/ui/section-backdrop\";\nimport type * as React from \"react\";\n\nexport interface LandingFeature {\n /** A stable key. */\n id: string;\n /** Its name. */\n title: React.ReactNode;\n /** One or two sentences. */\n text: React.ReactNode;\n /** A signature icon above the title. */\n icon?: React.ReactNode;\n /** A visual at the bottom of the tile (a chart, a screenshot, a code line). */\n visual?: React.ReactNode;\n /**\n * Its weight in the bento: `lg` (four columns, two rows — the main one), `md` (three), `sm` (two).\n * @default \"sm\"\n */\n size?: \"lg\" | \"md\" | \"sm\";\n}\n\nexport type LandingPageProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** A mono line above the title. */\n eyebrow?: React.ReactNode;\n /** The promise: the page's `h1`. */\n title: React.ReactNode;\n /** One or two sentences under it. */\n lead?: React.ReactNode;\n /** The hero's actions (the main one cut, with shimmer). */\n actions?: React.ReactNode;\n /** The product, alive, beside the words: a recording, a live demo (framed as a window). */\n visual: React.ReactNode;\n /** The window's caption (`orochia.app — studio`). */\n visualLabel?: React.ReactNode;\n /** Figures or facts in a band under the hero: `{ value, label }`. */\n proofs?: readonly { value: React.ReactNode; label: React.ReactNode }[];\n /** The features' section: its eyebrow, title and lead. */\n featuresHeading?: { eyebrow?: React.ReactNode; title: React.ReactNode; lead?: React.ReactNode };\n /** The features, as a bento. */\n features: readonly LandingFeature[];\n /** Between the features and the closing call: a story, a quote, testimonials. */\n children?: React.ReactNode;\n /** The closing call: its title, body and actions. */\n closing?: { title: React.ReactNode; body?: React.ReactNode; actions: React.ReactNode };\n};\n\n/** A landing page: split hero with the product alive, proofs, a bento of features, the closing call. */\nexport function LandingPage({ eyebrow, title, lead, actions, visual, visualLabel, proofs, featuresHeading, features, closing, children, ...props }: LandingPageProps) {\n return (\n <div {...props}>\n <SectionBackdrop as=\"header\" grid className=\"kz-hero\">\n <Page className=\"kz-tpl-hero-frame\">\n <div className=\"kz-tpl-split\">\n <div className=\"kz-hero-text\">\n {eyebrow && <p className=\"kz-eyebrow\">{eyebrow}</p>}\n <h1 className=\"kz-hero-title\">{title}</h1>\n {lead && <p className=\"kz-hero-lead\">{lead}</p>}\n {actions && <div className=\"kz-tpl-actions kz-hero-extra\">{actions}</div>}\n </div>\n <figure className=\"kz-tpl-stage\">\n <div className=\"kz-tpl-stage-bar\" aria-hidden>\n {visualLabel && <span>{visualLabel}</span>}\n </div>\n {visual}\n </figure>\n </div>\n </Page>\n </SectionBackdrop>\n {proofs && proofs.length > 0 && (\n <Page>\n <dl className=\"kz-tpl-band\">\n {proofs.map((p, i) => (\n <div key={i}>\n <dt>{p.value}</dt>\n <dd>{p.label}</dd>\n </div>\n ))}\n </dl>\n </Page>\n )}\n <Section id=\"features\" eyebrow={featuresHeading?.eyebrow} title={featuresHeading?.title} lead={featuresHeading?.lead} reveal aria-label={featuresHeading ? undefined : \"Features\"}>\n <ul className=\"kz-tpl-bento\">\n {features.map((f) => (\n <li key={f.id} data-size={f.size === \"sm\" ? undefined : f.size} data-reveal=\"\">\n {f.icon && <span className=\"kz-block-icon\" aria-hidden>{f.icon}</span>}\n <h3>{f.title}</h3>\n <p>{f.text}</p>\n {f.visual && <div className=\"kz-tpl-bento-visual\">{f.visual}</div>}\n </li>\n ))}\n </ul>\n </Section>\n {children}\n {closing && (\n <Section spacing=\"sm\" aria-labelledby=\"landing-closing\">\n <CtaPanel id=\"landing-closing\" title={closing.title} body={closing.body} reveal>\n {closing.actions}\n </CtaPanel>\n </Section>\n )}\n </div>\n );\n}\n"],"mappings":";AAGA,SAAS,gBAAgB;AACzB,SAAS,MAAM,eAAe;AAC9B,SAAS,uBAAuB;AAqDpB,SACc,KADd;AANL,SAAS,YAAY,EAAE,SAAS,OAAO,MAAM,SAAS,QAAQ,aAAa,QAAQ,iBAAiB,UAAU,SAAS,UAAU,GAAG,MAAM,GAAqB;AACpK,SACE,qBAAC,SAAK,GAAG,OACP;AAAA,wBAAC,mBAAgB,IAAG,UAAS,MAAI,MAAC,WAAU,WAC1C,8BAAC,QAAK,WAAU,qBACd,+BAAC,SAAI,WAAU,gBACb;AAAA,2BAAC,SAAI,WAAU,gBACZ;AAAA,mBAAW,oBAAC,OAAE,WAAU,cAAc,mBAAQ;AAAA,QAC/C,oBAAC,QAAG,WAAU,iBAAiB,iBAAM;AAAA,QACpC,QAAQ,oBAAC,OAAE,WAAU,gBAAgB,gBAAK;AAAA,QAC1C,WAAW,oBAAC,SAAI,WAAU,gCAAgC,mBAAQ;AAAA,SACrE;AAAA,MACA,qBAAC,YAAO,WAAU,gBAChB;AAAA,4BAAC,SAAI,WAAU,oBAAmB,eAAW,MAC1C,yBAAe,oBAAC,UAAM,uBAAY,GACrC;AAAA,QACC;AAAA,SACH;AAAA,OACF,GACF,GACF;AAAA,IACC,UAAU,OAAO,SAAS,KACzB,oBAAC,QACC,8BAAC,QAAG,WAAU,eACX,iBAAO,IAAI,CAAC,GAAG,MACd,qBAAC,SACC;AAAA,0BAAC,QAAI,YAAE,OAAM;AAAA,MACb,oBAAC,QAAI,YAAE,OAAM;AAAA,SAFL,CAGV,CACD,GACH,GACF;AAAA,IAEF,oBAAC,WAAQ,IAAG,YAAW,SAAS,iBAAiB,SAAS,OAAO,iBAAiB,OAAO,MAAM,iBAAiB,MAAM,QAAM,MAAC,cAAY,kBAAkB,SAAY,YACrK,8BAAC,QAAG,WAAU,gBACX,mBAAS,IAAI,CAAC,MACb,qBAAC,QAAc,aAAW,EAAE,SAAS,OAAO,SAAY,EAAE,MAAM,eAAY,IACzE;AAAA,QAAE,QAAQ,oBAAC,UAAK,WAAU,iBAAgB,eAAW,MAAE,YAAE,MAAK;AAAA,MAC/D,oBAAC,QAAI,YAAE,OAAM;AAAA,MACb,oBAAC,OAAG,YAAE,MAAK;AAAA,MACV,EAAE,UAAU,oBAAC,SAAI,WAAU,uBAAuB,YAAE,QAAO;AAAA,SAJrD,EAAE,EAKX,CACD,GACH,GACF;AAAA,IACC;AAAA,IACA,WACC,oBAAC,WAAQ,SAAQ,MAAK,mBAAgB,mBACpC,8BAAC,YAAS,IAAG,mBAAkB,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,QAAM,MAC5E,kBAAQ,SACX,GACF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  LanguageSwitch,
4
4
  languageSwitch
5
- } from "./chunk-63KCDMX2.js";
5
+ } from "./chunk-VPO27AFS.js";
6
6
 
7
7
  // src/language-switch/href.ts
8
8
  var languageNames = {
@@ -2,7 +2,7 @@
2
2
  import { tv } from "tailwind-variants";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  var skipLink = tv({
5
- base: "sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-full focus:bg-surface-1 focus:px-4 focus:py-2 focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
5
+ base: "sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-sm focus:bg-surface-1 focus:px-4 focus:py-2 focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
6
6
  });
7
7
  function SkipLink({ href = "#content", className, onClick, children, ...props }) {
8
8
  const handleClick = (event) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/skip-link/skip-link.tsx","../src/marketing-layout/marketing-layout.tsx"],"sourcesContent":["// Client (it moves focus). WCAG 2.4.1 Bypass Blocks: the first tab stop of a page, a link that skips the navigation and\n// lands on the content. Visually hidden until focused, then a pill above everything in the brand ring. The label is\n// passed translated; the target needs an `id` (and is made focusable, so focus really moves, router or not).\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const skipLink = tv({\n base:\n \"sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-full focus:bg-surface-1 focus:px-4 focus:py-2 \" +\n \"focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n});\n\nexport type SkipLinkProps = Omit<React.ComponentProps<\"a\">, \"href\"> & {\n /**\n * The content to land on: `#` and its `id`. The element becomes focusable (`tabindex=\"-1\"`) when it is not.\n * @default \"#content\"\n */\n href?: `#${string}`;\n};\n\n/** A \"skip to content\" link: the first tab stop, hidden until focused; Enter moves focus to the target. */\nexport function SkipLink({ href = \"#content\", className, onClick, children, ...props }: SkipLinkProps) {\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n const target = document.getElementById(decodeURIComponent(href.slice(1)));\n if (!target) return;\n if (!target.hasAttribute(\"tabindex\") && !target.matches(\"a[href], button, input, select, textarea, [contenteditable]\")) target.setAttribute(\"tabindex\", \"-1\");\n target.focus();\n };\n return (\n <a href={href} className={skipLink({ className })} onClick={handleClick} {...props}>\n {children}\n </a>\n );\n}\n","// Server-safe. The page of a site (krizaka.com, a product's public pages): a link to skip to the content, the site's\n// bar, the page — its PageHero and Sections (@krizaka/ui/page) — and the footer. The bar floats over the hero\n// (`--kz-hero-top` clears it); everything shares the page frame (`--kz-page-max`, `--kz-page-gutter`).\nimport { SkipLink } from \"@krizaka/ui/skip-link\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const marketingLayout = tv({\n slots: {\n root: \"flex min-h-dvh w-full flex-col bg-surface-0 text-fg\",\n header: \"fixed inset-x-0 top-3 z-50 px-(--kz-page-gutter,16px) pointer-events-none *:pointer-events-auto\",\n main: \"flex-1 outline-hidden\",\n },\n});\nconst s = marketingLayout();\n\nexport type MarketingLayoutProps = React.ComponentProps<\"div\"> & {\n /** The site's bar (a floating NavBar of @krizaka/ui/nav-bar). */\n header: React.ReactNode;\n /** The site's footer. */\n footer?: React.ReactNode;\n /** The words of the link that skips to the content (`Skip to content`). */\n skipLabel: string;\n /** The page: a PageHero, then Sections. */\n children: React.ReactNode;\n};\n\n/** A site's page: skip link, floating bar, the page, the footer. */\nexport function MarketingLayout({ header, footer, skipLabel, className, children, ...props }: MarketingLayoutProps) {\n return (\n <div className={s.root({ className })} {...props}>\n <SkipLink>{skipLabel}</SkipLink>\n <div className={s.header()}>{header}</div>\n <main id=\"content\" tabIndex={-1} className={s.main()}>\n {children}\n </main>\n {footer}\n </div>\n );\n}\n"],"mappings":";AAIA,SAAS,UAAU;AA2Bf;AAzBG,IAAM,WAAW,GAAG;AAAA,EACzB,MACE;AAEJ,CAAC;AAWM,SAAS,SAAS,EAAE,OAAO,YAAY,WAAW,SAAS,UAAU,GAAG,MAAM,GAAkB;AACrG,QAAM,cAAc,CAAC,UAA+C;AAClE,cAAU,KAAK;AACf,QAAI,MAAM,iBAAkB;AAC5B,UAAM,SAAS,SAAS,eAAe,mBAAmB,KAAK,MAAM,CAAC,CAAC,CAAC;AACxE,QAAI,CAAC,OAAQ;AACb,QAAI,CAAC,OAAO,aAAa,UAAU,KAAK,CAAC,OAAO,QAAQ,6DAA6D,EAAG,QAAO,aAAa,YAAY,IAAI;AAC5J,WAAO,MAAM;AAAA,EACf;AACA,SACE,oBAAC,OAAE,MAAY,WAAW,SAAS,EAAE,UAAU,CAAC,GAAG,SAAS,aAAc,GAAG,OAC1E,UACH;AAEJ;;;AC9BA,SAAS,MAAAA,WAAU;AAyBf,SACE,OAAAC,MADF;AAvBG,IAAM,kBAAkBD,IAAG;AAAA,EAChC,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AACF,CAAC;AACD,IAAM,IAAI,gBAAgB;AAcnB,SAAS,gBAAgB,EAAE,QAAQ,QAAQ,WAAW,WAAW,UAAU,GAAG,MAAM,GAAyB;AAClH,SACE,qBAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OACzC;AAAA,oBAAAC,KAAC,YAAU,qBAAU;AAAA,IACrB,gBAAAA,KAAC,SAAI,WAAW,EAAE,OAAO,GAAI,kBAAO;AAAA,IACpC,gBAAAA,KAAC,UAAK,IAAG,WAAU,UAAU,IAAI,WAAW,EAAE,KAAK,GAChD,UACH;AAAA,IACC;AAAA,KACH;AAEJ;","names":["tv","jsx"]}
1
+ {"version":3,"sources":["../src/skip-link/skip-link.tsx","../src/marketing-layout/marketing-layout.tsx"],"sourcesContent":["// Client (it moves focus). WCAG 2.4.1 Bypass Blocks: the first tab stop of a page, a link that skips the navigation and\n// lands on the content. Visually hidden until focused, then a pill above everything in the brand ring. The label is\n// passed translated; the target needs an `id` (and is made focusable, so focus really moves, router or not).\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const skipLink = tv({\n base:\n \"sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-sm focus:bg-surface-1 focus:px-4 focus:py-2 \" +\n \"focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n});\n\nexport type SkipLinkProps = Omit<React.ComponentProps<\"a\">, \"href\"> & {\n /**\n * The content to land on: `#` and its `id`. The element becomes focusable (`tabindex=\"-1\"`) when it is not.\n * @default \"#content\"\n */\n href?: `#${string}`;\n};\n\n/** A \"skip to content\" link: the first tab stop, hidden until focused; Enter moves focus to the target. */\nexport function SkipLink({ href = \"#content\", className, onClick, children, ...props }: SkipLinkProps) {\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n const target = document.getElementById(decodeURIComponent(href.slice(1)));\n if (!target) return;\n if (!target.hasAttribute(\"tabindex\") && !target.matches(\"a[href], button, input, select, textarea, [contenteditable]\")) target.setAttribute(\"tabindex\", \"-1\");\n target.focus();\n };\n return (\n <a href={href} className={skipLink({ className })} onClick={handleClick} {...props}>\n {children}\n </a>\n );\n}\n","// Server-safe. The page of a site (krizaka.com, a product's public pages): a link to skip to the content, the site's\n// bar, the page — its PageHero and Sections (@krizaka/ui/page) — and the footer. The bar floats over the hero\n// (`--kz-hero-top` clears it); everything shares the page frame (`--kz-page-max`, `--kz-page-gutter`).\nimport { SkipLink } from \"@krizaka/ui/skip-link\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const marketingLayout = tv({\n slots: {\n root: \"flex min-h-dvh w-full flex-col bg-surface-0 text-fg\",\n header: \"fixed inset-x-0 top-3 z-50 px-(--kz-page-gutter,16px) pointer-events-none *:pointer-events-auto\",\n main: \"flex-1 outline-hidden\",\n },\n});\nconst s = marketingLayout();\n\nexport type MarketingLayoutProps = React.ComponentProps<\"div\"> & {\n /** The site's bar (a floating NavBar of @krizaka/ui/nav-bar). */\n header: React.ReactNode;\n /** The site's footer. */\n footer?: React.ReactNode;\n /** The words of the link that skips to the content (`Skip to content`). */\n skipLabel: string;\n /** The page: a PageHero, then Sections. */\n children: React.ReactNode;\n};\n\n/** A site's page: skip link, floating bar, the page, the footer. */\nexport function MarketingLayout({ header, footer, skipLabel, className, children, ...props }: MarketingLayoutProps) {\n return (\n <div className={s.root({ className })} {...props}>\n <SkipLink>{skipLabel}</SkipLink>\n <div className={s.header()}>{header}</div>\n <main id=\"content\" tabIndex={-1} className={s.main()}>\n {children}\n </main>\n {footer}\n </div>\n );\n}\n"],"mappings":";AAIA,SAAS,UAAU;AA2Bf;AAzBG,IAAM,WAAW,GAAG;AAAA,EACzB,MACE;AAEJ,CAAC;AAWM,SAAS,SAAS,EAAE,OAAO,YAAY,WAAW,SAAS,UAAU,GAAG,MAAM,GAAkB;AACrG,QAAM,cAAc,CAAC,UAA+C;AAClE,cAAU,KAAK;AACf,QAAI,MAAM,iBAAkB;AAC5B,UAAM,SAAS,SAAS,eAAe,mBAAmB,KAAK,MAAM,CAAC,CAAC,CAAC;AACxE,QAAI,CAAC,OAAQ;AACb,QAAI,CAAC,OAAO,aAAa,UAAU,KAAK,CAAC,OAAO,QAAQ,6DAA6D,EAAG,QAAO,aAAa,YAAY,IAAI;AAC5J,WAAO,MAAM;AAAA,EACf;AACA,SACE,oBAAC,OAAE,MAAY,WAAW,SAAS,EAAE,UAAU,CAAC,GAAG,SAAS,aAAc,GAAG,OAC1E,UACH;AAEJ;;;AC9BA,SAAS,MAAAA,WAAU;AAyBf,SACE,OAAAC,MADF;AAvBG,IAAM,kBAAkBD,IAAG;AAAA,EAChC,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AACF,CAAC;AACD,IAAM,IAAI,gBAAgB;AAcnB,SAAS,gBAAgB,EAAE,QAAQ,QAAQ,WAAW,WAAW,UAAU,GAAG,MAAM,GAAyB;AAClH,SACE,qBAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OACzC;AAAA,oBAAAC,KAAC,YAAU,qBAAU;AAAA,IACrB,gBAAAA,KAAC,SAAI,WAAW,EAAE,OAAO,GAAI,kBAAO;AAAA,IACpC,gBAAAA,KAAC,UAAK,IAAG,WAAU,UAAU,IAAI,WAAW,EAAE,KAAK,GAChD,UACH;AAAA,IACC;AAAA,KACH;AAEJ;","names":["tv","jsx"]}
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  Card,
8
8
  Skeleton
9
- } from "./chunk-2DGLWTPS.js";
9
+ } from "./chunk-YKOLME7R.js";
10
10
  import "./chunk-JXLDUDTD.js";
11
11
  import {
12
12
  Button,
package/dist/meter.js CHANGED
@@ -7,8 +7,8 @@ var meter = tv({
7
7
  header: "flex items-baseline gap-2 text-xs",
8
8
  caption: "min-w-0 flex-1 truncate font-medium text-fg",
9
9
  figure: "shrink-0 tabular-nums text-fg-secondary",
10
- track: "relative flex h-2 w-full gap-0.5 overflow-hidden rounded-full bg-surface-3",
11
- fill: "h-full rounded-full transition-[width] duration-700 motion-reduce:transition-none",
10
+ track: "relative flex h-2 w-full gap-0.5 overflow-hidden rounded-sm bg-surface-3",
11
+ fill: "h-full rounded-sm transition-[width] duration-700 motion-reduce:transition-none",
12
12
  legend: "mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary",
13
13
  swatch: "inline-block h-2 w-2 shrink-0 rounded-full",
14
14
  ring: "relative inline-flex shrink-0 items-center justify-center",
package/dist/meter.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/meter/meter.tsx"],"sourcesContent":["// Server-safe. A measure within a known range — a quota, a balance, a share — as opposed to the progress of a task.\n// `role=\"meter\"`, its value in words; thresholds turn it to warning then danger (or the reverse when high is good);\n// split into parts (90 % to the creator, 10 % to the platform) it becomes a stacked bar or ring with its legend: the\n// text version of what it draws.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const meter = tv({\n slots: {\n root: \"flex w-full flex-col gap-1.5\",\n header: \"flex items-baseline gap-2 text-xs\",\n caption: \"min-w-0 flex-1 truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n track: \"relative flex h-2 w-full gap-0.5 overflow-hidden rounded-full bg-surface-3\",\n fill: \"h-full rounded-full transition-[width] duration-700 motion-reduce:transition-none\",\n legend: \"mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary\",\n swatch: \"inline-block h-2 w-2 shrink-0 rounded-full\",\n ring: \"relative inline-flex shrink-0 items-center justify-center\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n },\n variants: {\n size: { sm: { track: \"h-1.5\", ring: \"h-14 w-14\" }, md: { ring: \"h-24 w-24\" }, lg: { track: \"h-3\", ring: \"h-36 w-36\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\n/** The colour of a fill: by tone (thresholds) or by part (the accent, a neutral, accent-2, a lighter neutral). */\nconst BG = { ok: \"bg-accent\", success: \"bg-success\", warning: \"bg-warning\", danger: \"bg-danger\" } as const;\nconst STROKE = { ok: \"stroke-accent\", success: \"stroke-success\", warning: \"stroke-warning\", danger: \"stroke-danger\" } as const;\nconst PART_BG = [\"bg-accent\", \"bg-fg-secondary\", \"bg-accent-2\", \"bg-border-strong\"];\nconst PART_STROKE = [\"stroke-accent\", \"stroke-fg-secondary\", \"stroke-accent-2\", \"stroke-border-strong\"];\n\nexport interface MeterPart {\n /** Its name (“To the creator”) — passed translated. */\n label: string;\n /** Its value, in the meter's unit. */\n value: number;\n /** Its value in words (“€9.00”), in the legend and for assistive technology; default: its percentage. */\n valueText?: string;\n}\n\nexport type MeterProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The accessible name (“Storage”, “Split of a tip”) — passed translated; shown with `showLabel`. */\n label: string;\n /** The value, between `min` and `max` (ignored with `parts`). */\n value?: number;\n /**\n * The bottom of the range.\n * @default 0\n */\n min?: number;\n /**\n * The top of the range.\n * @default 100\n */\n max?: number;\n /** The value in words (“42 GB of 50 GB”) — passed translated; default: the percentage. */\n valueText?: string;\n /**\n * Ratios (0–1) where the tone changes: past `high` it is in danger, past `low` in warning — or the reverse with\n * `optimum=\"high\"` (a balance: the lower, the worse). Without thresholds the fill is the brand accent.\n */\n thresholds?: { readonly low: number; readonly high: number };\n /**\n * Which end is good.\n * @default \"low\"\n */\n optimum?: \"low\" | \"high\";\n /** Split into parts that add up to `max`: a stacked bar or ring, with its legend. */\n parts?: readonly MeterPart[];\n /**\n * bar · ring.\n * @default \"bar\"\n */\n variant?: \"bar\" | \"ring\";\n /**\n * sm · md · lg: the bar's thickness, the ring's diameter.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Shows the label and the value above the bar. */\n showLabel?: boolean;\n /** A ring's centre: the value, a word. */\n children?: React.ReactNode;\n};\n\n/** A measure within a range — a quota, a balance, a share — toned by thresholds or split into parts. */\nexport function Meter({\n label,\n value = 0,\n min = 0,\n max = 100,\n valueText,\n thresholds,\n optimum = \"low\",\n parts,\n variant = \"bar\",\n size = \"md\",\n showLabel,\n className,\n children,\n ...props\n}: MeterProps) {\n const s = meter({ size });\n const span = max - min || 1;\n const ratio = Math.min(1, Math.max(0, (value - min) / span));\n const level = !thresholds ? \"ok\" : ratio >= thresholds.high ? (optimum === \"low\" ? \"danger\" : \"success\") : ratio >= thresholds.low ? \"warning\" : optimum === \"low\" ? \"success\" : \"danger\";\n const words = valueText ?? `${Math.round(ratio * 100)} %`;\n const shares = (parts ?? []).map((p, i) => ({ ...p, i, ratio: Math.min(1, Math.max(0, p.value / span)), words: p.valueText ?? `${Math.round((p.value / span) * 100)} %` }));\n const a11y = parts\n ? { role: \"group\" as const, \"aria-label\": label }\n : { role: \"meter\" as const, \"aria-label\": label, \"aria-valuemin\": min, \"aria-valuemax\": max, \"aria-valuenow\": Math.min(max, Math.max(min, value)), \"aria-valuetext\": words };\n const legend = parts && (\n <ul className={s.legend()}>\n {shares.map((p) => (\n <li key={p.i} className=\"flex items-center gap-1.5\">\n <span aria-hidden className={[s.swatch(), PART_BG[p.i % PART_BG.length]].join(\" \")} />\n <span className=\"text-fg\">{p.label}</span>\n <span className=\"tabular-nums\">{p.words}</span>\n </li>\n ))}\n </ul>\n );\n\n if (variant === \"ring\") {\n const r = 44;\n let offset = 0;\n const arcs = parts\n ? shares.map((p) => {\n const arc = { cls: PART_STROKE[p.i % PART_STROKE.length]!, len: p.ratio * 100, start: offset };\n offset += arc.len;\n return arc;\n })\n : [{ cls: STROKE[level], len: ratio * 100, start: 0 }];\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={[\"inline-flex flex-col items-center gap-2\", className].filter(Boolean).join(\" \")} {...props}>\n <div className={s.ring()}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth=\"9\" className=\"stroke-border-default\" />\n {arcs.map((a, i) => (\n <circle\n key={i}\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth=\"9\"\n pathLength={100}\n strokeDasharray={`${Math.max(0, a.len - (parts ? 1 : 0))} ${100 - Math.max(0, a.len - (parts ? 1 : 0))}`}\n strokeDashoffset={-a.start}\n className={[a.cls, \"transition-[stroke-dasharray] duration-700 motion-reduce:transition-none\"].join(\" \")}\n />\n ))}\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n {legend}\n </div>\n );\n }\n\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={s.root({ className })} {...props}>\n {showLabel && (\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {!parts && <span className={s.figure()}>{words}</span>}\n </div>\n )}\n <div aria-hidden className={s.track()}>\n {parts ? (\n shares.map((p) => <span key={p.i} className={[s.fill(), PART_BG[p.i % PART_BG.length]].join(\" \")} style={{ width: `${p.ratio * 100}%` }} />)\n ) : (\n <span className={[s.fill(), BG[level]].join(\" \")} style={{ width: `${ratio * 100}%` }} />\n )}\n </div>\n {legend}\n </div>\n );\n}\n\nexport interface MeterValueProps {\n /** The figure. */\n value: React.ReactNode;\n /** A word under it. */\n caption?: React.ReactNode;\n}\n\n/** For the ring's centre when it shows a figure and a word. */\nexport function MeterValue({ value, caption }: MeterValueProps) {\n return (\n <>\n <span className=\"font-display text-lg font-semibold text-fg tabular-nums\">{value}</span>\n {caption && <span className=\"text-[10px] text-fg-secondary\">{caption}</span>}\n </>\n );\n}\n"],"mappings":";AAKA,SAAS,UAAU;AA8GX,SA4EJ,UA3EM,KADF;AA5GD,IAAM,QAAQ,GAAG;AAAA,EACtB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,EACV;AAAA,EACA,UAAU;AAAA,IACR,MAAM,EAAE,IAAI,EAAE,OAAO,SAAS,MAAM,YAAY,GAAG,IAAI,EAAE,MAAM,YAAY,GAAG,IAAI,EAAE,OAAO,OAAO,MAAM,YAAY,EAAE;AAAA,EACxH;AAAA,EACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;AAGD,IAAM,KAAK,EAAE,IAAI,aAAa,SAAS,cAAc,SAAS,cAAc,QAAQ,YAAY;AAChG,IAAM,SAAS,EAAE,IAAI,iBAAiB,SAAS,kBAAkB,SAAS,kBAAkB,QAAQ,gBAAgB;AACpH,IAAM,UAAU,CAAC,aAAa,mBAAmB,eAAe,kBAAkB;AAClF,IAAM,cAAc,CAAC,iBAAiB,uBAAuB,mBAAmB,sBAAsB;AAyD/F,SAAS,MAAM;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAe;AACb,QAAM,IAAI,MAAM,EAAE,KAAK,CAAC;AACxB,QAAM,OAAO,MAAM,OAAO;AAC1B,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,OAAO,IAAI,CAAC;AAC3D,QAAM,QAAQ,CAAC,aAAa,OAAO,SAAS,WAAW,OAAQ,YAAY,QAAQ,WAAW,YAAa,SAAS,WAAW,MAAM,YAAY,YAAY,QAAQ,YAAY;AACjL,QAAM,QAAQ,aAAa,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AACrD,QAAM,UAAU,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,GAAG,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAE,QAAQ,IAAI,CAAC,GAAG,OAAO,EAAE,aAAa,GAAG,KAAK,MAAO,EAAE,QAAQ,OAAQ,GAAG,CAAC,KAAK,EAAE;AAC1K,QAAM,OAAO,QACT,EAAE,MAAM,SAAkB,cAAc,MAAM,IAC9C,EAAE,MAAM,SAAkB,cAAc,OAAO,iBAAiB,KAAK,iBAAiB,KAAK,iBAAiB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,kBAAkB,MAAM;AAC7K,QAAM,SAAS,SACb,oBAAC,QAAG,WAAW,EAAE,OAAO,GACrB,iBAAO,IAAI,CAAC,MACX,qBAAC,QAAa,WAAU,6BACtB;AAAA,wBAAC,UAAK,eAAW,MAAC,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,IAAI,QAAQ,MAAM,CAAC,EAAE,KAAK,GAAG,GAAG;AAAA,IACpF,oBAAC,UAAK,WAAU,WAAW,YAAE,OAAM;AAAA,IACnC,oBAAC,UAAK,WAAU,gBAAgB,YAAE,OAAM;AAAA,OAHjC,EAAE,CAIX,CACD,GACH;AAGF,MAAI,YAAY,QAAQ;AACtB,UAAM,IAAI;AACV,QAAI,SAAS;AACb,UAAM,OAAO,QACT,OAAO,IAAI,CAAC,MAAM;AAChB,YAAM,MAAM,EAAE,KAAK,YAAY,EAAE,IAAI,YAAY,MAAM,GAAI,KAAK,EAAE,QAAQ,KAAK,OAAO,OAAO;AAC7F,gBAAU,IAAI;AACd,aAAO;AAAA,IACT,CAAC,IACD,CAAC,EAAE,KAAK,OAAO,KAAK,GAAG,KAAK,QAAQ,KAAK,OAAO,EAAE,CAAC;AACvD,WACE,qBAAC,SAAK,GAAG,MAAM,cAAY,QAAQ,SAAY,OAAO,WAAW,CAAC,2CAA2C,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,OACrJ;AAAA,2BAAC,SAAI,WAAW,EAAE,KAAK,GACrB;AAAA,6BAAC,SAAI,SAAQ,eAAc,WAAU,4BAA2B,eAAW,MACzE;AAAA,8BAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAM,MAAK,QAAO,aAAY,KAAI,WAAU,yBAAwB;AAAA,UAC3F,KAAK,IAAI,CAAC,GAAG,MACZ;AAAA,YAAC;AAAA;AAAA,cAEC,IAAG;AAAA,cACH,IAAG;AAAA,cACH;AAAA,cACA,MAAK;AAAA,cACL,aAAY;AAAA,cACZ,YAAY;AAAA,cACZ,iBAAiB,GAAG,KAAK,IAAI,GAAG,EAAE,OAAO,QAAQ,IAAI,EAAE,CAAC,IAAI,MAAM,KAAK,IAAI,GAAG,EAAE,OAAO,QAAQ,IAAI,EAAE,CAAC;AAAA,cACtG,kBAAkB,CAAC,EAAE;AAAA,cACrB,WAAW,CAAC,EAAE,KAAK,0EAA0E,EAAE,KAAK,GAAG;AAAA;AAAA,YATlG;AAAA,UAUP,CACD;AAAA,WACH;AAAA,QACC,aAAa,UAAa,oBAAC,SAAI,WAAW,EAAE,OAAO,GAAI,UAAS;AAAA,SACnE;AAAA,MACC;AAAA,OACH;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAK,GAAG,MAAM,cAAY,QAAQ,SAAY,OAAO,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OACzF;AAAA,iBACC,qBAAC,SAAI,eAAW,MAAC,WAAW,EAAE,OAAO,GACnC;AAAA,0BAAC,UAAK,WAAW,EAAE,QAAQ,GAAI,iBAAM;AAAA,MACpC,CAAC,SAAS,oBAAC,UAAK,WAAW,EAAE,OAAO,GAAI,iBAAM;AAAA,OACjD;AAAA,IAEF,oBAAC,SAAI,eAAW,MAAC,WAAW,EAAE,MAAM,GACjC,kBACC,OAAO,IAAI,CAAC,MAAM,oBAAC,UAAe,WAAW,CAAC,EAAE,KAAK,GAAG,QAAQ,EAAE,IAAI,QAAQ,MAAM,CAAC,EAAE,KAAK,GAAG,GAAG,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,GAAG,IAAI,KAAzG,EAAE,CAA0G,CAAE,IAE3I,oBAAC,UAAK,WAAW,CAAC,EAAE,KAAK,GAAG,GAAG,KAAK,CAAC,EAAE,KAAK,GAAG,GAAG,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,GAE3F;AAAA,IACC;AAAA,KACH;AAEJ;AAUO,SAAS,WAAW,EAAE,OAAO,QAAQ,GAAoB;AAC9D,SACE,iCACE;AAAA,wBAAC,UAAK,WAAU,2DAA2D,iBAAM;AAAA,IAChF,WAAW,oBAAC,UAAK,WAAU,iCAAiC,mBAAQ;AAAA,KACvE;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/meter/meter.tsx"],"sourcesContent":["// Server-safe. A measure within a known range — a quota, a balance, a share — as opposed to the progress of a task.\n// `role=\"meter\"`, its value in words; thresholds turn it to warning then danger (or the reverse when high is good);\n// split into parts (90 % to the creator, 10 % to the platform) it becomes a stacked bar or ring with its legend: the\n// text version of what it draws.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const meter = tv({\n slots: {\n root: \"flex w-full flex-col gap-1.5\",\n header: \"flex items-baseline gap-2 text-xs\",\n caption: \"min-w-0 flex-1 truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n track: \"relative flex h-2 w-full gap-0.5 overflow-hidden rounded-sm bg-surface-3\",\n fill: \"h-full rounded-sm transition-[width] duration-700 motion-reduce:transition-none\",\n legend: \"mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary\",\n swatch: \"inline-block h-2 w-2 shrink-0 rounded-full\",\n ring: \"relative inline-flex shrink-0 items-center justify-center\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n },\n variants: {\n size: { sm: { track: \"h-1.5\", ring: \"h-14 w-14\" }, md: { ring: \"h-24 w-24\" }, lg: { track: \"h-3\", ring: \"h-36 w-36\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\n/** The colour of a fill: by tone (thresholds) or by part (the accent, a neutral, accent-2, a lighter neutral). */\nconst BG = { ok: \"bg-accent\", success: \"bg-success\", warning: \"bg-warning\", danger: \"bg-danger\" } as const;\nconst STROKE = { ok: \"stroke-accent\", success: \"stroke-success\", warning: \"stroke-warning\", danger: \"stroke-danger\" } as const;\nconst PART_BG = [\"bg-accent\", \"bg-fg-secondary\", \"bg-accent-2\", \"bg-border-strong\"];\nconst PART_STROKE = [\"stroke-accent\", \"stroke-fg-secondary\", \"stroke-accent-2\", \"stroke-border-strong\"];\n\nexport interface MeterPart {\n /** Its name (“To the creator”) — passed translated. */\n label: string;\n /** Its value, in the meter's unit. */\n value: number;\n /** Its value in words (“€9.00”), in the legend and for assistive technology; default: its percentage. */\n valueText?: string;\n}\n\nexport type MeterProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The accessible name (“Storage”, “Split of a tip”) — passed translated; shown with `showLabel`. */\n label: string;\n /** The value, between `min` and `max` (ignored with `parts`). */\n value?: number;\n /**\n * The bottom of the range.\n * @default 0\n */\n min?: number;\n /**\n * The top of the range.\n * @default 100\n */\n max?: number;\n /** The value in words (“42 GB of 50 GB”) — passed translated; default: the percentage. */\n valueText?: string;\n /**\n * Ratios (0–1) where the tone changes: past `high` it is in danger, past `low` in warning — or the reverse with\n * `optimum=\"high\"` (a balance: the lower, the worse). Without thresholds the fill is the brand accent.\n */\n thresholds?: { readonly low: number; readonly high: number };\n /**\n * Which end is good.\n * @default \"low\"\n */\n optimum?: \"low\" | \"high\";\n /** Split into parts that add up to `max`: a stacked bar or ring, with its legend. */\n parts?: readonly MeterPart[];\n /**\n * bar · ring.\n * @default \"bar\"\n */\n variant?: \"bar\" | \"ring\";\n /**\n * sm · md · lg: the bar's thickness, the ring's diameter.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Shows the label and the value above the bar. */\n showLabel?: boolean;\n /** A ring's centre: the value, a word. */\n children?: React.ReactNode;\n};\n\n/** A measure within a range — a quota, a balance, a share — toned by thresholds or split into parts. */\nexport function Meter({\n label,\n value = 0,\n min = 0,\n max = 100,\n valueText,\n thresholds,\n optimum = \"low\",\n parts,\n variant = \"bar\",\n size = \"md\",\n showLabel,\n className,\n children,\n ...props\n}: MeterProps) {\n const s = meter({ size });\n const span = max - min || 1;\n const ratio = Math.min(1, Math.max(0, (value - min) / span));\n const level = !thresholds ? \"ok\" : ratio >= thresholds.high ? (optimum === \"low\" ? \"danger\" : \"success\") : ratio >= thresholds.low ? \"warning\" : optimum === \"low\" ? \"success\" : \"danger\";\n const words = valueText ?? `${Math.round(ratio * 100)} %`;\n const shares = (parts ?? []).map((p, i) => ({ ...p, i, ratio: Math.min(1, Math.max(0, p.value / span)), words: p.valueText ?? `${Math.round((p.value / span) * 100)} %` }));\n const a11y = parts\n ? { role: \"group\" as const, \"aria-label\": label }\n : { role: \"meter\" as const, \"aria-label\": label, \"aria-valuemin\": min, \"aria-valuemax\": max, \"aria-valuenow\": Math.min(max, Math.max(min, value)), \"aria-valuetext\": words };\n const legend = parts && (\n <ul className={s.legend()}>\n {shares.map((p) => (\n <li key={p.i} className=\"flex items-center gap-1.5\">\n <span aria-hidden className={[s.swatch(), PART_BG[p.i % PART_BG.length]].join(\" \")} />\n <span className=\"text-fg\">{p.label}</span>\n <span className=\"tabular-nums\">{p.words}</span>\n </li>\n ))}\n </ul>\n );\n\n if (variant === \"ring\") {\n const r = 44;\n let offset = 0;\n const arcs = parts\n ? shares.map((p) => {\n const arc = { cls: PART_STROKE[p.i % PART_STROKE.length]!, len: p.ratio * 100, start: offset };\n offset += arc.len;\n return arc;\n })\n : [{ cls: STROKE[level], len: ratio * 100, start: 0 }];\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={[\"inline-flex flex-col items-center gap-2\", className].filter(Boolean).join(\" \")} {...props}>\n <div className={s.ring()}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth=\"9\" className=\"stroke-border-default\" />\n {arcs.map((a, i) => (\n <circle\n key={i}\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth=\"9\"\n pathLength={100}\n strokeDasharray={`${Math.max(0, a.len - (parts ? 1 : 0))} ${100 - Math.max(0, a.len - (parts ? 1 : 0))}`}\n strokeDashoffset={-a.start}\n className={[a.cls, \"transition-[stroke-dasharray] duration-700 motion-reduce:transition-none\"].join(\" \")}\n />\n ))}\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n {legend}\n </div>\n );\n }\n\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={s.root({ className })} {...props}>\n {showLabel && (\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {!parts && <span className={s.figure()}>{words}</span>}\n </div>\n )}\n <div aria-hidden className={s.track()}>\n {parts ? (\n shares.map((p) => <span key={p.i} className={[s.fill(), PART_BG[p.i % PART_BG.length]].join(\" \")} style={{ width: `${p.ratio * 100}%` }} />)\n ) : (\n <span className={[s.fill(), BG[level]].join(\" \")} style={{ width: `${ratio * 100}%` }} />\n )}\n </div>\n {legend}\n </div>\n );\n}\n\nexport interface MeterValueProps {\n /** The figure. */\n value: React.ReactNode;\n /** A word under it. */\n caption?: React.ReactNode;\n}\n\n/** For the ring's centre when it shows a figure and a word. */\nexport function MeterValue({ value, caption }: MeterValueProps) {\n return (\n <>\n <span className=\"font-display text-lg font-semibold text-fg tabular-nums\">{value}</span>\n {caption && <span className=\"text-[10px] text-fg-secondary\">{caption}</span>}\n </>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/motion.css CHANGED
@@ -162,7 +162,7 @@
162
162
  .maplibregl-canvas-container.maplibregl-interactive { cursor: grab; }
163
163
  .maplibregl-canvas-container.maplibregl-interactive:active { cursor: grabbing; }
164
164
  .maplibregl-marker { position: absolute; top: 0; left: 0; will-change: transform; }
165
- .kz-map-user { position: relative; width: 16px; height: 16px; border-radius: 9999px; background: var(--kz-accent); border: 3px solid var(--kz-surface-1); box-shadow: 0 1px 4px var(--kz-scrim); }
165
+ .kz-map-user { position: relative; width: 16px; height: 16px; border-radius: 9999px; background: var(--kz-accent); border: 3px solid var(--kz-surface-1); box-shadow: var(--kz-shadow-md); }
166
166
  .kz-map-user::after { content: ""; position: absolute; inset: -3px; border-radius: inherit; background: var(--kz-accent); opacity: 0.35; animation: kz-map-pulse 2s var(--kz-ease) infinite; }
167
167
  /* A pin dropped onto its place (`Marker drop={delay}`): falls from above, settles with a small bounce; once. */
168
168
  [data-kz-marker] > [data-drop] { animation: kz-map-drop 620ms cubic-bezier(0.34, 1.4, 0.64, 1) backwards; animation-delay: var(--kz-map-drop-delay, 0ms); }
@@ -174,7 +174,7 @@
174
174
  .kz-map-pulse[data-tone="soft"]::before, .kz-map-pulse[data-tone="soft"]::after { border-width: 1px; background: color-mix(in oklab, var(--kz-accent) 10%, transparent); }
175
175
  @keyframes kz-map-heat { from { transform: scale(0.15); opacity: 0.9; } to { transform: scale(1); opacity: 0; } }
176
176
  /* The courier of a `Route`: an accent dot with a ring, moved along the line by the component. */
177
- .kz-map-courier { width: 14px; height: 14px; border-radius: 9999px; background: var(--kz-accent); border: 3px solid var(--kz-surface-1); box-shadow: 0 0 0 4px color-mix(in oklab, var(--kz-accent) 30%, transparent), 0 2px 6px var(--kz-scrim); }
177
+ .kz-map-courier { width: 14px; height: 14px; border-radius: 9999px; background: var(--kz-accent); border: 3px solid var(--kz-surface-1); box-shadow: 0 0 0 4px color-mix(in oklab, var(--kz-accent) 30%, transparent); }
178
178
  @keyframes kz-map-pulse { from { transform: scale(1); opacity: 0.4; } to { transform: scale(3.2); opacity: 0; } }
179
179
 
180
180
  /* Section backdrop (SectionBackdrop): the brand's section gradient (--kz-brand-gradient-*), sections that glide from
package/dist/native.js CHANGED
@@ -845,7 +845,7 @@ function defineRecipe(config) {
845
845
 
846
846
  // src/badge/recipe.ts
847
847
  var badgeRecipe = defineRecipe({
848
- base: "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-full font-semibold uppercase tracking-wider",
848
+ base: "inline-flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm font-semibold ",
849
849
  variants: {
850
850
  tone: {
851
851
  neutral: "bg-surface-3 text-fg-secondary",
@@ -1215,13 +1215,13 @@ var cardRecipe = defineRecipe({
1215
1215
  title: "line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-fg-accent",
1216
1216
  description: "line-clamp-2 text-xs text-fg-secondary",
1217
1217
  stat: "min-w-0",
1218
- statLabel: "text-[10px] font-semibold uppercase tracking-wider text-fg-secondary",
1218
+ statLabel: "font-mono text-[11.5px] text-fg-secondary",
1219
1219
  statValue: "font-display text-xl font-black tabular-nums text-fg",
1220
1220
  footer: "relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary"
1221
1221
  },
1222
1222
  variants: {
1223
1223
  radius: { md: { root: "rounded-md" }, lg: { root: "rounded-lg" }, xl: { root: "rounded-xl" } },
1224
- tone: { default: {}, elevated: { root: "bg-surface-2 shadow-md" }, glass: { root: "bg-surface-1/70 backdrop-blur-md" } },
1224
+ tone: { default: {}, elevated: { root: "bg-surface-2 shadow-md" }, glass: { root: "bg-glass border-glass-border" } },
1225
1225
  interactive: { true: { root: "duration-150 hover:border-border-strong hover:bg-surface-2" } },
1226
1226
  padding: { none: { body: "p-0" }, sm: { body: "p-3" }, md: { body: "p-4" }, lg: { body: "p-6" } }
1227
1227
  },
@@ -1556,7 +1556,7 @@ import { Pressable as Pressable6, ScrollView, StyleSheet as StyleSheet10, Text a
1556
1556
  var chipRecipe = defineRecipe({
1557
1557
  slots: {
1558
1558
  group: "flex flex-wrap items-center gap-2 data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch",
1559
- base: "inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border font-semibold transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg data-[state=on]:border-accent data-[state=on]:bg-accent-soft data-[state=on]:text-fg",
1559
+ base: "inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-sm border font-semibold transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg data-[state=on]:border-accent data-[state=on]:bg-accent-soft data-[state=on]:text-fg",
1560
1560
  remove: "-mr-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
1561
1561
  },
1562
1562
  variants: {