@krizaka/ui 3.13.0 → 3.15.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 (71) hide show
  1. package/README.md +5 -3
  2. package/dist/avatar/defaults.d.ts +43 -0
  3. package/dist/avatar/defaults.js +76 -0
  4. package/dist/avatar/defaults.js.map +1 -0
  5. package/dist/avatar.d.ts +1 -1
  6. package/dist/avatar.js +4 -2
  7. package/dist/avatar.js.map +1 -1
  8. package/dist/chat.js +1 -1
  9. package/dist/{chunk-UB2BI3AW.js → chunk-BCVLLY3R.js} +47 -3
  10. package/dist/chunk-BCVLLY3R.js.map +1 -0
  11. package/dist/{chunk-GR5JNFGQ.js → chunk-LIPK4UCS.js} +300 -164
  12. package/dist/chunk-LIPK4UCS.js.map +1 -0
  13. package/dist/{chunk-COJ3IDE2.js → chunk-SKMJRM5K.js} +11 -4
  14. package/dist/chunk-SKMJRM5K.js.map +1 -0
  15. package/dist/{chunk-2JFARUKC.js → chunk-YIBKJ36L.js} +1 -1
  16. package/dist/chunk-YIBKJ36L.js.map +1 -0
  17. package/dist/{geometry-B2E8oFhg.d.ts → geometry-DX-W-Yyf.d.ts} +5 -2
  18. package/dist/index.d.ts +18 -2
  19. package/dist/index.js +5 -1
  20. package/dist/map.js +60 -0
  21. package/dist/map.js.map +1 -1
  22. package/dist/mascot-quote.d.ts +5 -2
  23. package/dist/mascot-quote.js +45 -1
  24. package/dist/mascot-quote.js.map +1 -1
  25. package/dist/mascot-state.d.ts +2 -2
  26. package/dist/mascot-state.js +1 -1
  27. package/dist/mascot-state.js.map +1 -1
  28. package/dist/mascots.d.ts +4 -4
  29. package/dist/mascots.js +1 -1
  30. package/dist/match-dialog.js +1 -1
  31. package/dist/native/map.js +60 -0
  32. package/dist/native/map.js.map +1 -1
  33. package/dist/native.d.ts +35 -18
  34. package/dist/native.js +1114 -912
  35. package/dist/native.js.map +1 -1
  36. package/dist/nav-bar.d.ts +1 -1
  37. package/dist/nav-bar.js +5 -4
  38. package/dist/nav-bar.js.map +1 -1
  39. package/dist/site-footer.d.ts +9 -2
  40. package/dist/site-footer.js +14 -4
  41. package/dist/site-footer.js.map +1 -1
  42. package/dist/{sites-ClGou6ou.d.ts → sites-CHp_vELn.d.ts} +4 -3
  43. package/dist/sites.d.ts +5 -4
  44. package/dist/sites.js +9 -2
  45. package/dist/sites.js.map +1 -1
  46. package/dist/vertical-video.js +1 -1
  47. package/package.json +14 -8
  48. package/registry/avatar.json +6 -2
  49. package/registry/examples/mascots/beaver.d.ts +4 -0
  50. package/registry/examples/mascots/beaver.js +19 -0
  51. package/registry/examples/mascots/beaver.tsx +18 -0
  52. package/registry/examples/mascots/flock.tsx +1 -1
  53. package/registry/examples/mascots/magpie.d.ts +4 -0
  54. package/registry/examples/mascots/magpie.js +19 -0
  55. package/registry/examples/mascots/magpie.tsx +18 -0
  56. package/registry/examples/site-footer/product.js +26 -12
  57. package/registry/examples/site-footer/product.tsx +15 -2
  58. package/registry/index.json +9 -9
  59. package/registry/map.json +1 -1
  60. package/registry/mascot-quote.json +5 -5
  61. package/registry/mascot-state.json +33 -33
  62. package/registry/mascots.json +36 -20
  63. package/registry/nav-bar.json +19 -3
  64. package/registry/permission-prompt.json +1 -1
  65. package/registry/product-page.json +2 -2
  66. package/registry/site-footer.json +42 -11
  67. package/registry/sites.json +3 -3
  68. package/dist/chunk-2JFARUKC.js.map +0 -1
  69. package/dist/chunk-COJ3IDE2.js.map +0 -1
  70. package/dist/chunk-GR5JNFGQ.js.map +0 -1
  71. package/dist/chunk-UB2BI3AW.js.map +0 -1
@@ -445,11 +445,15 @@
445
445
  },
