@krizaka/ui 3.18.0 → 3.18.2

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.
@@ -19,7 +19,7 @@ var sites = [
19
19
  site({ id: "krizaka", name: "Krizaka", url: "https://www.krizaka.com", kind: "company", group: "krizaka", status: "released", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-com" }),
20
20
  site({ id: "orazaka", name: "Orazaka", url: "https://dev.orazaka.com", kind: "product", group: "products", status: "released", mark: "orazaka", repo: "https://github.com/krizaka/orazaka" }),
21
21
  site({ id: "orochia", name: "Orochia", url: "https://dev.orochia.com", kind: "product", group: "products", status: "released", mark: "orochia", repo: "https://github.com/krizaka/orochia" }),
22
- site({ id: "yasakia", name: "Yasakia", url: "https://dev.yasakia.com", kind: "product", group: "products", status: "upcoming", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
22
+ site({ id: "yasakia", name: "Yasakia", url: "https://dev.yasakia.com", kind: "product", group: "products", status: "development", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
23
23
  site({ id: "yororis", name: "Yororis", url: "https://dev.yororis.com", kind: "product", group: "products", status: "construction", mark: "yororis", repo: "https://github.com/krizaka/yororis" }),
24
24
  site({ id: "yomizaka", name: "Yomizaka", url: "https://dev.yomizaka.com", kind: "product", group: "products", status: "development", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
25
25
  // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).
@@ -92,4 +92,4 @@ export {
92
92
  sitesByGroup,
93
93
  familyLabels
94
94
  };
95
- //# sourceMappingURL=chunk-53LQPYQZ.js.map
95
+ //# sourceMappingURL=chunk-5MH64NIL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/family-mark.tsx","../src/sites/sites.ts"],"sourcesContent":["// The mark of a site of the family (`@krizaka/ui/sites`): its brand's `ProductLogo`, still (a list of marks never\n// moves), or GitHub's, in the text colour. Shared by `NavBar.Family` and `SiteFooter` — not an entry of its own.\nimport type * as React from \"react\";\n\nimport { ProductLogo } from \"./marks/ProductLogo\";\nimport type { SiteMark } from \"./sites/sites\";\n\n/** GitHub's mark, in the text colour (both themes). */\nexport function GitHubGlyph({ size = 16, className }: { size?: number; className?: string }) {\n return (\n <svg viewBox=\"0 0 16 16\" width={size} height={size} fill=\"currentColor\" aria-hidden className={className}>\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z\" />\n </svg>\n );\n}\n\n/** A site's mark, decorative (its name is always written beside it). */\nexport function FamilyMark({ mark, size }: { mark: SiteMark; size: number }): React.ReactElement {\n if (mark === \"github\") return <GitHubGlyph size={Math.round(size * 0.8)} />;\n return <ProductLogo id={mark} size={size} animated={false} />;\n}\n","// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"],"mappings":"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","names":[]}
@@ -187,7 +187,7 @@ var Card = {
187
187
  };
188
188
 
189
189
  // src/media-grid/video-card.tsx
190
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
190
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
191
191
  var FRAME = { landscape: "aspect-video", portrait: "aspect-[9/16]" };
192
192
  function VideoCard({
193
193
  title,
@@ -217,6 +217,7 @@ function VideoCard({
217
217
  const [live, setLive] = React2.useState(false);
218
218
  const picture = live && previewSrc ? previewSrc : poster;
219
219
  const row = layout === "row";
220
+ const tall = aspect === "portrait" && !row;
220
221
  return /* @__PURE__ */ jsx5(
221
222
  "article",
222
223
  {
@@ -238,13 +239,15 @@ function VideoCard({
238
239
  ] }),
239
240
  /* @__PURE__ */ jsxs4(Card.Body, { className: "min-w-0 flex-1 justify-between gap-2 p-3", children: [
240
241
  /* @__PURE__ */ jsx5("h3", { className: "m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg", children: /* @__PURE__ */ jsx5(BlockLink, { href, linkAs, className: "line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring", children: title }) }),
241
- /* @__PURE__ */ jsxs4("div", { className: "relative z-10 flex h-8 items-center gap-2 text-xs text-fg-secondary", children: [
242
- creator && /* @__PURE__ */ jsxs4(Fragment, { children: [
242
+ /* @__PURE__ */ jsxs4("div", { className: cn("relative z-10 text-xs text-fg-secondary", tall ? "flex flex-col gap-1" : "flex h-8 items-center gap-2"), children: [
243
+ creator && /* @__PURE__ */ jsxs4("div", { className: "flex h-6 min-w-0 items-center gap-2", children: [
243
244
  /* @__PURE__ */ jsx5(Avatar, { src: creator.avatarSrc, defaultAvatar: creator.defaultAvatar, alt: "", fallback: creator.name.charAt(0), size: "xs", className: "shrink-0" }),
244
- creator.href ? /* @__PURE__ */ jsx5(BlockLink, { href: creator.href, linkAs, className: "truncate hover:text-fg", children: creator.name }) : /* @__PURE__ */ jsx5("span", { className: "truncate", children: creator.name })
245
+ creator.href ? /* @__PURE__ */ jsx5(BlockLink, { href: creator.href, linkAs, className: "min-w-0 truncate hover:text-fg", children: creator.name }) : /* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate", children: creator.name })
245
246
  ] }),
246
- meta && /* @__PURE__ */ jsx5("span", { className: "ml-auto shrink-0 font-mono text-[11px] text-fg-muted", children: meta }),
247
- action && /* @__PURE__ */ jsx5("span", { className: "shrink-0", children: action })
247
+ (meta || action) && /* @__PURE__ */ jsxs4("div", { className: cn("flex items-center gap-2", tall ? "h-7 justify-between" : "ml-auto shrink-0"), children: [
248
+ meta && /* @__PURE__ */ jsx5("span", { className: "shrink-0 font-mono text-[11px] text-fg-muted", children: meta }),
249
+ action && /* @__PURE__ */ jsx5("span", { className: "shrink-0", children: action })
250
+ ] })
248
251
  ] })
249
252
  ] })
250
253
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/media-grid/shorts-shelf.tsx","../src/block-link.tsx","../src/media-grid/media-grid.tsx","../src/media-grid/video-card.tsx","../src/card/card.tsx","../src/card/recipe.ts"],"sourcesContent":["// Client (the arrows scroll the row). A horizontal row of 9:16 cards of one height: scroll-snap, arrows with a mouse,\n// a swipe on touch, a \"See all\" link. The row is a labelled list that scrolls with the keyboard (it takes focus).\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\n\nexport interface ShortsShelfProps<T> extends Omit<React.ComponentProps<\"section\">, \"children\" | \"title\"> {\n /** The shelf's accessible name (\"Shorts\"), passed translated; shown as its heading unless `title` is set. */\n label: string;\n /** A heading different from the name. */\n title?: React.ReactNode;\n /** The clips, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** One card: use `VideoCard aspect=\"portrait\"`. */\n renderItem: (item: T, index: number) => React.ReactNode;\n /** Opens the vertical feed. */\n seeAll?: { href: string; label: string; linkAs?: LinkComponent };\n /** The arrows' names, passed translated. */\n previousLabel: string;\n /** The next arrow's name. */\n nextLabel: string;\n}\n\nconst Chevron = ({ flip }: { flip?: boolean }) => (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={flip ? \"rotate-180\" : undefined}>\n <path d=\"m9 6 6 6-6 6\" />\n </svg>\n);\n\nconst ARROW =\n \"hidden h-9 w-9 items-center justify-center rounded-full border border-border-default bg-surface-1 text-fg shadow-sm transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40 [@media(hover:hover)]:inline-flex\";\n\n/** A Shorts shelf: equal-height 9:16 cards in a snap-scrolling row. */\nexport function ShortsShelf<T>({ label, title, items, getId, renderItem, seeAll, previousLabel, nextLabel, className, ...props }: ShortsShelfProps<T>) {\n const row = React.useRef<HTMLUListElement>(null);\n const [edge, setEdge] = React.useState({ start: true, end: false });\n const measure = React.useCallback(() => {\n const el = row.current;\n if (!el) return;\n setEdge({ start: el.scrollLeft <= 2, end: el.scrollLeft + el.clientWidth >= el.scrollWidth - 2 });\n }, []);\n React.useEffect(measure, [measure, items.length]);\n const page = (dir: 1 | -1) => {\n const el = row.current;\n if (el) el.scrollBy({ left: dir * el.clientWidth * 0.85, behavior: window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\" });\n };\n const headingId = React.useId();\n if (items.length === 0) return null;\n return (\n <section aria-labelledby={headingId} data-shorts-shelf className={cn(\"min-w-0\", className)} {...props}>\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={headingId} className=\"m-0 mr-auto text-base font-bold text-fg\">\n {title ?? label}\n </h2>\n {seeAll && (\n <BlockLink href={seeAll.href} linkAs={seeAll.linkAs} className=\"text-sm font-semibold text-fg-accent hover:underline\">\n {seeAll.label}\n </BlockLink>\n )}\n <button type=\"button\" aria-label={previousLabel} disabled={edge.start} onClick={() => page(-1)} className={ARROW}>\n <Chevron flip />\n </button>\n <button type=\"button\" aria-label={nextLabel} disabled={edge.end} onClick={() => page(1)} className={ARROW}>\n <Chevron />\n </button>\n </div>\n <ul\n ref={row}\n onScroll={measure}\n // biome-ignore: a scroll container must take the keyboard focus to scroll with the arrow keys\n tabIndex={0}\n aria-labelledby={headingId}\n className=\"m-0 flex list-none snap-x snap-mandatory items-stretch gap-3 overflow-x-auto overscroll-x-contain p-0 pb-2 [scrollbar-width:none] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((item, i) => (\n <li key={getId(item)} className=\"w-[9.5rem] shrink-0 snap-start sm:w-44\">\n {renderItem(item, i)}\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","// The links of the page blocks (PageHero's back link, LinkCard, ArgumentCard, ActionBar): an internal path renders\n// through the app's router link (`linkAs`, e.g. Next's `Link`), anything else is an `<a>` that opens in a new tab.\n// Shared by the block entries; never exported on its own.\nimport type * as React from \"react\";\n\n/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */\nexport type LinkComponent = React.ElementType<{ href: string; className?: string; children?: React.ReactNode; \"aria-label\"?: string; \"aria-describedby\"?: string }>;\n\n/** An address that leaves the app: an absolute URL or a protocol (`mailto:`). */\nexport const isExternal = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nexport function BlockLink({\n href,\n linkAs: Link = \"a\",\n external = isExternal(href),\n rel,\n ...props\n}: Omit<React.ComponentProps<\"a\">, \"href\"> & { href: string; linkAs?: LinkComponent; external?: boolean }) {\n if (external) return <a href={href} target=\"_blank\" rel={rel ?? \"noopener noreferrer\"} {...props} />;\n const Comp = Link as React.ElementType;\n return <Comp href={href} {...props} />;\n}\n","// Client (it renders the shelf). Mixed orientations without a broken grid. `split` (the feed default) sends the 9:16\n// clips to a `ShortsShelf` and lays the rest in a uniform 16:9 grid; `uniform` puts everything in one grid of one frame\n// (a source of the other orientation is told to `contain`, never cropped to a face). `layout=\"list\"` is one column.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { ShortsShelf, type ShortsShelfProps } from \"./shorts-shelf\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface MediaGridState {\n /** The frame the card is drawn in (`aspect` of `VideoCard`). */\n aspect: VideoCardAspect;\n /** The card's `layout`. */\n layout: \"stack\" | \"row\";\n /** The source's orientation differs from the frame: pass `fit=\"contain\"`. */\n mismatch: boolean;\n}\n\nexport interface MediaGridProps<T> extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The videos, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** The source's orientation (from its width and height). */\n orientationOf: (item: T) => VideoCardAspect;\n /** One card, drawn in the frame the grid gives (`state`): use `VideoCard`. */\n renderItem: (item: T, state: MediaGridState) => React.ReactNode;\n /**\n * `split`: verticals on the shelf, the others in the grid · `uniform`: all in one frame (`frame`).\n * @default \"split\"\n */\n mode?: \"split\" | \"uniform\";\n /** The frame of `uniform`. @default \"landscape\" */\n frame?: VideoCardAspect;\n /** `grid` or `list` (one row per video). @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The shelf of `split`: its name, \"See all\" and arrows, passed translated. */\n shelf: Pick<ShortsShelfProps<unknown>, \"label\" | \"title\" | \"seeAll\" | \"previousLabel\" | \"nextLabel\">;\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** A media grid that never mixes heights badly: a shelf for the 9:16, a uniform grid for the rest. */\nexport function MediaGrid<T>({ items, getId, orientationOf, renderItem, mode = \"split\", frame = \"landscape\", layout = \"grid\", shelf, className, ...props }: MediaGridProps<T>) {\n const row = layout === \"list\";\n const split = mode === \"split\";\n const verticals = split ? items.filter((i) => orientationOf(i) === \"portrait\") : [];\n const rest = split ? items.filter((i) => orientationOf(i) !== \"portrait\") : items;\n const gridFrame: VideoCardAspect = split ? \"landscape\" : frame;\n const state = (item: T, aspect: VideoCardAspect): MediaGridState => ({ aspect, layout: row ? \"row\" : \"stack\", mismatch: orientationOf(item) !== aspect });\n return (\n <div data-media-grid={mode} className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n {verticals.length > 0 && <ShortsShelf {...shelf} items={verticals} getId={getId} renderItem={(item) => renderItem(item, { ...state(item, \"portrait\"), layout: \"stack\" })} />}\n {rest.length > 0 && (\n <ul data-grid-frame={gridFrame} className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[gridFrame])}>\n {rest.map((item) => (\n <li key={getId(item)} className=\"min-w-0\">\n {renderItem(item, state(item, gridFrame))}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n","// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /** An animated picture (WebP) shown while the card is hovered or focused. */\n previewSrc?: string | null;\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}: VideoCardProps) {\n const [live, setLive] = React.useState(false);\n const picture = live && previewSrc ? previewSrc : poster;\n const row = layout === \"row\";\n return (\n <article\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n onMouseEnter={(e) => (setLive(true), onMouseEnter?.(e))}\n onMouseLeave={(e) => (setLive(false), onMouseLeave?.(e))}\n onFocus={(e) => (setLive(true), onFocus?.(e))}\n onBlur={(e) => (setLive(false), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && picture && <img src={picture} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image src={picture} loading={loading} fallback={fallback} className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")} />\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n <Card.Body className=\"min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n <div className=\"relative z-10 flex h-8 items-center gap-2 text-xs text-fg-secondary\">\n {creator && (\n <>\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"truncate\">{creator.name}</span>\n )}\n </>\n )}\n {meta && <span className=\"ml-auto shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n","// Server-safe: no hook, no context. One `tv` definition with slots: each part takes its slot, and a product overrides\n// any part through `className` (merged last by tailwind-merge).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { cardRecipe } from \"./recipe\";\n\nexport const card = tv(cardRecipe);\n\ntype CardVariants = VariantProps<typeof card>;\n/** The parts without a variant read the default slots: no context, so server-safe. */\nconst slots = card();\n\nexport type CardRootProps = React.ComponentProps<\"div\"> &\n Pick<CardVariants, \"radius\" | \"tone\" | \"interactive\"> & {\n /**\n * md · lg · xl.\n * @default \"xl\"\n */\n radius?: CardVariants[\"radius\"];\n /**\n * default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).\n * @default \"default\"\n */\n tone?: CardVariants[\"tone\"];\n /** The spotlight that follows the pointer and the lift on hover: for a card that is a link. */\n interactive?: CardVariants[\"interactive\"];\n /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */\n reveal?: number;\n };\n\nexport function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n const revealStyle =\n reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <Comp\n data-reveal={reveal === undefined ? undefined : \"\"}\n data-interactive={interactive ? \"\" : undefined}\n style={revealStyle}\n className={card({ radius, tone, interactive }).root({ className })}\n {...props}\n />\n );\n}\n\nconst ASPECT = { video: \"aspect-video\", square: \"aspect-square\", portrait: \"aspect-[3/4]\", auto: \"\" } as const;\nexport type CardMediaProps = React.ComponentProps<\"div\"> & {\n /** The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's). */\n aspect?: keyof typeof ASPECT;\n};\n\nexport function CardMedia({ aspect = \"video\", className, ...props }: CardMediaProps) {\n return <div data-aspect={aspect} className={slots.media({ className: cn(ASPECT[aspect], className) })} {...props} />;\n}\n\nexport type CardImageProps = Omit<React.ComponentProps<\"img\">, \"src\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** Shown when there is no image: an icon, an illustration (decorative). */\n fallback?: React.ReactNode;\n /**\n * When the browser loads it: `lazy` (default, a card in a list or below the fold) or `eager` for the first cards\n * of a page — the image that is the largest contentful paint must not wait (add `fetchPriority=\"high\"`).\n * @default \"lazy\"\n */\n loading?: \"lazy\" | \"eager\";\n};\n\n/** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */\nexport function CardImage({ src, alt = \"\", fallback, loading = \"lazy\", className, ...props }: CardImageProps) {\n if (!src) {\n return (\n <div data-fallback=\"\" aria-hidden className={slots.fallback({ className })}>\n {fallback}\n </div>\n );\n }\n return <img src={src} alt={alt} loading={loading} decoding=\"async\" className={slots.image({ className })} {...props} />;\n}\n\nconst CORNER = {\n \"top-left\": \"left-2.5 top-2.5\",\n \"top-right\": \"right-2.5 top-2.5\",\n \"bottom-left\": \"bottom-2.5 left-2.5\",\n \"bottom-right\": \"bottom-2.5 right-2.5\",\n} as const;\nexport type CardOverlayProps = React.ComponentProps<\"div\"> & {\n /** The corner of the media it sits in. */\n corner?: keyof typeof CORNER;\n};\n\n/** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */\nexport function CardOverlay({ corner = \"top-left\", className, ...props }: CardOverlayProps) {\n return <div data-corner={corner} className={slots.overlay({ className: cn(CORNER[corner], className) })} {...props} />;\n}\n\nexport type CardBodyProps = React.ComponentProps<\"div\"> & {\n /**\n * none · sm · md · lg.\n * @default \"md\"\n */\n padding?: CardVariants[\"padding\"];\n};\n\nexport function CardBody({ padding, className, ...props }: CardBodyProps) {\n return <div className={card({ padding }).body({ className })} {...props} />;\n}\n\nexport type CardTitleProps = React.ComponentProps<\"h3\"> & {\n /** The heading level, to fit the page's outline. */\n as?: \"h2\" | \"h3\" | \"h4\";\n};\n\nexport function CardTitle({ as: Tag = \"h3\", className, ...props }: CardTitleProps) {\n return <Tag className={slots.title({ className })} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return <p className={slots.description({ className })} {...props} />;\n}\n\nexport type CardStatProps = React.ComponentProps<\"div\"> & {\n /** What the figure is, in small capitals above it (the figure is the children). */\n label: React.ReactNode;\n};\n\n/** A figure put forward: the label in capitals, the value in tabular digits. */\nexport function CardStat({ label, className, children, ...props }: CardStatProps) {\n return (\n <div className={slots.stat({ className })} {...props}>\n <p className={slots.statLabel()}>{label}</p>\n <p className={slots.statValue()}>{children}</p>\n </div>\n );\n}\n\nexport function CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={slots.footer({ className })} {...props} />;\n}\n\n/** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */\nexport const Card = {\n Root: CardRoot,\n Media: CardMedia,\n Image: CardImage,\n Overlay: CardOverlay,\n Body: CardBody,\n Title: CardTitle,\n Description: CardDescription,\n Stat: CardStat,\n Footer: CardFooter,\n};\n","// The card's parts, radii, tones and paddings, written once as classes of the Krizaka tokens: the web wears them with\n// `tv(cardRecipe)`, React Native reads the same classes into styles (native/card.tsx): a part of the card that exists only\n// on the web (the media's zoom, the spotlight) is simply ignored there.\nimport { defineRecipe } from \"../recipes/define\";\n\nexport const cardRecipe = defineRecipe({\n slots: {\n root:\n \"group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n media: \"relative overflow-hidden bg-media\",\n image: \"h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105\",\n fallback: \"flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60\",\n overlay: \"absolute z-10 flex items-center gap-1.5\",\n body: \"relative flex flex-1 flex-col gap-3\",\n title: \"line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-fg-accent\",\n description: \"line-clamp-2 text-xs text-fg-secondary\",\n stat: \"min-w-0\",\n statLabel: \"text-[10px] font-semibold uppercase tracking-wider text-fg-secondary\",\n statValue: \"font-display text-xl font-black tabular-nums text-fg\",\n footer: \"relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary\",\n },\n variants: {\n radius: { md: { root: \"rounded-md\" }, lg: { root: \"rounded-lg\" }, xl: { root: \"rounded-xl\" } },\n tone: { default: {}, elevated: { root: \"bg-surface-2 shadow-md\" }, glass: { root: \"bg-surface-1/70 backdrop-blur-md\" } },\n interactive: { true: { root: \"kz-spotlight kz-lift hover:border-accent/50\" } },\n padding: { none: { body: \"p-0\" }, sm: { body: \"p-3\" }, md: { body: \"p-4\" }, lg: { body: \"p-6\" } },\n },\n defaultVariants: { radius: \"xl\", tone: \"default\", padding: \"md\" },\n});\n"],"mappings":"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","names":["jsx","jsx","jsxs","React","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/media-grid/shorts-shelf.tsx","../src/block-link.tsx","../src/media-grid/media-grid.tsx","../src/media-grid/video-card.tsx","../src/card/card.tsx","../src/card/recipe.ts"],"sourcesContent":["// Client (the arrows scroll the row). A horizontal row of 9:16 cards of one height: scroll-snap, arrows with a mouse,\n// a swipe on touch, a \"See all\" link. The row is a labelled list that scrolls with the keyboard (it takes focus).\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\n\nexport interface ShortsShelfProps<T> extends Omit<React.ComponentProps<\"section\">, \"children\" | \"title\"> {\n /** The shelf's accessible name (\"Shorts\"), passed translated; shown as its heading unless `title` is set. */\n label: string;\n /** A heading different from the name. */\n title?: React.ReactNode;\n /** The clips, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** One card: use `VideoCard aspect=\"portrait\"`. */\n renderItem: (item: T, index: number) => React.ReactNode;\n /** Opens the vertical feed. */\n seeAll?: { href: string; label: string; linkAs?: LinkComponent };\n /** The arrows' names, passed translated. */\n previousLabel: string;\n /** The next arrow's name. */\n nextLabel: string;\n}\n\nconst Chevron = ({ flip }: { flip?: boolean }) => (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={flip ? \"rotate-180\" : undefined}>\n <path d=\"m9 6 6 6-6 6\" />\n </svg>\n);\n\nconst ARROW =\n \"hidden h-9 w-9 items-center justify-center rounded-full border border-border-default bg-surface-1 text-fg shadow-sm transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40 [@media(hover:hover)]:inline-flex\";\n\n/** A Shorts shelf: equal-height 9:16 cards in a snap-scrolling row. */\nexport function ShortsShelf<T>({ label, title, items, getId, renderItem, seeAll, previousLabel, nextLabel, className, ...props }: ShortsShelfProps<T>) {\n const row = React.useRef<HTMLUListElement>(null);\n const [edge, setEdge] = React.useState({ start: true, end: false });\n const measure = React.useCallback(() => {\n const el = row.current;\n if (!el) return;\n setEdge({ start: el.scrollLeft <= 2, end: el.scrollLeft + el.clientWidth >= el.scrollWidth - 2 });\n }, []);\n React.useEffect(measure, [measure, items.length]);\n const page = (dir: 1 | -1) => {\n const el = row.current;\n if (el) el.scrollBy({ left: dir * el.clientWidth * 0.85, behavior: window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\" });\n };\n const headingId = React.useId();\n if (items.length === 0) return null;\n return (\n <section aria-labelledby={headingId} data-shorts-shelf className={cn(\"min-w-0\", className)} {...props}>\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={headingId} className=\"m-0 mr-auto text-base font-bold text-fg\">\n {title ?? label}\n </h2>\n {seeAll && (\n <BlockLink href={seeAll.href} linkAs={seeAll.linkAs} className=\"text-sm font-semibold text-fg-accent hover:underline\">\n {seeAll.label}\n </BlockLink>\n )}\n <button type=\"button\" aria-label={previousLabel} disabled={edge.start} onClick={() => page(-1)} className={ARROW}>\n <Chevron flip />\n </button>\n <button type=\"button\" aria-label={nextLabel} disabled={edge.end} onClick={() => page(1)} className={ARROW}>\n <Chevron />\n </button>\n </div>\n <ul\n ref={row}\n onScroll={measure}\n // biome-ignore: a scroll container must take the keyboard focus to scroll with the arrow keys\n tabIndex={0}\n aria-labelledby={headingId}\n className=\"m-0 flex list-none snap-x snap-mandatory items-stretch gap-3 overflow-x-auto overscroll-x-contain p-0 pb-2 [scrollbar-width:none] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((item, i) => (\n <li key={getId(item)} className=\"w-[9.5rem] shrink-0 snap-start sm:w-44\">\n {renderItem(item, i)}\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","// The links of the page blocks (PageHero's back link, LinkCard, ArgumentCard, ActionBar): an internal path renders\n// through the app's router link (`linkAs`, e.g. Next's `Link`), anything else is an `<a>` that opens in a new tab.\n// Shared by the block entries; never exported on its own.\nimport type * as React from \"react\";\n\n/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */\nexport type LinkComponent = React.ElementType<{ href: string; className?: string; children?: React.ReactNode; \"aria-label\"?: string; \"aria-describedby\"?: string }>;\n\n/** An address that leaves the app: an absolute URL or a protocol (`mailto:`). */\nexport const isExternal = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nexport function BlockLink({\n href,\n linkAs: Link = \"a\",\n external = isExternal(href),\n rel,\n ...props\n}: Omit<React.ComponentProps<\"a\">, \"href\"> & { href: string; linkAs?: LinkComponent; external?: boolean }) {\n if (external) return <a href={href} target=\"_blank\" rel={rel ?? \"noopener noreferrer\"} {...props} />;\n const Comp = Link as React.ElementType;\n return <Comp href={href} {...props} />;\n}\n","// Client (it renders the shelf). Mixed orientations without a broken grid. `split` (the feed default) sends the 9:16\n// clips to a `ShortsShelf` and lays the rest in a uniform 16:9 grid; `uniform` puts everything in one grid of one frame\n// (a source of the other orientation is told to `contain`, never cropped to a face). `layout=\"list\"` is one column.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { ShortsShelf, type ShortsShelfProps } from \"./shorts-shelf\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface MediaGridState {\n /** The frame the card is drawn in (`aspect` of `VideoCard`). */\n aspect: VideoCardAspect;\n /** The card's `layout`. */\n layout: \"stack\" | \"row\";\n /** The source's orientation differs from the frame: pass `fit=\"contain\"`. */\n mismatch: boolean;\n}\n\nexport interface MediaGridProps<T> extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The videos, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** The source's orientation (from its width and height). */\n orientationOf: (item: T) => VideoCardAspect;\n /** One card, drawn in the frame the grid gives (`state`): use `VideoCard`. */\n renderItem: (item: T, state: MediaGridState) => React.ReactNode;\n /**\n * `split`: verticals on the shelf, the others in the grid · `uniform`: all in one frame (`frame`).\n * @default \"split\"\n */\n mode?: \"split\" | \"uniform\";\n /** The frame of `uniform`. @default \"landscape\" */\n frame?: VideoCardAspect;\n /** `grid` or `list` (one row per video). @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The shelf of `split`: its name, \"See all\" and arrows, passed translated. */\n shelf: Pick<ShortsShelfProps<unknown>, \"label\" | \"title\" | \"seeAll\" | \"previousLabel\" | \"nextLabel\">;\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** A media grid that never mixes heights badly: a shelf for the 9:16, a uniform grid for the rest. */\nexport function MediaGrid<T>({ items, getId, orientationOf, renderItem, mode = \"split\", frame = \"landscape\", layout = \"grid\", shelf, className, ...props }: MediaGridProps<T>) {\n const row = layout === \"list\";\n const split = mode === \"split\";\n const verticals = split ? items.filter((i) => orientationOf(i) === \"portrait\") : [];\n const rest = split ? items.filter((i) => orientationOf(i) !== \"portrait\") : items;\n const gridFrame: VideoCardAspect = split ? \"landscape\" : frame;\n const state = (item: T, aspect: VideoCardAspect): MediaGridState => ({ aspect, layout: row ? \"row\" : \"stack\", mismatch: orientationOf(item) !== aspect });\n return (\n <div data-media-grid={mode} className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n {verticals.length > 0 && <ShortsShelf {...shelf} items={verticals} getId={getId} renderItem={(item) => renderItem(item, { ...state(item, \"portrait\"), layout: \"stack\" })} />}\n {rest.length > 0 && (\n <ul data-grid-frame={gridFrame} className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[gridFrame])}>\n {rest.map((item) => (\n <li key={getId(item)} className=\"min-w-0\">\n {renderItem(item, state(item, gridFrame))}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n","// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /** An animated picture (WebP) shown while the card is hovered or focused. */\n previewSrc?: string | null;\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}: VideoCardProps) {\n const [live, setLive] = React.useState(false);\n const picture = live && previewSrc ? previewSrc : poster;\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <article\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n onMouseEnter={(e) => (setLive(true), onMouseEnter?.(e))}\n onMouseLeave={(e) => (setLive(false), onMouseLeave?.(e))}\n onFocus={(e) => (setLive(true), onFocus?.(e))}\n onBlur={(e) => (setLive(false), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && picture && <img src={picture} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image src={picture} loading={loading} fallback={fallback} className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")} />\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n <Card.Body className=\"min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n {/* A narrow card (9:16 on a shelf) puts the count and the action on a second line so the name is never cut to an initial. */}\n <div className={cn(\"relative z-10 text-xs text-fg-secondary\", tall ? \"flex flex-col gap-1\" : \"flex h-8 items-center gap-2\")}>\n {creator && (\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"min-w-0 truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"min-w-0 truncate\">{creator.name}</span>\n )}\n </div>\n )}\n {(meta || action) && (\n <div className={cn(\"flex items-center gap-2\", tall ? \"h-7 justify-between\" : \"ml-auto shrink-0\")}>\n {meta && <span className=\"shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n )}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n","// Server-safe: no hook, no context. One `tv` definition with slots: each part takes its slot, and a product overrides\n// any part through `className` (merged last by tailwind-merge).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { cardRecipe } from \"./recipe\";\n\nexport const card = tv(cardRecipe);\n\ntype CardVariants = VariantProps<typeof card>;\n/** The parts without a variant read the default slots: no context, so server-safe. */\nconst slots = card();\n\nexport type CardRootProps = React.ComponentProps<\"div\"> &\n Pick<CardVariants, \"radius\" | \"tone\" | \"interactive\"> & {\n /**\n * md · lg · xl.\n * @default \"xl\"\n */\n radius?: CardVariants[\"radius\"];\n /**\n * default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).\n * @default \"default\"\n */\n tone?: CardVariants[\"tone\"];\n /** The spotlight that follows the pointer and the lift on hover: for a card that is a link. */\n interactive?: CardVariants[\"interactive\"];\n /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */\n reveal?: number;\n };\n\nexport function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n const revealStyle =\n reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <Comp\n data-reveal={reveal === undefined ? undefined : \"\"}\n data-interactive={interactive ? \"\" : undefined}\n style={revealStyle}\n className={card({ radius, tone, interactive }).root({ className })}\n {...props}\n />\n );\n}\n\nconst ASPECT = { video: \"aspect-video\", square: \"aspect-square\", portrait: \"aspect-[3/4]\", auto: \"\" } as const;\nexport type CardMediaProps = React.ComponentProps<\"div\"> & {\n /** The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's). */\n aspect?: keyof typeof ASPECT;\n};\n\nexport function CardMedia({ aspect = \"video\", className, ...props }: CardMediaProps) {\n return <div data-aspect={aspect} className={slots.media({ className: cn(ASPECT[aspect], className) })} {...props} />;\n}\n\nexport type CardImageProps = Omit<React.ComponentProps<\"img\">, \"src\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** Shown when there is no image: an icon, an illustration (decorative). */\n fallback?: React.ReactNode;\n /**\n * When the browser loads it: `lazy` (default, a card in a list or below the fold) or `eager` for the first cards\n * of a page — the image that is the largest contentful paint must not wait (add `fetchPriority=\"high\"`).\n * @default \"lazy\"\n */\n loading?: \"lazy\" | \"eager\";\n};\n\n/** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */\nexport function CardImage({ src, alt = \"\", fallback, loading = \"lazy\", className, ...props }: CardImageProps) {\n if (!src) {\n return (\n <div data-fallback=\"\" aria-hidden className={slots.fallback({ className })}>\n {fallback}\n </div>\n );\n }\n return <img src={src} alt={alt} loading={loading} decoding=\"async\" className={slots.image({ className })} {...props} />;\n}\n\nconst CORNER = {\n \"top-left\": \"left-2.5 top-2.5\",\n \"top-right\": \"right-2.5 top-2.5\",\n \"bottom-left\": \"bottom-2.5 left-2.5\",\n \"bottom-right\": \"bottom-2.5 right-2.5\",\n} as const;\nexport type CardOverlayProps = React.ComponentProps<\"div\"> & {\n /** The corner of the media it sits in. */\n corner?: keyof typeof CORNER;\n};\n\n/** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */\nexport function CardOverlay({ corner = \"top-left\", className, ...props }: CardOverlayProps) {\n return <div data-corner={corner} className={slots.overlay({ className: cn(CORNER[corner], className) })} {...props} />;\n}\n\nexport type CardBodyProps = React.ComponentProps<\"div\"> & {\n /**\n * none · sm · md · lg.\n * @default \"md\"\n */\n padding?: CardVariants[\"padding\"];\n};\n\nexport function CardBody({ padding, className, ...props }: CardBodyProps) {\n return <div className={card({ padding }).body({ className })} {...props} />;\n}\n\nexport type CardTitleProps = React.ComponentProps<\"h3\"> & {\n /** The heading level, to fit the page's outline. */\n as?: \"h2\" | \"h3\" | \"h4\";\n};\n\nexport function CardTitle({ as: Tag = \"h3\", className, ...props }: CardTitleProps) {\n return <Tag className={slots.title({ className })} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return <p className={slots.description({ className })} {...props} />;\n}\n\nexport type CardStatProps = React.ComponentProps<\"div\"> & {\n /** What the figure is, in small capitals above it (the figure is the children). */\n label: React.ReactNode;\n};\n\n/** A figure put forward: the label in capitals, the value in tabular digits. */\nexport function CardStat({ label, className, children, ...props }: CardStatProps) {\n return (\n <div className={slots.stat({ className })} {...props}>\n <p className={slots.statLabel()}>{label}</p>\n <p className={slots.statValue()}>{children}</p>\n </div>\n );\n}\n\nexport function CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={slots.footer({ className })} {...props} />;\n}\n\n/** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */\nexport const Card = {\n Root: CardRoot,\n Media: CardMedia,\n Image: CardImage,\n Overlay: CardOverlay,\n Body: CardBody,\n Title: CardTitle,\n Description: CardDescription,\n Stat: CardStat,\n Footer: CardFooter,\n};\n","// The card's parts, radii, tones and paddings, written once as classes of the Krizaka tokens: the web wears them with\n// `tv(cardRecipe)`, React Native reads the same classes into styles (native/card.tsx): a part of the card that exists only\n// on the web (the media's zoom, the spotlight) is simply ignored there.\nimport { defineRecipe } from \"../recipes/define\";\n\nexport const cardRecipe = defineRecipe({\n slots: {\n root:\n \"group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n media: \"relative overflow-hidden bg-media\",\n image: \"h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105\",\n fallback: \"flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60\",\n overlay: \"absolute z-10 flex items-center gap-1.5\",\n body: \"relative flex flex-1 flex-col gap-3\",\n title: \"line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-fg-accent\",\n description: \"line-clamp-2 text-xs text-fg-secondary\",\n stat: \"min-w-0\",\n statLabel: \"text-[10px] font-semibold uppercase tracking-wider text-fg-secondary\",\n statValue: \"font-display text-xl font-black tabular-nums text-fg\",\n footer: \"relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary\",\n },\n variants: {\n radius: { md: { root: \"rounded-md\" }, lg: { root: \"rounded-lg\" }, xl: { root: \"rounded-xl\" } },\n tone: { default: {}, elevated: { root: \"bg-surface-2 shadow-md\" }, glass: { root: \"bg-surface-1/70 backdrop-blur-md\" } },\n interactive: { true: { root: \"kz-spotlight kz-lift hover:border-accent/50\" } },\n padding: { none: { body: \"p-0\" }, sm: { body: \"p-3\" }, md: { body: \"p-4\" }, lg: { body: \"p-6\" } },\n },\n defaultVariants: { radius: \"xl\", tone: \"default\", padding: \"md\" },\n});\n"],"mappings":"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","names":["jsx","jsx","jsxs","React","jsx","jsxs","jsx","jsxs"]}
package/dist/nav-bar.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  GitHubGlyph,
5
5
  familyLabels,
6
6
  sitesByGroup
7
- } from "./chunk-53LQPYQZ.js";
7
+ } from "./chunk-5MH64NIL.js";
8
8
  import {
9
9
  useSlidingIndicator
10
10
  } from "./chunk-VDR2EP6K.js";
@@ -4,7 +4,7 @@ import {
4
4
  familyLabels,
5
5
  siteById,
6
6
  sitesByGroup
7
- } from "./chunk-53LQPYQZ.js";
7
+ } from "./chunk-5MH64NIL.js";
8
8
  import {
9
9
  KrizakaLogo
10
10
  } from "./chunk-LIPK4UCS.js";
package/dist/sites.js CHANGED
@@ -4,7 +4,7 @@ var sites = [
4
4
  site({ id: "krizaka", name: "Krizaka", url: "https://www.krizaka.com", kind: "company", group: "krizaka", status: "released", mark: "krizaka", repo: "https://github.com/krizaka/krizaka-com" }),
5
5
  site({ id: "orazaka", name: "Orazaka", url: "https://dev.orazaka.com", kind: "product", group: "products", status: "released", mark: "orazaka", repo: "https://github.com/krizaka/orazaka" }),
6
6
  site({ id: "orochia", name: "Orochia", url: "https://dev.orochia.com", kind: "product", group: "products", status: "released", mark: "orochia", repo: "https://github.com/krizaka/orochia" }),
7
- site({ id: "yasakia", name: "Yasakia", url: "https://dev.yasakia.com", kind: "product", group: "products", status: "upcoming", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
7
+ site({ id: "yasakia", name: "Yasakia", url: "https://dev.yasakia.com", kind: "product", group: "products", status: "development", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
8
8
  site({ id: "yororis", name: "Yororis", url: "https://dev.yororis.com", kind: "product", group: "products", status: "construction", mark: "yororis", repo: "https://github.com/krizaka/yororis" }),
9
9
  site({ id: "yomizaka", name: "Yomizaka", url: "https://dev.yomizaka.com", kind: "product", group: "products", status: "development", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
10
10
  // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).
package/dist/sites.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sites/sites.ts"],"sourcesContent":["// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"upcoming\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/sites/sites.ts"],"sourcesContent":["// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "3.18.0",
3
+ "version": "3.18.2",
4
4
  "description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
5
5
  "keywords": [
6
6
  "krizaka",
@@ -629,7 +629,7 @@
629
629
  ],
630
630
  "peerDependencies": {
631
631
  "@krizaka/query": ">=0.2.1",
632
- "@krizaka/tailwind": ">=3.18.0",
632
+ "@krizaka/tailwind": ">=3.18.2",
633
633
  "@maplibre/maplibre-react-native": ">=11",
634
634
  "@shopify/flash-list": ">=2",
635
635
  "expo-camera": ">=16",
@@ -689,8 +689,8 @@
689
689
  "typescript": "^5.9.0",
690
690
  "vitest": "^4.1.11",
691
691
  "zod": "^4.6.5",
692
- "@krizaka/query": "0.2.1",
693
- "@krizaka/config": "0.4.1"
692
+ "@krizaka/config": "0.4.1",
693
+ "@krizaka/query": "0.2.1"
694
694
  },
695
695
  "engines": {
696
696
  "node": ">=22"
@@ -2143,7 +2143,7 @@
2143
2143
  "sonner": "^2.0.8",
2144
2144
  "tailwind-variants": "^3.3.1",
2145
2145
  "@krizaka/intl": "^0.2.1",
2146
- "@krizaka/tokens": "^3.18.0",
2146
+ "@krizaka/tokens": "^3.18.2",
2147
2147
  "@krizaka/pacer": "^0.1.0"
2148
2148
  },
2149
2149
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "3.18.0",
3
+ "version": "3.18.2",
4
4
  "templatePurposes": {
5
5
  "convince": {
6
6
  "title": "Convince",
@@ -1750,7 +1750,7 @@
1750
1750
  "native": 2
1751
1751
  },
1752
1752
  "dependencies": [
1753
- "@krizaka/tokens@^3.18.0",
1753
+ "@krizaka/tokens@^3.18.2",
1754
1754
  "@protomaps/basemaps@5.7.2",
1755
1755
  "tailwind-variants@^3.3.1"
1756
1756
  ],
@@ -1978,7 +1978,7 @@
1978
1978
  "native": 0
1979
1979
  },
1980
1980
  "dependencies": [
1981
- "@krizaka/tokens@^3.18.0",
1981
+ "@krizaka/tokens@^3.18.2",
1982
1982
  "radix-ui@^1.7.0",
1983
1983
  "tailwind-variants@^3.3.1"
1984
1984
  ],
@@ -2494,7 +2494,7 @@
2494
2494
  "native": 0
2495
2495
  },
2496
2496
  "dependencies": [
2497
- "@krizaka/tokens@^3.18.0",
2497
+ "@krizaka/tokens@^3.18.2",
2498
2498
  "radix-ui@^1.7.0",
2499
2499
  "tailwind-variants@^3.3.1"
2500
2500
  ],
package/registry/map.json CHANGED
@@ -930,7 +930,7 @@
930
930
  }
931
931
  ],
932
932
  "dependencies": [
933
- "@krizaka/tokens@^3.18.0",
933
+ "@krizaka/tokens@^3.18.2",
934
934
  "@protomaps/basemaps@5.7.2",
935
935
  "tailwind-variants@^3.3.1"
936
936
  ],
@@ -347,7 +347,7 @@
347
347
  },
348
348
  {
349
349
  "path": "media-grid/video-card.tsx",
350
- "content": "// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /** An animated picture (WebP) shown while the card is hovered or focused. */\n previewSrc?: string | null;\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}: VideoCardProps) {\n const [live, setLive] = React.useState(false);\n const picture = live && previewSrc ? previewSrc : poster;\n const row = layout === \"row\";\n return (\n <article\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n onMouseEnter={(e) => (setLive(true), onMouseEnter?.(e))}\n onMouseLeave={(e) => (setLive(false), onMouseLeave?.(e))}\n onFocus={(e) => (setLive(true), onFocus?.(e))}\n onBlur={(e) => (setLive(false), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && picture && <img src={picture} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image src={picture} loading={loading} fallback={fallback} className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")} />\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n <Card.Body className=\"min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n <div className=\"relative z-10 flex h-8 items-center gap-2 text-xs text-fg-secondary\">\n {creator && (\n <>\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"truncate\">{creator.name}</span>\n )}\n </>\n )}\n {meta && <span className=\"ml-auto shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n"
350
+ "content": "// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /** An animated picture (WebP) shown while the card is hovered or focused. */\n previewSrc?: string | null;\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...props\n}: VideoCardProps) {\n const [live, setLive] = React.useState(false);\n const picture = live && previewSrc ? previewSrc : poster;\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <article\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n onMouseEnter={(e) => (setLive(true), onMouseEnter?.(e))}\n onMouseLeave={(e) => (setLive(false), onMouseLeave?.(e))}\n onFocus={(e) => (setLive(true), onFocus?.(e))}\n onBlur={(e) => (setLive(false), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && picture && <img src={picture} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image src={picture} loading={loading} fallback={fallback} className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")} />\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n <Card.Body className=\"min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n {/* A narrow card (9:16 on a shelf) puts the count and the action on a second line so the name is never cut to an initial. */}\n <div className={cn(\"relative z-10 text-xs text-fg-secondary\", tall ? \"flex flex-col gap-1\" : \"flex h-8 items-center gap-2\")}>\n {creator && (\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"min-w-0 truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"min-w-0 truncate\">{creator.name}</span>\n )}\n </div>\n )}\n {(meta || action) && (\n <div className={cn(\"flex items-center gap-2\", tall ? \"h-7 justify-between\" : \"ml-auto shrink-0\")}>\n {meta && <span className=\"shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n )}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n"
351
351
  },
352
352
  {
353
353
  "path": "meta.ts",
@@ -494,7 +494,7 @@
494
494
  }
495
495
  ],
496
496
  "dependencies": [
497
- "@krizaka/tokens@^3.18.0",
497
+ "@krizaka/tokens@^3.18.2",
498
498
  "radix-ui@^1.7.0",
499
499
  "tailwind-variants@^3.3.1"
500
500
  ],
@@ -291,7 +291,7 @@
291
291
  }
292
292
  ],
293
293
  "dependencies": [
294
- "@krizaka/tokens@^3.18.0",
294
+ "@krizaka/tokens@^3.18.2",
295
295
  "radix-ui@^1.7.0",
296
296
  "tailwind-variants@^3.3.1"
297
297
  ],
@@ -80,7 +80,7 @@
80
80
  },
81
81
  {
82
82
  "path": "sites/sites.ts",
83
- "content": "// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"upcoming\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"
83
+ "content": "// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"
84
84
  }
85
85
  ],
86
86
  "dependencies": [],
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/family-mark.tsx","../src/sites/sites.ts"],"sourcesContent":["// The mark of a site of the family (`@krizaka/ui/sites`): its brand's `ProductLogo`, still (a list of marks never\n// moves), or GitHub's, in the text colour. Shared by `NavBar.Family` and `SiteFooter` — not an entry of its own.\nimport type * as React from \"react\";\n\nimport { ProductLogo } from \"./marks/ProductLogo\";\nimport type { SiteMark } from \"./sites/sites\";\n\n/** GitHub's mark, in the text colour (both themes). */\nexport function GitHubGlyph({ size = 16, className }: { size?: number; className?: string }) {\n return (\n <svg viewBox=\"0 0 16 16\" width={size} height={size} fill=\"currentColor\" aria-hidden className={className}>\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z\" />\n </svg>\n );\n}\n\n/** A site's mark, decorative (its name is always written beside it). */\nexport function FamilyMark({ mark, size }: { mark: SiteMark; size: number }): React.ReactElement {\n if (mark === \"github\") return <GitHubGlyph size={Math.round(size * 0.8)} />;\n return <ProductLogo id={mark} size={size} animated={false} />;\n}\n","// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"upcoming\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"],"mappings":"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","names":[]}