446
446
  {
447
447
  "path": "avatar/default-avatar.ts",
448
- "content": "// The default profile pictures of the Krizaka family: what a product shows when a person has no photo.\n// They are generated from this file (tools/export-brand.mjs renders them to SVG and WebP and uploads them to Bunny,\n// `people/defaults/v1/<brand>/…`), so the pictures and the URLs that name them cannot drift apart.\n\nexport type DefaultAvatarBrand = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\";\n/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */\nexport type DefaultAvatarKind = \"person\" | \"plain\";\nexport type DefaultAvatarSize = 256 | 512;\n\nexport const DEFAULT_AVATAR_BRANDS: readonly DefaultAvatarBrand[] = [\"krizaka\", \"orazaka\", \"orochia\", \"yasakia\", \"yororis\", \"yomizaka\"];\nexport const DEFAULT_AVATAR_KINDS: readonly DefaultAvatarKind[] = [\"person\", \"plain\"];\nexport const DEFAULT_AVATAR_SIZES: readonly DefaultAvatarSize[] = [256, 512];\n/** How many tones each brand has: a person always gets the same one from their seed, so a list is not one colour. */\nexport const DEFAULT_AVATAR_TONES = 6;\n\n/** Where the set lives. `v1` is the set's version: a changed picture is a new version folder, never an overwrite. */\nexport const DEFAULT_AVATAR_BASE = \"https://krizaka-assets.b-cdn.net/people/defaults/v1\";\n\nexport interface DefaultAvatar {\n /** The brand of the product showing the avatar: its gradient, its hues. */\n brand: DefaultAvatarBrand;\n /** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */\n seed?: string | number;\n /** Force a tone, 1 to 6, instead of deriving it from `seed`. */\n tone?: number;\n /**\n * `person` (default): a silhouette. `plain`: the gradient alone.\n * @default \"person\"\n */\n kind?: DefaultAvatarKind;\n}\n\n/** The mark gradient of each brand (BRAND.md, `$extensions[\"com.krizaka\"].mark`) as [hue, saturation %, lightness %] and the hue spread of its tones. */\nconst BRANDS: Record<DefaultAvatarBrand, { from: [number, number, number]; to: [number, number, number]; spread: number }> = {\n krizaka: { from: [217, 92, 53], to: [217, 92, 68], spread: 14 },\n orazaka: { from: [26, 90.5, 37.1], to: [26, 92, 45], spread: 7 },\n orochia: { from: [262.1, 83.3, 57.8], to: [328.6, 85.5, 70.2], spread: 12 },\n yasakia: { from: [156, 92, 32], to: [178, 90, 32], spread: 12 },\n yororis: { from: [34, 46, 44], to: [104, 28, 42], spread: 10 },\n yomizaka: { from: [232, 50, 55], to: [248, 50, 58], spread: 10 },\n};\n\n/** White on top of a stop must stay visible: a stop is darkened until the contrast reaches this (WCAG 1.4.11, non-text). */\nconst MIN_CONTRAST = 3.2;\nconst OFFSETS = [0, -1, 1, -2, 2, 3] as const;\n\nfunction hslToHex([h, s, l]: [number, number, number]): string {\n const sat = s / 100;\n const light = l / 100;\n const k = (n: number) => (n + (((h % 360) + 360) % 360) / 30) % 12;\n const a = sat * Math.min(light, 1 - light);\n const channel = (n: number) => Math.round(255 * (light - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));\n return `#${[0, 8, 4].map((n) => channel(n).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nconst channelLuminance = (v: number) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);\nconst whiteContrast = (hex: string) => {\n const [r, g, b] = [1, 3, 5].map((i) => channelLuminance(Number.parseInt(hex.slice(i, i + 2), 16) / 255));\n return 1.05 / (0.2126 * r! + 0.7152 * g! + 0.0722 * b! + 0.05);\n};\n/** The stop, darkened one point of lightness at a time until white reads on it. */\nfunction readable([h, s, l]: [number, number, number]): string {\n let hex = hslToHex([h, s, l]);\n for (let light = l; whiteContrast(hex) < MIN_CONTRAST && light > 10; light -= 1) hex = hslToHex([h, s, light]);\n return hex;\n}\n\n/** The two stops (hex) of tone `tone` (1 to 6) of a brand. */\nexport function defaultAvatarTone(brand: DefaultAvatarBrand, tone: number): readonly [string, string] {\n const b = BRANDS[brand];\n const o = OFFSETS[(clampTone(tone) - 1) % OFFSETS.length] * b.spread;\n return [readable([b.from[0] + o, b.from[1], b.from[2]]), readable([b.to[0] + o, b.to[1], b.to[2]])];\n}\n\nconst clampTone = (tone: number) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));\n\n/** 1 to 6 from a seed (FNV-1a): stable across renders, devices and products. */\nexport function defaultAvatarToneOf(seed: string | number | undefined): number {\n if (seed === undefined) return 1;\n let h = 0x811c9dc5;\n for (const ch of String(seed)) {\n h ^= ch.codePointAt(0) ?? 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return (h % DEFAULT_AVATAR_TONES) + 1;\n}\n\nconst resolveTone = (a: DefaultAvatar) => (a.tone === undefined ? defaultAvatarToneOf(a.seed) : clampTone(a.tone));\n\n/** The file name of a picture in the set: `person-3` + 256 → `person-3-256.webp`; `svg` has no size. */\nexport function defaultAvatarFile(kind: DefaultAvatarKind, tone: number, size: DefaultAvatarSize | \"svg\"): string {\n return size === \"svg\" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;\n}\n\n/**\n * The URL of a default avatar: `defaultAvatarUrl({ brand: \"orochia\", seed: user.id })`.\n * @param size 256 for lists, 512 for a profile; `\"svg\"` for the vector.\n * @param base Another host or version of the set (a self-hosted copy); the default is the Krizaka CDN.\n */\nexport function defaultAvatarUrl(avatar: DefaultAvatar, { size = 256, base = DEFAULT_AVATAR_BASE }: { size?: DefaultAvatarSize | \"svg\"; base?: string } = {}): string {\n return `${base.replace(/\\/+$/, \"\")}/${avatar.brand}/${defaultAvatarFile(avatar.kind ?? \"person\", resolveTone(avatar), size)}`;\n}\n\n/** The SVG source of a default avatar, 512 × 512 and square (the Avatar clips it round). */\nexport function defaultAvatarSvg(avatar: DefaultAvatar): string {\n const [from, to] = defaultAvatarTone(avatar.brand, resolveTone(avatar));\n const person =\n (avatar.kind ?? \"person\") === \"person\"\n ? `<circle cx=\"256\" cy=\"200\" r=\"86\" fill=\"#fff\" fill-opacity=\".92\"/><path d=\"M84 512c0-108 78-182 172-182s172 74 172 182z\" fill=\"#fff\" fill-opacity=\".92\"/>`\n : \"\";\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/></linearGradient><radialGradient id=\"l\" cx=\".28\" cy=\".18\" r=\".85\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".24\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"512\" height=\"512\" fill=\"url(#g)\"/><rect width=\"512\" height=\"512\" fill=\"url(#l)\"/>${person}</svg>`;\n}\n"
448
+ "content": "// The default profile pictures of the Krizaka family: what a product shows when a person has no photo.\n// They are generated from this file (tools/export-brand.mjs renders them to SVG and WebP and uploads them to Bunny,\n// `people/defaults/v1/<brand>/…`), so the pictures and the URLs that name them cannot drift apart.\n\nexport type DefaultAvatarBrand = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\";\n/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */\nexport type DefaultAvatarKind = \"person\" | \"plain\";\nexport type DefaultAvatarSize = 256 | 512;\n\nexport const DEFAULT_AVATAR_BRANDS: readonly DefaultAvatarBrand[] = [\"krizaka\", \"orazaka\", \"orochia\", \"yasakia\", \"yororis\", \"yomizaka\", \"kachizaka\", \"akinaza\"];\nexport const DEFAULT_AVATAR_KINDS: readonly DefaultAvatarKind[] = [\"person\", \"plain\"];\nexport const DEFAULT_AVATAR_SIZES: readonly DefaultAvatarSize[] = [256, 512];\n/** How many tones each brand has: a person always gets the same one from their seed, so a list is not one colour. */\nexport const DEFAULT_AVATAR_TONES = 6;\n\n/** Where the set lives. `v1` is the set's version: a changed picture is a new version folder, never an overwrite. */\nexport const DEFAULT_AVATAR_BASE = \"https://krizaka-assets.b-cdn.net/people/defaults/v1\";\n\nexport interface DefaultAvatar {\n /** The brand of the product showing the avatar: its gradient, its hues. */\n brand: DefaultAvatarBrand;\n /** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */\n seed?: string | number;\n /** Force a tone, 1 to 6, instead of deriving it from `seed`. */\n tone?: number;\n /**\n * `person` (default): a silhouette. `plain`: the gradient alone.\n * @default \"person\"\n */\n kind?: DefaultAvatarKind;\n}\n\n/** The mark gradient of each brand (BRAND.md, `$extensions[\"com.krizaka\"].mark`) as [hue, saturation %, lightness %] and the hue spread of its tones. */\nconst BRANDS: Record<DefaultAvatarBrand, { from: [number, number, number]; to: [number, number, number]; spread: number }> = {\n krizaka: { from: [217, 92, 53], to: [217, 92, 68], spread: 14 },\n orazaka: { from: [26, 90.5, 37.1], to: [26, 92, 45], spread: 7 },\n orochia: { from: [262.1, 83.3, 57.8], to: [328.6, 85.5, 70.2], spread: 12 },\n yasakia: { from: [156, 92, 32], to: [178, 90, 32], spread: 12 },\n yororis: { from: [34, 46, 44], to: [104, 28, 42], spread: 10 },\n yomizaka: { from: [232, 50, 55], to: [248, 50, 58], spread: 10 },\n kachizaka: { from: [226, 80, 56], to: [44, 92, 33], spread: 10 },\n akinaza: { from: [140, 48, 34], to: [22, 60, 46], spread: 10 },\n};\n\n/** White on top of a stop must stay visible: a stop is darkened until the contrast reaches this (WCAG 1.4.11, non-text). */\nconst MIN_CONTRAST = 3.2;\nconst OFFSETS = [0, -1, 1, -2, 2, 3] as const;\n\nfunction hslToHex([h, s, l]: [number, number, number]): string {\n const sat = s / 100;\n const light = l / 100;\n const k = (n: number) => (n + (((h % 360) + 360) % 360) / 30) % 12;\n const a = sat * Math.min(light, 1 - light);\n const channel = (n: number) => Math.round(255 * (light - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));\n return `#${[0, 8, 4].map((n) => channel(n).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nconst channelLuminance = (v: number) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);\nconst whiteContrast = (hex: string) => {\n const [r, g, b] = [1, 3, 5].map((i) => channelLuminance(Number.parseInt(hex.slice(i, i + 2), 16) / 255));\n return 1.05 / (0.2126 * r! + 0.7152 * g! + 0.0722 * b! + 0.05);\n};\n/** The stop, darkened one point of lightness at a time until white reads on it. */\nfunction readable([h, s, l]: [number, number, number]): string {\n let hex = hslToHex([h, s, l]);\n for (let light = l; whiteContrast(hex) < MIN_CONTRAST && light > 10; light -= 1) hex = hslToHex([h, s, light]);\n return hex;\n}\n\n/** The two stops (hex) of tone `tone` (1 to 6) of a brand. */\nexport function defaultAvatarTone(brand: DefaultAvatarBrand, tone: number): readonly [string, string] {\n const b = BRANDS[brand];\n const o = OFFSETS[(clampTone(tone) - 1) % OFFSETS.length] * b.spread;\n return [readable([b.from[0] + o, b.from[1], b.from[2]]), readable([b.to[0] + o, b.to[1], b.to[2]])];\n}\n\nconst clampTone = (tone: number) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));\n\n/** 1 to 6 from a seed (FNV-1a): stable across renders, devices and products. */\nexport function defaultAvatarToneOf(seed: string | number | undefined): number {\n if (seed === undefined) return 1;\n let h = 0x811c9dc5;\n for (const ch of String(seed)) {\n h ^= ch.codePointAt(0) ?? 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return (h % DEFAULT_AVATAR_TONES) + 1;\n}\n\nconst resolveTone = (a: DefaultAvatar) => (a.tone === undefined ? defaultAvatarToneOf(a.seed) : clampTone(a.tone));\n\n/** The file name of a picture in the set: `person-3` + 256 → `person-3-256.webp`; `svg` has no size. */\nexport function defaultAvatarFile(kind: DefaultAvatarKind, tone: number, size: DefaultAvatarSize | \"svg\"): string {\n return size === \"svg\" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;\n}\n\n/**\n * The URL of a default avatar: `defaultAvatarUrl({ brand: \"orochia\", seed: user.id })`.\n * @param size 256 for lists, 512 for a profile; `\"svg\"` for the vector.\n * @param base Another host or version of the set (a self-hosted copy); the default is the Krizaka CDN.\n */\nexport function defaultAvatarUrl(avatar: DefaultAvatar, { size = 256, base = DEFAULT_AVATAR_BASE }: { size?: DefaultAvatarSize | \"svg\"; base?: string } = {}): string {\n return `${base.replace(/\\/+$/, \"\")}/${avatar.brand}/${defaultAvatarFile(avatar.kind ?? \"person\", resolveTone(avatar), size)}`;\n}\n\n/** The SVG source of a default avatar, 512 × 512 and square (the Avatar clips it round). */\nexport function defaultAvatarSvg(avatar: DefaultAvatar): string {\n const [from, to] = defaultAvatarTone(avatar.brand, resolveTone(avatar));\n const person =\n (avatar.kind ?? \"person\") === \"person\"\n ? `<circle cx=\"256\" cy=\"200\" r=\"86\" fill=\"#fff\" fill-opacity=\".92\"/><path d=\"M84 512c0-108 78-182 172-182s172 74 172 182z\" fill=\"#fff\" fill-opacity=\".92\"/>`\n : \"\";\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/></linearGradient><radialGradient id=\"l\" cx=\".28\" cy=\".18\" r=\".85\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".24\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"512\" height=\"512\" fill=\"url(#g)\"/><rect width=\"512\" height=\"512\" fill=\"url(#l)\"/>${person}</svg>`;\n}\n"
449
+ },
450
+ {
451
+ "path": "avatar/defaults.ts",
452
+ "content": "// @krizaka/ui/avatar/defaults — the default pictures' pure helpers, SERVER-SAFE: no \"use client\", no React.\n// `defaultAvatarUrl(…)` can be called in a Server Component; `@krizaka/ui/avatar` (a client entry) re-exports the same\n// functions for client code, but calling them from the server through it fails (\"Attempted to call … from the server\").\nexport {\n DEFAULT_AVATAR_BASE,\n DEFAULT_AVATAR_BRANDS,\n DEFAULT_AVATAR_KINDS,\n DEFAULT_AVATAR_SIZES,\n DEFAULT_AVATAR_TONES,\n type DefaultAvatar,\n type DefaultAvatarBrand,\n defaultAvatarFile,\n type DefaultAvatarKind,\n type DefaultAvatarSize,\n defaultAvatarSvg,\n defaultAvatarTone,\n defaultAvatarToneOf,\n defaultAvatarUrl,\n} from \"./default-avatar\";\n"
449
453
  },
450
454
  {
451
455
  "path": "avatar/index.ts",
452
- "content": "/** @krizaka/ui/avatar — `Avatar`, `Avatar.Group`, and the default pictures (`defaultAvatarUrl`…). Client (\"use client\"). */\nexport { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, AvatarRoot, type AvatarSize } from \"./avatar\";\nexport {\n DEFAULT_AVATAR_BASE,\n DEFAULT_AVATAR_BRANDS,\n DEFAULT_AVATAR_KINDS,\n DEFAULT_AVATAR_SIZES,\n DEFAULT_AVATAR_TONES,\n type DefaultAvatar,\n type DefaultAvatarBrand,\n defaultAvatarFile,\n type DefaultAvatarKind,\n type DefaultAvatarSize,\n defaultAvatarSvg,\n defaultAvatarTone,\n defaultAvatarToneOf,\n defaultAvatarUrl,\n} from \"./default-avatar\";\n"
456
+ "content": "/** @krizaka/ui/avatar — `Avatar`, `Avatar.Group`, and the default pictures (`defaultAvatarUrl`…) for CLIENT code. In a Server Component import the helpers from `@krizaka/ui/avatar/defaults`. Client (\"use client\"). */\nexport { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, AvatarRoot, type AvatarSize } from \"./avatar\";\nexport {\n DEFAULT_AVATAR_BASE,\n DEFAULT_AVATAR_BRANDS,\n DEFAULT_AVATAR_KINDS,\n DEFAULT_AVATAR_SIZES,\n DEFAULT_AVATAR_TONES,\n type DefaultAvatar,\n type DefaultAvatarBrand,\n defaultAvatarFile,\n type DefaultAvatarKind,\n type DefaultAvatarSize,\n defaultAvatarSvg,\n defaultAvatarTone,\n defaultAvatarToneOf,\n defaultAvatarUrl,\n} from \"./default-avatar\";\n"
453
457
  },
454
458
  {
455
459
  "path": "avatar/meta.ts",
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ // registry/examples/mascots/beaver.tsx
4
+ import { Button } from "@krizaka/ui/button";
5
+ import { Mascot } from "@krizaka/ui/mascots";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function MascotBeaver() {
8
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-96 items-center gap-4 rounded-xl border border-border-default bg-surface-1 p-4", children: [
9
+ /* @__PURE__ */ jsx(Mascot, { bird: "beaver", mood: "welcome", size: 88 }),
10
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
11
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-fg", children: "Six appointments today" }),
12
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-fg-secondary", children: "The first one is at 9:30. Every invoice is ready." }),
13
+ /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", className: "mt-3", children: "Open the day" })
14
+ ] })
15
+ ] });
16
+ }
17
+ export {
18
+ MascotBeaver as default
19
+ };
@@ -0,0 +1,18 @@
1
+ import { Button } from "@krizaka/ui/button";
2
+ import { Mascot } from "@krizaka/ui/mascots";
3
+
4
+ // The beaver builds the shop: the day at work — the words and one action beside it.
5
+ export default function MascotBeaver() {
6
+ return (
7
+ <div className="flex w-96 items-center gap-4 rounded-xl border border-border-default bg-surface-1 p-4">
8
+ <Mascot bird="beaver" mood="welcome" size={88} />
9
+ <div className="flex-1">
10
+ <p className="text-sm font-medium text-fg">Six appointments today</p>
11
+ <p className="mt-1 text-sm text-fg-secondary">The first one is at 9:30. Every invoice is ready.</p>
12
+ <Button variant="primary" size="sm" className="mt-3">
13
+ Open the day
14
+ </Button>
15
+ </div>
16
+ </div>
17
+ );
18
+ }
@@ -1,6 +1,6 @@
1
1
  import { Mascot,MASCOT_BIRDS } from "@krizaka/ui/mascots";
2
2
 
3
- // The seven birds in the host's brand colours: blue on Krizaka, orange in Orazaka, violet in Orochia, turquoise in
3
+ // The nine figures in the host's brand colours: blue on Krizaka, orange in Orazaka, violet in Orochia, turquoise in
4
4
  // Yasakia, sage in Yororis.
5
5
  export default function MascotFlock() {
6
6
  return (
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ // registry/examples/mascots/magpie.tsx
4
+ import { Button } from "@krizaka/ui/button";
5
+ import { Mascot } from "@krizaka/ui/mascots";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function MascotMagpie() {
8
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-96 items-center gap-4 rounded-xl border border-border-default bg-surface-1 p-4", children: [
9
+ /* @__PURE__ */ jsx(Mascot, { bird: "magpie", mood: "success", size: 88 }),
10
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
11
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-fg", children: "+5 points \u2014 exact score" }),
12
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-fg-secondary", children: "You move up to second place in the league." }),
13
+ /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", className: "mt-3", children: "See the standings" })
14
+ ] })
15
+ ] });
16
+ }
17
+ export {
18
+ MascotMagpie as default
19
+ };
@@ -0,0 +1,18 @@
1
+ import { Button } from "@krizaka/ui/button";
2
+ import { Mascot } from "@krizaka/ui/mascots";
3
+
4
+ // The magpie keeps the score: a win counted, the standings moved — the words and one action beside it.
5
+ export default function MascotMagpie() {
6
+ return (
7
+ <div className="flex w-96 items-center gap-4 rounded-xl border border-border-default bg-surface-1 p-4">
8
+ <Mascot bird="magpie" mood="success" size={88} />
9
+ <div className="flex-1">
10
+ <p className="text-sm font-medium text-fg">+5 points — exact score</p>
11
+ <p className="mt-1 text-sm text-fg-secondary">You move up to second place in the league.</p>
12
+ <Button variant="primary" size="sm" className="mt-3">
13
+ See the standings
14
+ </Button>
15
+ </div>
16
+ </div>
17
+ );
18
+ }
@@ -3,19 +3,33 @@
3
3
  // registry/examples/site-footer/product.tsx
4
4
  import { SiteFooter } from "@krizaka/ui/site-footer";
5
5
  import { familyLabels } from "@krizaka/ui/sites";
6
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  function SiteFooterProduct() {
8
- return /* @__PURE__ */ jsxs(SiteFooter, { current: "orazaka", labels: familyLabels.en, env: "DEV", version: "1.2.3", children: [
9
- /* @__PURE__ */ jsxs(SiteFooter.Column, { title: "Orazaka", children: [
10
- /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#how", children: "How it works" }),
11
- /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#demo", children: "Demo" }),
12
- /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#docs", children: "Docs" })
13
- ] }),
14
- /* @__PURE__ */ jsxs(SiteFooter.Column, { title: "Talk to us", children: [
15
- /* @__PURE__ */ jsx(SiteFooter.Link, { href: "https://www.krizaka.com/contact?topic=orazaka", children: "Contact" }),
16
- /* @__PURE__ */ jsx(SiteFooter.Link, { href: "https://github.com/krizaka/orazaka", children: "Source" })
17
- ] })
18
- ] });
8
+ return /* @__PURE__ */ jsxs(
9
+ SiteFooter,
10
+ {
11
+ current: "orazaka",
12
+ family: false,
13
+ labels: familyLabels.en,
14
+ env: "DEV",
15
+ version: "1.2.3",
16
+ legal: /* @__PURE__ */ jsxs(Fragment, { children: [
17
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#privacy", children: "Privacy" }),
18
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#terms", children: "Terms" })
19
+ ] }),
20
+ children: [
21
+ /* @__PURE__ */ jsxs(SiteFooter.Column, { title: "Orazaka", children: [
22
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#how", children: "How it works" }),
23
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#demo", children: "Demo" }),
24
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "#docs", children: "Docs" })
25
+ ] }),
26
+ /* @__PURE__ */ jsxs(SiteFooter.Column, { title: "Talk to us", children: [
27
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "https://www.krizaka.com/contact?topic=orazaka", children: "Contact" }),
28
+ /* @__PURE__ */ jsx(SiteFooter.Link, { href: "https://github.com/krizaka/orazaka", children: "Source" })
29
+ ] })
30
+ ]
31
+ }
32
+ );
19
33
  }
20
34
  export {
21
35
  SiteFooterProduct as default
@@ -1,10 +1,23 @@
1
1
  import { SiteFooter } from "@krizaka/ui/site-footer";
2
2
  import { familyLabels } from "@krizaka/ui/sites";
3
3
 
4
- // A product's footer (dev.orazaka.com): its own columns, the family, “A Krizaka product”, the version it runs.
4
+ // A product's footer (dev.orazaka.com): independent once deployed — its own columns, the legal links, the version it
5
+ // runs, and one publisher line “A Krizaka product” (→ www.krizaka.com). No family columns, no link to another product.
5
6
  export default function SiteFooterProduct() {
6
7
  return (
7
- <SiteFooter current="orazaka" labels={familyLabels.en} env="DEV" version="1.2.3">
8
+ <SiteFooter
9
+ current="orazaka"
10
+ family={false}
11
+ labels={familyLabels.en}
12
+ env="DEV"
13
+ version="1.2.3"
14
+ legal={
15
+ <>
16
+ <SiteFooter.Link href="#privacy">Privacy</SiteFooter.Link>
17
+ <SiteFooter.Link href="#terms">Terms</SiteFooter.Link>
18
+ </>
19
+ }
20
+ >
8
21
  <SiteFooter.Column title="Orazaka">
9
22
  <SiteFooter.Link href="#how">How it works</SiteFooter.Link>
10
23
  <SiteFooter.Link href="#demo">Demo</SiteFooter.Link>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "3.13.0",
3
+ "version": "3.15.0",
4
4
  "templatePurposes": {
5
5
  "convince": {
6
6
  "title": "Convince",
@@ -1731,7 +1731,7 @@
1731
1731
  "native": 2
1732
1732
  },
1733
1733
  "dependencies": [
1734
- "@krizaka/tokens@^3.13.0",
1734
+ "@krizaka/tokens@^3.15.0",
1735
1735
  "@protomaps/basemaps@5.7.2",
1736
1736
  "tailwind-variants@^3.3.1"
1737
1737
  ],
@@ -1841,13 +1841,13 @@
1841
1841
  "name": "mascots",
1842
1842
  "type": "primitive",
1843
1843
  "title": "Mascots",
1844
- "summary": "The Krizaka flock: seven birds (falcon, owl, duck, flamingo, pigeon, lovebird, crane) in five moods, drawn in the product's brand colours — and the phoenix, the comeback.",
1845
- "description": "The Krizaka flock: seven birds (falcon, owl, duck, flamingo, pigeon, lovebird, crane) in five moods, drawn in the product's brand colours — and the phoenix, the comeback.",
1844
+ "summary": "The Krizaka flock: nine figures (falcon, owl, duck, flamingo, pigeon, lovebird, crane, magpie — and the beaver) in five moods, drawn in the product's brand colours — and the phoenix, the comeback.",
1845
+ "description": "The Krizaka flock: nine figures (falcon, owl, duck, flamingo, pigeon, lovebird, crane, magpie — and the beaver) in five moods, drawn in the product's brand colours — and the phoenix, the comeback.",
1846
1846
  "status": "beta",
1847
1847
  "category": "feedback",
1848
1848
  "platforms": "both",
1849
1849
  "examples": {
1850
- "web": 8,
1850
+ "web": 10,
1851
1851
  "native": 4
1852
1852
  },
1853
1853
  "dependencies": [],
@@ -1939,7 +1939,7 @@
1939
1939
  "native": 0
1940
1940
  },
1941
1941
  "dependencies": [
1942
- "@krizaka/tokens@^3.13.0",
1942
+ "@krizaka/tokens@^3.15.0",
1943
1943
  "radix-ui@^1.7.0",
1944
1944
  "tailwind-variants@^3.3.1"
1945
1945
  ],
@@ -2445,8 +2445,8 @@
2445
2445
  "name": "site-footer",
2446
2446
  "type": "primitive",
2447
2447
  "title": "Site footer",
2448
- "summary": "The footer shared by every site of the Krizaka family: the site's mark and line, its own columns, the family in columns (from `@krizaka/ui/sites`), and the signature line — “A Krizaka company / product / open-source project” — with the version and Montréal.",
2449
- "description": "The footer shared by every site of the Krizaka family: the site's mark and line, its own columns, the family in columns (from `@krizaka/ui/sites`), and the signature line — “A Krizaka company / product / open-source project” — with the version and Montréal.",
2448
+ "summary": "The footer of a Krizaka site: the site's mark and line, its own columns, the family in columns on the Krizaka sites (from `@krizaka/ui/sites`; `family={false}` for an independent product), and the signature line — “A Krizaka company / product / open-source project” — with the version and Montréal.",
2449
+ "description": "The footer of a Krizaka site: the site's mark and line, its own columns, the family in columns on the Krizaka sites (from `@krizaka/ui/sites`; `family={false}` for an independent product), and the signature line — “A Krizaka company / product / open-source project” — with the version and Montréal.",
2450
2450
  "status": "beta",
2451
2451
  "category": "navigation",
2452
2452
  "platforms": "web",
@@ -2455,7 +2455,7 @@
2455
2455
  "native": 0
2456
2456
  },
2457
2457
  "dependencies": [
2458
- "@krizaka/tokens@^3.13.0",
2458
+ "@krizaka/tokens@^3.15.0",
2459
2459
  "radix-ui@^1.7.0",
2460
2460
  "tailwind-variants@^3.3.1"
2461
2461
  ],
package/registry/map.json CHANGED
@@ -930,7 +930,7 @@
930
930
  }
931
931
  ],
932
932
  "dependencies": [
933
- "@krizaka/tokens@^3.13.0",
933
+ "@krizaka/tokens@^3.15.0",
934
934
  "@protomaps/basemaps@5.7.2",
935
935
  "tailwind-variants@^3.3.1"
936
936
  ],
@@ -9,7 +9,7 @@
9
9
  "category": "blocks",
10
10
  "platforms": "web",
11
11
  "whenToUse": [
12
- "To state the rule a feature stands for (the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the pigeon delivers, the lovebird introduces, the crane listens).",
12
+ "To state the rule a feature stands for (the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the pigeon delivers, the lovebird introduces, the crane listens, the magpie keeps the score, the beaver builds the shop).",
13
13
  "A promise that deserves its own moment in a section (`callout`)."
14
14
  ],
15
15
  "whenNotToUse": [
@@ -49,7 +49,7 @@
49
49
  "props": [
50
50
  {
51
51
  "name": "bird",
52
- "type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\"",
52
+ "type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\"",
53
53
  "default": null,
54
54
  "description": "The bird that speaks — the one whose idea it is.",
55
55
  "required": true
@@ -119,11 +119,11 @@
119
119
  },
120
120
  {
121
121
  "path": "mascot-quote/mascot-quote.tsx",
122
- "content": "// Client (the bird has an id per instance). A rule, a promise or a decision told by a bird (blocks.css): the sentence in\n// a speech bubble — an opening quote mark, the hue around it — and under its tail the bird and who speaks. One bird per\n// idea (Mascot's rule): the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the\n// pigeon delivers, the lovebird introduces, the crane listens.\nimport type * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type MascotBird, type MascotMood } from \"../mascots/geometry\";\nimport { Mascot } from \"../mascots/mascot\";\n\nexport type MascotQuoteProps = Omit<React.HTMLAttributes<HTMLElement>, \"children\"> & {\n /** The bird that speaks — the one whose idea it is. */\n bird: MascotBird;\n /**\n * Its mood.\n * @default \"thinking\"\n */\n mood?: MascotMood;\n /** Who speaks, under the bubble: `The falcon's rule`. */\n label: React.ReactNode;\n /** The sentence: the rule, the promise. */\n children: React.ReactNode;\n /**\n * `inline` inside a card (small, the bird answers the pointer); `callout` alone in a section (larger, the bird alive).\n * @default \"inline\"\n */\n variant?: \"inline\" | \"callout\";\n /** The bird's idle loop (it pauses off screen and stops under reduced motion). Defaults to `true` for a callout. */\n animated?: boolean;\n};\n\n/** A sentence told by a bird: a speech bubble, the bird under its tail, who speaks. The bird is decorative. */\nexport function MascotQuote({ bird, mood = \"thinking\", label, children, variant = \"inline\", animated, className, ...props }: MascotQuoteProps) {\n const callout = variant === \"callout\";\n return (\n <figure className={cn(\"kz-quote\", className)} data-variant={callout ? \"callout\" : undefined} {...props}>\n <blockquote>\n <p>{children}</p>\n </blockquote>\n <figcaption>\n <Mascot bird={bird} mood={mood} size={callout ? 72 : 44} animated={animated ?? callout} className=\"kz-quote-bird\" aria-hidden />\n <span>{label}</span>\n </figcaption>\n </figure>\n );\n}\n"
122
+ "content": "// Client (the bird has an id per instance). A rule, a promise or a decision told by a bird (blocks.css): the sentence in\n// a speech bubble — an opening quote mark, the hue around it — and under its tail the bird and who speaks. One bird per\n// idea (Mascot's rule): the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the\n// pigeon delivers, the lovebird introduces, the crane listens, the magpie keeps the score, the beaver builds the shop.\nimport type * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type MascotBird, type MascotMood } from \"../mascots/geometry\";\nimport { Mascot } from \"../mascots/mascot\";\n\nexport type MascotQuoteProps = Omit<React.HTMLAttributes<HTMLElement>, \"children\"> & {\n /** The bird that speaks — the one whose idea it is. */\n bird: MascotBird;\n /**\n * Its mood.\n * @default \"thinking\"\n */\n mood?: MascotMood;\n /** Who speaks, under the bubble: `The falcon's rule`. */\n label: React.ReactNode;\n /** The sentence: the rule, the promise. */\n children: React.ReactNode;\n /**\n * `inline` inside a card (small, the bird answers the pointer); `callout` alone in a section (larger, the bird alive).\n * @default \"inline\"\n */\n variant?: \"inline\" | \"callout\";\n /** The bird's idle loop (it pauses off screen and stops under reduced motion). Defaults to `true` for a callout. */\n animated?: boolean;\n};\n\n/** A sentence told by a bird: a speech bubble, the bird under its tail, who speaks. The bird is decorative. */\nexport function MascotQuote({ bird, mood = \"thinking\", label, children, variant = \"inline\", animated, className, ...props }: MascotQuoteProps) {\n const callout = variant === \"callout\";\n return (\n <figure className={cn(\"kz-quote\", className)} data-variant={callout ? \"callout\" : undefined} {...props}>\n <blockquote>\n <p>{children}</p>\n </blockquote>\n <figcaption>\n <Mascot bird={bird} mood={mood} size={callout ? 72 : 44} animated={animated ?? callout} className=\"kz-quote-bird\" aria-hidden />\n <span>{label}</span>\n </figcaption>\n </figure>\n );\n}\n"
123
123
  },
124
124
  {
125
125
  "path": "mascot-quote/meta.ts",
126
- "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Mascot quote\",\n summary:\n \"A rule, a promise or a decision told by a bird: the sentence in a speech bubble, the bird under its tail and who speaks — `inline` in a card, `callout` alone in a section.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"To state the rule a feature stands for (the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the pigeon delivers, the lovebird introduces, the crane listens).\",\n \"A promise that deserves its own moment in a section (`callout`).\",\n ],\n whenNotToUse: [\n { when: \"For a bird that only decorates: a bird carries a promise, or it is not there.\", use: \"mascots\" },\n { when: \"For a status or a warning the reader must act on.\", use: \"alert\" },\n ],\n bestPractices: [\n \"One bird per idea, the same bird for the same idea everywhere.\",\n \"One sentence, said as a rule: short, in the present tense.\",\n \"The label says who speaks (`The falcon's rule`): words as props.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"A `figure` with a `blockquote` and its `figcaption`: read as a quote and who says it.\",\n \"The bird is decorative (`aria-hidden`); its idle loop pauses off screen and stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"mascots\", \"argument-card\", \"alert\"],\n web: {\n imports: [\"MascotQuote\"],\n examples: [\n { name: \"inline\", title: \"Inline\", description: \"The falcon's rule, small, beside a paragraph or inside a card.\" },\n { name: \"callout\", title: \"Callout\", description: \"The owl's promise, alone in a section, the bird alive.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
126
+ "content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Mascot quote\",\n summary:\n \"A rule, a promise or a decision told by a bird: the sentence in a speech bubble, the bird under its tail and who speaks — `inline` in a card, `callout` alone in a section.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n whenToUse: [\n \"To state the rule a feature stands for (the falcon watches, the owl reads, the duck builds, the flamingo answers or gets paid, the pigeon delivers, the lovebird introduces, the crane listens, the magpie keeps the score, the beaver builds the shop).\",\n \"A promise that deserves its own moment in a section (`callout`).\",\n ],\n whenNotToUse: [\n { when: \"For a bird that only decorates: a bird carries a promise, or it is not there.\", use: \"mascots\" },\n { when: \"For a status or a warning the reader must act on.\", use: \"alert\" },\n ],\n bestPractices: [\n \"One bird per idea, the same bird for the same idea everywhere.\",\n \"One sentence, said as a rule: short, in the present tense.\",\n \"The label says who speaks (`The falcon's rule`): words as props.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"A `figure` with a `blockquote` and its `figcaption`: read as a quote and who says it.\",\n \"The bird is decorative (`aria-hidden`); its idle loop pauses off screen and stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"mascots\", \"argument-card\", \"alert\"],\n web: {\n imports: [\"MascotQuote\"],\n examples: [\n { name: \"inline\", title: \"Inline\", description: \"The falcon's rule, small, beside a paragraph or inside a card.\" },\n { name: \"callout\", title: \"Callout\", description: \"The owl's promise, alone in a section, the bird alive.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
127
127
  },
128
128
  {
129
129
  "path": "meta.ts",
@@ -146,7 +146,7 @@
146
146
  "props": [
147
147
  {
148
148
  "name": "bird",
149
- "type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\"",
149
+ "type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\"",
150
150
  "default": null,
151
151
  "description": "The bird that speaks — the one whose idea it is.",
152
152
  "required": true