@krizaka/ui 3.40.0 → 3.41.1
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.
- package/dist/{chunk-7JFHTPI3.js → chunk-CC2FBLLR.js} +8 -8
- package/dist/{chunk-7JFHTPI3.js.map → chunk-CC2FBLLR.js.map} +1 -1
- package/dist/flock.d.ts +7 -1
- package/dist/flock.js +35 -7
- package/dist/flock.js.map +1 -1
- package/dist/nav-bar.js +1 -1
- package/dist/site-footer.js +1 -1
- package/dist/sites.js +7 -7
- package/dist/sites.js.map +1 -1
- package/package.json +5 -5
- package/registry/action-bar.json +1 -1
- package/registry/examples/action-bar/icons.js +1 -1
- package/registry/examples/action-bar/icons.tsx +1 -1
- package/registry/examples/progress/bar-sizes.js +1 -1
- package/registry/examples/progress/bar-sizes.tsx +1 -1
- package/registry/examples/specimen-tag/product.js +1 -1
- package/registry/examples/specimen-tag/product.tsx +1 -1
- package/registry/flock.json +45 -2
- package/registry/index.json +5 -5
- package/registry/map.json +1 -1
- package/registry/nav-bar.json +1 -1
- package/registry/progress.json +1 -1
- package/registry/rail-nav.json +1 -1
- package/registry/site-footer.json +1 -1
- package/registry/sites.json +1 -1
- package/registry/specimen-tag.json +2 -2
|
@@ -17,13 +17,13 @@ function FamilyMark({ mark, size }) {
|
|
|
17
17
|
var site = (s) => ({ ...s, host: s.url.replace(/^https?:\/\//, "").replace(/\/$/, "") });
|
|
18
18
|
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
|
-
site({ id: "orazaka", name: "Orazaka", url: "https://
|
|
21
|
-
site({ id: "orochia", name: "Orochia", url: "https://
|
|
22
|
-
site({ id: "yasakia", name: "Yasakia", url: "https://
|
|
23
|
-
site({ id: "yororis", name: "Yororis", url: "https://
|
|
24
|
-
site({ id: "yomizaka", name: "Yomizaka", url: "https://
|
|
25
|
-
site({ id: "kachizaka", name: "Kachizaka", url: "https://
|
|
26
|
-
site({ id: "akinaza", name: "Akinaza", url: "https://
|
|
20
|
+
site({ id: "orazaka", name: "Orazaka", url: "https://www.orazaka.com", kind: "product", group: "products", status: "released", mark: "orazaka", repo: "https://github.com/krizaka/orazaka" }),
|
|
21
|
+
site({ id: "orochia", name: "Orochia", url: "https://www.orochia.com", kind: "product", group: "products", status: "released", mark: "orochia", repo: "https://github.com/krizaka/orochia" }),
|
|
22
|
+
site({ id: "yasakia", name: "Yasakia", url: "https://www.yasakia.com", kind: "product", group: "products", status: "development", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
|
|
23
|
+
site({ id: "yororis", name: "Yororis", url: "https://www.yororis.com", kind: "product", group: "products", status: "construction", mark: "yororis", repo: "https://github.com/krizaka/yororis" }),
|
|
24
|
+
site({ id: "yomizaka", name: "Yomizaka", url: "https://www.yomizaka.com", kind: "product", group: "products", status: "development", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
|
|
25
|
+
site({ id: "kachizaka", name: "Kachizaka", url: "https://www.kachizaka.com", kind: "product", group: "products", status: "development", mark: "kachizaka", repo: "https://github.com/krizaka/kachizaka" }),
|
|
26
|
+
site({ id: "akinaza", name: "Akinaza", url: "https://www.akinaza.com", kind: "product", group: "products", status: "development", mark: "akinaza", repo: "https://github.com/krizaka/akinaza" }),
|
|
27
27
|
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" }),
|
|
28
28
|
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" }),
|
|
29
29
|
// The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.
|
|
@@ -105,4 +105,4 @@ export {
|
|
|
105
105
|
companyLinks,
|
|
106
106
|
familyLabels
|
|
107
107
|
};
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-CC2FBLLR.js.map
|
|
@@ -1 +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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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 site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://dev.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://dev.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\n },\n};\n"],"mappings":";;;;;;AAWM;AAHC,SAAS,YAAY,EAAE,OAAO,IAAI,UAAU,GAA0C;AAC3F,SACE,oBAAC,SAAI,SAAQ,aAAY,OAAO,MAAM,QAAQ,MAAM,MAAK,gBAAe,eAAW,MAAC,WAClF,8BAAC,UAAK,GAAE,8jBAA6jB,GACvkB;AAEJ;AAGO,SAAS,WAAW,EAAE,MAAM,KAAK,GAAyD;AAC/F,MAAI,SAAS,SAAU,QAAO,oBAAC,eAAY,MAAM,KAAK,MAAM,OAAO,GAAG,GAAG;AACzE,SAAO,oBAAC,eAAY,IAAI,MAAM,MAAY,UAAU,OAAO;AAC7D;;;ACsBA,IAAM,OAAO,CAAC,OAAiC,EAAE,GAAG,GAAG,MAAM,EAAE,IAAI,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,OAAO,EAAE,EAAE;AAG3G,IAAM,QAAyB;AAAA,EACpC,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,WAAW,QAAQ,YAAY,MAAM,WAAW,MAAM,yCAAyC,CAAC;AAAA,EAC/L,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,YAAY,QAAQ,YAAY,MAAM,WAAW,MAAM,qCAAqC,CAAC;AAAA,EAC5L,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,YAAY,QAAQ,YAAY,MAAM,WAAW,MAAM,qCAAqC,CAAC;AAAA,EAC5L,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,YAAY,QAAQ,eAAe,MAAM,WAAW,MAAM,qCAAqC,CAAC;AAAA,EAC/L,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,YAAY,QAAQ,gBAAgB,MAAM,WAAW,MAAM,qCAAqC,CAAC;AAAA,EAChM,KAAK,EAAE,IAAI,YAAY,MAAM,YAAY,KAAK,4BAA4B,MAAM,WAAW,OAAO,YAAY,QAAQ,eAAe,MAAM,YAAY,MAAM,sCAAsC,CAAC;AAAA,EACpM,KAAK,EAAE,IAAI,aAAa,MAAM,aAAa,KAAK,6BAA6B,MAAM,WAAW,OAAO,YAAY,QAAQ,eAAe,MAAM,aAAa,MAAM,uCAAuC,CAAC;AAAA,EACzM,KAAK,EAAE,IAAI,WAAW,MAAM,WAAW,KAAK,2BAA2B,MAAM,WAAW,OAAO,YAAY,QAAQ,eAAe,MAAM,WAAW,MAAM,qCAAqC,CAAC;AAAA,EAC/L,KAAK,EAAE,IAAI,MAAM,MAAM,cAAc,KAAK,0BAA0B,MAAM,eAAe,OAAO,eAAe,QAAQ,YAAY,MAAM,WAAW,MAAM,wCAAwC,CAAC;AAAA,EACnM,KAAK,EAAE,IAAI,QAAQ,MAAM,gBAAgB,KAAK,4BAA4B,MAAM,eAAe,OAAO,eAAe,QAAQ,YAAY,MAAM,WAAW,MAAM,2CAA2C,CAAC;AAAA;AAAA,EAE5M,KAAK,EAAE,IAAI,UAAU,MAAM,kBAAkB,KAAK,4CAA4C,MAAM,eAAe,OAAO,eAAe,QAAQ,eAAe,MAAM,WAAW,MAAM,4CAA4C,CAAC;AAAA,EACpO,KAAK,EAAE,IAAI,UAAU,MAAM,UAAU,KAAK,8BAA8B,MAAM,eAAe,OAAO,UAAU,QAAQ,YAAY,MAAM,UAAU,MAAM,6BAA6B,CAAC;AACxL;AAGO,IAAM,aAAmC,CAAC,WAAW,YAAY,eAAe,QAAQ;AAGxF,SAAS,SAAS,IAAkB;AACzC,QAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC3C,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,yBAAyB,EAAE,IAAI;AAC3D,SAAO;AACT;AAGO,SAAS,eAAsD;AACpE,SAAO,WAAW,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,SAAS,CAAC;AAC/H;AAOO,IAAM,eAAkF;AAAA,EAC7F,EAAE,IAAI,SAAS,KAAK,EAAE,IAAI,iCAAiC,IAAI,mCAAmC,EAAE;AAAA,EACpG,EAAE,IAAI,QAAQ,KAAK,EAAE,IAAI,4BAA4B,IAAI,2BAA2B,EAAE;AAAA,EACtF,EAAE,IAAI,WAAW,KAAK,EAAE,IAAI,mCAAmC,IAAI,qCAAqC,EAAE;AAC5G;AA2BO,IAAM,eAAiD;AAAA,EAC5D,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,QAAQ,EAAE,SAAS,WAAW,UAAU,YAAY,eAAe,eAAe,QAAQ,SAAS;AAAA,IACnG,QAAQ,EAAE,UAAU,eAAe,cAAc,sBAAsB,aAAa,iBAAiB;AAAA,IACrG,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,IACA,WAAW,EAAE,SAAS,qBAAqB,SAAS,qBAAqB,eAAe,gCAAgC;AAAA,IACxH,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS,EAAE,OAAO,aAAa,MAAM,QAAQ,SAAS,aAAa;AAAA,EACrE;AAAA,EACA,IAAI;AAAA,IACF,QAAQ;AAAA,IACR,QAAQ,EAAE,SAAS,WAAW,UAAU,YAAY,eAAe,eAAe,QAAQ,SAAS;AAAA,IACnG,QAAQ,EAAE,UAAU,cAAW,cAAc,mBAAmB,aAAa,sBAAmB;AAAA,IAChG,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,IACA,WAAW,EAAE,SAAS,yBAAyB,SAAS,sBAAsB,eAAe,mCAAmC;AAAA,IAChI,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS,EAAE,OAAO,kBAAkB,MAAM,UAAU,SAAS,iBAAc;AAAA,EAC7E;AACF;","names":[]}
|
|
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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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://www.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://www.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://www.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://www.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://www.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://www.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://www.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\n },\n};\n"],"mappings":"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","names":[]}
|
package/dist/flock.d.ts
CHANGED
|
@@ -28,6 +28,12 @@ type FlockProps = Omit<React.ComponentProps<"div">, "children"> & FlockMoments &
|
|
|
28
28
|
tone?: "brand" | "own";
|
|
29
29
|
/** The source of chance (default `Math.random`): give it a fixed one in a test. */
|
|
30
30
|
random?: () => number;
|
|
31
|
+
/** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */
|
|
32
|
+
tempo?: number;
|
|
33
|
+
/** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */
|
|
34
|
+
active?: MascotBird | null;
|
|
35
|
+
/** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */
|
|
36
|
+
greet?: boolean;
|
|
31
37
|
};
|
|
32
38
|
/**
|
|
33
39
|
* The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every
|
|
@@ -35,6 +41,6 @@ type FlockProps = Omit<React.ComponentProps<"div">, "children"> & FlockMoments &
|
|
|
35
41
|
* returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still
|
|
36
42
|
* under `prefers-reduced-motion`.
|
|
37
43
|
*/
|
|
38
|
-
declare function Flock({ birds, ratio, tone, glance, courier, react, random, className, style, ...props }: FlockProps): React.JSX.Element;
|
|
44
|
+
declare function Flock({ birds, ratio, tone, glance, courier, react, random, tempo, active, greet, className, style, ...props }: FlockProps): React.JSX.Element;
|
|
39
45
|
|
|
40
46
|
export { Flock, type FlockBird, type FlockMoments, type FlockProps };
|
package/dist/flock.js
CHANGED
|
@@ -16,7 +16,9 @@ var COURIER_EVERY_MS = [26e3, 38e3];
|
|
|
16
16
|
var FLIGHT_MS = 2600;
|
|
17
17
|
var HAND_OVER_MS = 1300;
|
|
18
18
|
var LOOK_BACK_MS = 1600;
|
|
19
|
-
var between = (random, [lo, hi]) => lo + random() * (hi - lo);
|
|
19
|
+
var between = (random, [lo, hi], tempo = 1) => (lo + random() * (hi - lo)) / tempo;
|
|
20
|
+
var GREET_STEP_MS = 110;
|
|
21
|
+
var POKE_MS = 700;
|
|
20
22
|
function useMayMove() {
|
|
21
23
|
const [may, setMay] = React.useState(false);
|
|
22
24
|
React.useEffect(() => {
|
|
@@ -43,7 +45,7 @@ function useOnScreen(ref) {
|
|
|
43
45
|
}, [ref]);
|
|
44
46
|
return on;
|
|
45
47
|
}
|
|
46
|
-
function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }) {
|
|
48
|
+
function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }) {
|
|
47
49
|
const stage = React.useRef(null);
|
|
48
50
|
const bodies = React.useRef([]);
|
|
49
51
|
const may = useMayMove();
|
|
@@ -69,14 +71,14 @@ function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier =
|
|
|
69
71
|
rest(a, b);
|
|
70
72
|
next();
|
|
71
73
|
}, between(random, GLANCE_HOLD_MS));
|
|
72
|
-
}, between(random, GLANCE_EVERY_MS));
|
|
74
|
+
}, between(random, GLANCE_EVERY_MS, tempo));
|
|
73
75
|
};
|
|
74
76
|
next();
|
|
75
77
|
return () => {
|
|
76
78
|
clearTimeout(timer);
|
|
77
79
|
clearTimeout(back);
|
|
78
80
|
};
|
|
79
|
-
}, [running, glance, order, random]);
|
|
81
|
+
}, [running, glance, order, random, tempo]);
|
|
80
82
|
React.useEffect(() => {
|
|
81
83
|
const pigeon = birds.findIndex((b) => b.bird === "pigeon");
|
|
82
84
|
if (!running || !courier || pigeon < 0 || birds.length < 2) return;
|
|
@@ -122,17 +124,43 @@ function Flock({ birds, ratio = 16 / 9, tone = "brand", glance = true, courier =
|
|
|
122
124
|
const schedule = (ms) => {
|
|
123
125
|
timer = setTimeout(async () => {
|
|
124
126
|
await trip();
|
|
125
|
-
if (alive) schedule(between(random, COURIER_EVERY_MS));
|
|
127
|
+
if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));
|
|
126
128
|
}, ms);
|
|
127
129
|
};
|
|
128
|
-
schedule(COURIER_FIRST_MS);
|
|
130
|
+
schedule(COURIER_FIRST_MS / tempo);
|
|
129
131
|
return () => {
|
|
130
132
|
alive = false;
|
|
131
133
|
clearTimeout(timer);
|
|
132
134
|
animation?.cancel();
|
|
133
135
|
setFlying(null);
|
|
134
136
|
};
|
|
135
|
-
}, [running, courier, birds, random]);
|
|
137
|
+
}, [running, courier, birds, random, tempo]);
|
|
138
|
+
const greeted = React.useRef(false);
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
if (!running || !greet || greeted.current) return;
|
|
141
|
+
greeted.current = true;
|
|
142
|
+
const timers = [];
|
|
143
|
+
order.forEach((index, n) => {
|
|
144
|
+
timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));
|
|
145
|
+
timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));
|
|
146
|
+
});
|
|
147
|
+
return () => timers.forEach(clearTimeout);
|
|
148
|
+
}, [running, greet, order]);
|
|
149
|
+
React.useEffect(() => {
|
|
150
|
+
const index = active ? birds.findIndex((b) => b.bird === active) : -1;
|
|
151
|
+
if (!running || index < 0) return;
|
|
152
|
+
const start = setTimeout(() => {
|
|
153
|
+
look(index, "up");
|
|
154
|
+
setPoked((p) => ({ ...p, [index]: true }));
|
|
155
|
+
}, 0);
|
|
156
|
+
const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);
|
|
157
|
+
return () => {
|
|
158
|
+
clearTimeout(start);
|
|
159
|
+
clearTimeout(t);
|
|
160
|
+
setPoked((p) => ({ ...p, [index]: false }));
|
|
161
|
+
rest(index);
|
|
162
|
+
};
|
|
163
|
+
}, [active, running, birds]);
|
|
136
164
|
const hover = (index, event) => {
|
|
137
165
|
if (!running || !react || event.pointerType !== "mouse") return;
|
|
138
166
|
look(index, "up");
|
package/dist/flock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flock/flock.tsx"],"sourcesContent":["\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number]) => lo + random() * (hi - lo);\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS));\n }, ms);\n };\n schedule(COURIER_FIRST_MS);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAMA,YAAY,WAAW;AAoOb;AA5LV,IAAM,iBAAiB,CAAC,MAAM,IAAI;AAClC,IAAM,kBAAkB,CAAC,MAAM,GAAI;AACnC,IAAM,mBAAmB;AACzB,IAAM,mBAAmB,CAAC,MAAO,IAAK;AACtC,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,eAAe;AAErB,IAAM,UAAU,CAAC,QAAsB,CAAC,IAAI,EAAE,MAAiC,KAAK,OAAO,KAAK,KAAK;AAGrG,SAAS,aAAsB;AAC7B,QAAM,CAAC,KAAK,MAAM,IAAU,eAAS,KAAK;AAC1C,EAAM,gBAAU,MAAM;AACpB,UAAM,QAAQ,OAAO,WAAW,kCAAkC;AAClE,UAAM,SAAS,MAAM,OAAO,CAAC,MAAM,WAAW,SAAS,oBAAoB,QAAQ;AACnF,WAAO;AACP,UAAM,iBAAiB,UAAU,MAAM;AACvC,aAAS,iBAAiB,oBAAoB,MAAM;AACpD,WAAO,MAAM;AACX,YAAM,oBAAoB,UAAU,MAAM;AAC1C,eAAS,oBAAoB,oBAAoB,MAAM;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAGA,SAAS,YAAY,KAA+C;AAClE,QAAM,CAAC,IAAI,KAAK,IAAU,eAAS,KAAK;AACxC,EAAM,gBAAU,MAAM;AACpB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,MAAM,OAAO,yBAAyB,YAAa;AACxD,UAAM,WAAW,IAAI,qBAAqB,CAAC,CAAC,KAAK,MAAM,MAAM,QAAQ,OAAO,cAAc,CAAC,GAAG,EAAE,WAAW,IAAI,CAAC;AAChH,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,GAAG,CAAC;AACR,SAAO;AACT;AAQO,SAAS,MAAM,EAAE,OAAO,QAAQ,KAAK,GAAG,OAAO,SAAS,SAAS,MAAM,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAK,QAAQ,WAAW,OAAO,GAAG,MAAM,GAAe;AAC1K,QAAM,QAAc,aAAuB,IAAI;AAC/C,QAAM,SAAe,aAAkC,CAAC,CAAC;AACzD,QAAM,MAAM,WAAW;AACvB,QAAM,WAAW,YAAY,KAAK;AAClC,QAAM,UAAU,OAAO;AACvB,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAqC,CAAC,CAAC;AACvE,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAiD,IAAI;AACvF,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAkC,CAAC,CAAC;AAGpE,QAAM,QAAc,cAAQ,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,EAAG,IAAI,MAAM,CAAC,EAAG,CAAC,GAAG,CAAC,KAAK,CAAC;AAC3G,QAAM,OAAO,CAAC,OAAe,OAAmB,SAAS,CAAC,MAAO,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAE;AAC7G,QAAM,OAAO,IAAI,YAAsB,SAAS,CAAC,MAAM,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,SAAS,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAG1I,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,UAAU,MAAM,SAAS,EAAG;AAC7C,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,MAAM;AACjB,cAAQ,WAAW,MAAM;AACvB,cAAM,KAAK,KAAK,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE;AACnD,cAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAI,MAAM,KAAK,CAAC,CAAE;AAC1C,aAAK,GAAG,OAAO;AACf,aAAK,GAAG,MAAM;AACd,eAAO,WAAW,MAAM;AACtB,eAAK,GAAG,CAAC;AACT,eAAK;AAAA,QACP,GAAG,QAAQ,QAAQ,cAAc,CAAC;AAAA,MACpC,GAAG,QAAQ,QAAQ,eAAe,CAAC;AAAA,IACrC;AACA,SAAK;AACL,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,OAAO,MAAM,CAAC;AAGnC,EAAM,gBAAU,MAAM;AACpB,UAAM,SAAS,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,QAAQ;AACzD,QAAI,CAAC,WAAW,CAAC,WAAW,SAAS,KAAK,MAAM,SAAS,EAAG;AAC5D,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,CAAC,OAAe,IAAI,QAAc,CAAC,YAAa,QAAQ,WAAW,SAAS,EAAE,CAAE;AAC7F,UAAM,OAAO,YAAY;AACvB,YAAM,KAAK,OAAO,QAAQ,MAAM;AAChC,YAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,UAAI,CAAC,MAAM,CAAC,OAAO,OAAO,GAAG,YAAY,WAAY;AAErD,YAAM,OAAO,MAAM,MAAM;AACzB,YAAM,SAAS,MAAM,OAAO,CAAC,KAAK,GAAG,MAAO,MAAM,UAAU,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,MAAM,GAAG,EAAG,IAAI,KAAK,CAAC,IAAI,IAAI,KAAM,WAAW,IAAI,IAAI,CAAC;AACtJ,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,OAAO,GAAG,IAAI,KAAK,IAAI,IAAI;AAEjC,YAAM,MAAO,GAAG,IAAI,KAAK,KAAK,MAAO,IAAI,QAAQ,QAAQ,GAAG,OAAO,OAAO,IAAI,QAAQ;AACtF,YAAM,OAAQ,KAAK,OAAO,MAAO,IAAI,QAAQ;AAC7C,YAAM,MAAM,OAAO,OAAe,QAAgB;AAChD,kBAAU,EAAE,OAAO,QAAQ,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC;AACzD,oBAAY,GAAG;AAAA,UACb;AAAA,YACE,EAAE,WAAW,aAAa,KAAK,UAAU,QAAQ,WAAW;AAAA,YAC5D,EAAE,WAAW,cAAc,QAAQ,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,QAAQ,WAAW,QAAQ,IAAI;AAAA,YAC7F,EAAE,WAAW,aAAa,GAAG,SAAS;AAAA,UACxC;AAAA,UACA,EAAE,UAAU,WAAW,MAAM,WAAW;AAAA,QAC1C;AACA,cAAM,UAAU,SAAS,MAAM,MAAM,MAAS;AAC9C,kBAAU,IAAI;AAAA,MAChB;AACA,WAAK,QAAQ,OAAO,IAAI,SAAS,OAAO;AACxC,YAAM,IAAI,GAAG,EAAE;AACf,UAAI,CAAC,MAAO;AAEZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE;AAC1C,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,MAAO;AACZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE;AAC3C,WAAK,QAAQ,IAAI;AACjB,YAAM,IAAI,IAAI,CAAC;AACf,iBAAW,OAAO;AAClB,WAAK,MAAM;AAAA,IACb;AACA,UAAM,WAAW,CAAC,OAAe;AAC/B,cAAQ,WAAW,YAAY;AAC7B,cAAM,KAAK;AACX,YAAI,MAAO,UAAS,QAAQ,QAAQ,gBAAgB,CAAC;AAAA,MACvD,GAAG,EAAE;AAAA,IACP;AACA,aAAS,gBAAgB;AACzB,WAAO,MAAM;AACX,cAAQ;AACR,mBAAa,KAAK;AAClB,iBAAW,OAAO;AAClB,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,OAAO,MAAM,CAAC;AAGpC,QAAM,QAAQ,CAAC,OAAe,UAA8B;AAC1D,QAAI,CAAC,WAAW,CAAC,SAAS,MAAM,gBAAgB,QAAS;AACzD,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,QAAM,QAAQ,CAAC,UAAkB;AAC/B,QAAI,CAAC,MAAO;AACZ,SAAK,KAAK;AAAA,EACZ;AACA,QAAM,MAAM,CAAC,UAAkB;AAC7B,QAAI,CAAC,WAAW,CAAC,MAAO;AACxB,SAAK,OAAO,IAAI;AAChB,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE;AACzC,eAAW,MAAM;AACf,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE;AAC1C,WAAK,KAAK;AAAA,IACZ,GAAG,YAAY;AAAA,EACjB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,eAAW;AAAA,MACX,cAAW;AAAA,MACX,gBAAc,UAAU,KAAK;AAAA,MAC7B,WAAW,GAAG,mBAAmB,SAAS;AAAA,MAC1C,OAAO,EAAE,aAAa,OAAO,KAAK,GAAG,eAAe,eAAe,GAAG,MAAM;AAAA,MAC3E,GAAG;AAAA,MAEH,gBAAM,IAAI,CAAC,GAAG,MACb;AAAA,QAAC;AAAA;AAAA,UAEC,KAAK,CAAC,OAAO;AACX,mBAAO,QAAQ,CAAC,IAAI;AAAA,UACtB;AAAA,UACA,mBAAiB,EAAE;AAAA,UACnB,WAAU;AAAA,UACV,OAAO,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,EAAE,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,aAAa,KAAK,eAAe,QAAQ,SAAS,OAAO;AAAA,UAChK,gBAAgB,CAAC,UAAU,MAAM,GAAG,KAAK;AAAA,UACzC,gBAAgB,MAAM,MAAM,CAAC;AAAA,UAC7B,eAAe,MAAM,IAAI,CAAC;AAAA,UAE1B;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,EAAE;AAAA,cACR;AAAA,cACA,MAAI;AAAA,cACJ,MAAM,MAAM,CAAC;AAAA,cACb,QAAQ,QAAQ,UAAU;AAAA,cAC1B,MAAM,QAAQ,UAAU,IAAI,OAAO,OAAO;AAAA,cAC1C,OAAO,MAAM,CAAC;AAAA,cACd,MAAM;AAAA,cACN,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,SAAS,SAAS,UAAU,UAAU;AAAA;AAAA,UAChF;AAAA;AAAA,QArBK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAsBrB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/flock/flock.tsx"],"sourcesContent":["\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n /** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */\n tempo?: number;\n /** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */\n active?: MascotBird | null;\n /** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */\n greet?: boolean;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number], tempo = 1) => (lo + random() * (hi - lo)) / tempo;\nconst GREET_STEP_MS = 110;\nconst POKE_MS = 700;\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS, tempo));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random, tempo]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));\n }, ms);\n };\n schedule(COURIER_FIRST_MS / tempo);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random, tempo]);\n\n // The greeting: the first time the flock is on screen, each bird jumps in turn.\n const greeted = React.useRef(false);\n React.useEffect(() => {\n if (!running || !greet || greeted.current) return;\n greeted.current = true;\n const timers: ReturnType<typeof setTimeout>[] = [];\n order.forEach((index, n) => {\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));\n });\n return () => timers.forEach(clearTimeout);\n }, [running, greet, order]);\n\n // The bird the page points at looks up and jumps.\n React.useEffect(() => {\n const index = active ? birds.findIndex((b) => b.bird === active) : -1;\n if (!running || index < 0) return;\n const start = setTimeout(() => {\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n }, 0);\n const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);\n return () => {\n clearTimeout(start);\n clearTimeout(t);\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n };\n }, [active, running, birds]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAMA,YAAY,WAAW;AA0Qb;AA5NV,IAAM,iBAAiB,CAAC,MAAM,IAAI;AAClC,IAAM,kBAAkB,CAAC,MAAM,GAAI;AACnC,IAAM,mBAAmB;AACzB,IAAM,mBAAmB,CAAC,MAAO,IAAK;AACtC,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,eAAe;AAErB,IAAM,UAAU,CAAC,QAAsB,CAAC,IAAI,EAAE,GAA8B,QAAQ,OAAO,KAAK,OAAO,KAAK,KAAK,OAAO;AACxH,IAAM,gBAAgB;AACtB,IAAM,UAAU;AAGhB,SAAS,aAAsB;AAC7B,QAAM,CAAC,KAAK,MAAM,IAAU,eAAS,KAAK;AAC1C,EAAM,gBAAU,MAAM;AACpB,UAAM,QAAQ,OAAO,WAAW,kCAAkC;AAClE,UAAM,SAAS,MAAM,OAAO,CAAC,MAAM,WAAW,SAAS,oBAAoB,QAAQ;AACnF,WAAO;AACP,UAAM,iBAAiB,UAAU,MAAM;AACvC,aAAS,iBAAiB,oBAAoB,MAAM;AACpD,WAAO,MAAM;AACX,YAAM,oBAAoB,UAAU,MAAM;AAC1C,eAAS,oBAAoB,oBAAoB,MAAM;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAGA,SAAS,YAAY,KAA+C;AAClE,QAAM,CAAC,IAAI,KAAK,IAAU,eAAS,KAAK;AACxC,EAAM,gBAAU,MAAM;AACpB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,MAAM,OAAO,yBAAyB,YAAa;AACxD,UAAM,WAAW,IAAI,qBAAqB,CAAC,CAAC,KAAK,MAAM,MAAM,QAAQ,OAAO,cAAc,CAAC,GAAG,EAAE,WAAW,IAAI,CAAC;AAChH,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,GAAG,CAAC;AACR,SAAO;AACT;AAQO,SAAS,MAAM,EAAE,OAAO,QAAQ,KAAK,GAAG,OAAO,SAAS,SAAS,MAAM,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAK,QAAQ,QAAQ,GAAG,SAAS,MAAM,QAAQ,OAAO,WAAW,OAAO,GAAG,MAAM,GAAe;AACnN,QAAM,QAAc,aAAuB,IAAI;AAC/C,QAAM,SAAe,aAAkC,CAAC,CAAC;AACzD,QAAM,MAAM,WAAW;AACvB,QAAM,WAAW,YAAY,KAAK;AAClC,QAAM,UAAU,OAAO;AACvB,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAqC,CAAC,CAAC;AACvE,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAiD,IAAI;AACvF,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAkC,CAAC,CAAC;AAGpE,QAAM,QAAc,cAAQ,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,EAAG,IAAI,MAAM,CAAC,EAAG,CAAC,GAAG,CAAC,KAAK,CAAC;AAC3G,QAAM,OAAO,CAAC,OAAe,OAAmB,SAAS,CAAC,MAAO,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAE;AAC7G,QAAM,OAAO,IAAI,YAAsB,SAAS,CAAC,MAAM,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,SAAS,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAG1I,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,UAAU,MAAM,SAAS,EAAG;AAC7C,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,MAAM;AACjB,cAAQ,WAAW,MAAM;AACvB,cAAM,KAAK,KAAK,MAAM,OAAO,KAAK,MAAM,SAAS,EAAE;AACnD,cAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAI,MAAM,KAAK,CAAC,CAAE;AAC1C,aAAK,GAAG,OAAO;AACf,aAAK,GAAG,MAAM;AACd,eAAO,WAAW,MAAM;AACtB,eAAK,GAAG,CAAC;AACT,eAAK;AAAA,QACP,GAAG,QAAQ,QAAQ,cAAc,CAAC;AAAA,MACpC,GAAG,QAAQ,QAAQ,iBAAiB,KAAK,CAAC;AAAA,IAC5C;AACA,SAAK;AACL,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,OAAO,QAAQ,KAAK,CAAC;AAG1C,EAAM,gBAAU,MAAM;AACpB,UAAM,SAAS,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,QAAQ;AACzD,QAAI,CAAC,WAAW,CAAC,WAAW,SAAS,KAAK,MAAM,SAAS,EAAG;AAC5D,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,CAAC,OAAe,IAAI,QAAc,CAAC,YAAa,QAAQ,WAAW,SAAS,EAAE,CAAE;AAC7F,UAAM,OAAO,YAAY;AACvB,YAAM,KAAK,OAAO,QAAQ,MAAM;AAChC,YAAM,MAAM,MAAM,SAAS,sBAAsB;AACjD,UAAI,CAAC,MAAM,CAAC,OAAO,OAAO,GAAG,YAAY,WAAY;AAErD,YAAM,OAAO,MAAM,MAAM;AACzB,YAAM,SAAS,MAAM,OAAO,CAAC,KAAK,GAAG,MAAO,MAAM,UAAU,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,MAAM,GAAG,EAAG,IAAI,KAAK,CAAC,IAAI,IAAI,KAAM,WAAW,IAAI,IAAI,CAAC;AACtJ,YAAM,KAAK,MAAM,MAAM;AACvB,YAAM,OAAO,GAAG,IAAI,KAAK,IAAI,IAAI;AAEjC,YAAM,MAAO,GAAG,IAAI,KAAK,KAAK,MAAO,IAAI,QAAQ,QAAQ,GAAG,OAAO,OAAO,IAAI,QAAQ;AACtF,YAAM,OAAQ,KAAK,OAAO,MAAO,IAAI,QAAQ;AAC7C,YAAM,MAAM,OAAO,OAAe,QAAgB;AAChD,kBAAU,EAAE,OAAO,QAAQ,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC;AACzD,oBAAY,GAAG;AAAA,UACb;AAAA,YACE,EAAE,WAAW,aAAa,KAAK,UAAU,QAAQ,WAAW;AAAA,YAC5D,EAAE,WAAW,cAAc,QAAQ,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,QAAQ,WAAW,QAAQ,IAAI;AAAA,YAC7F,EAAE,WAAW,aAAa,GAAG,SAAS;AAAA,UACxC;AAAA,UACA,EAAE,UAAU,WAAW,MAAM,WAAW;AAAA,QAC1C;AACA,cAAM,UAAU,SAAS,MAAM,MAAM,MAAS;AAC9C,kBAAU,IAAI;AAAA,MAChB;AACA,WAAK,QAAQ,OAAO,IAAI,SAAS,OAAO;AACxC,YAAM,IAAI,GAAG,EAAE;AACf,UAAI,CAAC,MAAO;AAEZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE;AAC1C,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,MAAO;AACZ,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE;AAC3C,WAAK,QAAQ,IAAI;AACjB,YAAM,IAAI,IAAI,CAAC;AACf,iBAAW,OAAO;AAClB,WAAK,MAAM;AAAA,IACb;AACA,UAAM,WAAW,CAAC,OAAe;AAC/B,cAAQ,WAAW,YAAY;AAC7B,cAAM,KAAK;AACX,YAAI,MAAO,UAAS,QAAQ,QAAQ,kBAAkB,KAAK,CAAC;AAAA,MAC9D,GAAG,EAAE;AAAA,IACP;AACA,aAAS,mBAAmB,KAAK;AACjC,WAAO,MAAM;AACX,cAAQ;AACR,mBAAa,KAAK;AAClB,iBAAW,OAAO;AAClB,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,SAAS,SAAS,OAAO,QAAQ,KAAK,CAAC;AAG3C,QAAM,UAAgB,aAAO,KAAK;AAClC,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,SAAS,QAAQ,QAAS;AAC3C,YAAQ,UAAU;AAClB,UAAM,SAA0C,CAAC;AACjD,UAAM,QAAQ,CAAC,OAAO,MAAM;AAC1B,aAAO,KAAK,WAAW,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE,GAAG,IAAI,aAAa,CAAC;AAC3F,aAAO,KAAK,WAAW,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE,GAAG,IAAI,gBAAgB,OAAO,CAAC;AAAA,IACxG,CAAC;AACD,WAAO,MAAM,OAAO,QAAQ,YAAY;AAAA,EAC1C,GAAG,CAAC,SAAS,OAAO,KAAK,CAAC;AAG1B,EAAM,gBAAU,MAAM;AACpB,UAAM,QAAQ,SAAS,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI;AACnE,QAAI,CAAC,WAAW,QAAQ,EAAG;AAC3B,UAAM,QAAQ,WAAW,MAAM;AAC7B,WAAK,OAAO,IAAI;AAChB,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE;AAAA,IAC3C,GAAG,CAAC;AACJ,UAAM,IAAI,WAAW,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE,GAAG,OAAO;AAC/E,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,mBAAa,CAAC;AACd,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE;AAC1C,WAAK,KAAK;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,KAAK,CAAC;AAG3B,QAAM,QAAQ,CAAC,OAAe,UAA8B;AAC1D,QAAI,CAAC,WAAW,CAAC,SAAS,MAAM,gBAAgB,QAAS;AACzD,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,QAAM,QAAQ,CAAC,UAAkB;AAC/B,QAAI,CAAC,MAAO;AACZ,SAAK,KAAK;AAAA,EACZ;AACA,QAAM,MAAM,CAAC,UAAkB;AAC7B,QAAI,CAAC,WAAW,CAAC,MAAO;AACxB,SAAK,OAAO,IAAI;AAChB,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE;AACzC,eAAW,MAAM;AACf,eAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,MAAM,EAAE;AAC1C,WAAK,KAAK;AAAA,IACZ,GAAG,YAAY;AAAA,EACjB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,eAAW;AAAA,MACX,cAAW;AAAA,MACX,gBAAc,UAAU,KAAK;AAAA,MAC7B,WAAW,GAAG,mBAAmB,SAAS;AAAA,MAC1C,OAAO,EAAE,aAAa,OAAO,KAAK,GAAG,eAAe,eAAe,GAAG,MAAM;AAAA,MAC3E,GAAG;AAAA,MAEH,gBAAM,IAAI,CAAC,GAAG,MACb;AAAA,QAAC;AAAA;AAAA,UAEC,KAAK,CAAC,OAAO;AACX,mBAAO,QAAQ,CAAC,IAAI;AAAA,UACtB;AAAA,UACA,mBAAiB,EAAE;AAAA,UACnB,WAAU;AAAA,UACV,OAAO,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,EAAE,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,aAAa,KAAK,eAAe,QAAQ,SAAS,OAAO;AAAA,UAChK,gBAAgB,CAAC,UAAU,MAAM,GAAG,KAAK;AAAA,UACzC,gBAAgB,MAAM,MAAM,CAAC;AAAA,UAC7B,eAAe,MAAM,IAAI,CAAC;AAAA,UAE1B;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,EAAE;AAAA,cACR;AAAA,cACA,MAAI;AAAA,cACJ,MAAM,MAAM,CAAC;AAAA,cACb,QAAQ,QAAQ,UAAU;AAAA,cAC1B,MAAM,QAAQ,UAAU,IAAI,OAAO,OAAO;AAAA,cAC1C,OAAO,MAAM,CAAC;AAAA,cACd,MAAM;AAAA,cACN,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,SAAS,SAAS,UAAU,UAAU;AAAA;AAAA,UAChF;AAAA;AAAA,QArBK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAsBrB,CACD;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
package/dist/nav-bar.js
CHANGED
package/dist/site-footer.js
CHANGED
package/dist/sites.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
var site = (s) => ({ ...s, host: s.url.replace(/^https?:\/\//, "").replace(/\/$/, "") });
|
|
3
3
|
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
|
-
site({ id: "orazaka", name: "Orazaka", url: "https://
|
|
6
|
-
site({ id: "orochia", name: "Orochia", url: "https://
|
|
7
|
-
site({ id: "yasakia", name: "Yasakia", url: "https://
|
|
8
|
-
site({ id: "yororis", name: "Yororis", url: "https://
|
|
9
|
-
site({ id: "yomizaka", name: "Yomizaka", url: "https://
|
|
10
|
-
site({ id: "kachizaka", name: "Kachizaka", url: "https://
|
|
11
|
-
site({ id: "akinaza", name: "Akinaza", url: "https://
|
|
5
|
+
site({ id: "orazaka", name: "Orazaka", url: "https://www.orazaka.com", kind: "product", group: "products", status: "released", mark: "orazaka", repo: "https://github.com/krizaka/orazaka" }),
|
|
6
|
+
site({ id: "orochia", name: "Orochia", url: "https://www.orochia.com", kind: "product", group: "products", status: "released", mark: "orochia", repo: "https://github.com/krizaka/orochia" }),
|
|
7
|
+
site({ id: "yasakia", name: "Yasakia", url: "https://www.yasakia.com", kind: "product", group: "products", status: "development", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
|
|
8
|
+
site({ id: "yororis", name: "Yororis", url: "https://www.yororis.com", kind: "product", group: "products", status: "construction", mark: "yororis", repo: "https://github.com/krizaka/yororis" }),
|
|
9
|
+
site({ id: "yomizaka", name: "Yomizaka", url: "https://www.yomizaka.com", kind: "product", group: "products", status: "development", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
|
|
10
|
+
site({ id: "kachizaka", name: "Kachizaka", url: "https://www.kachizaka.com", kind: "product", group: "products", status: "development", mark: "kachizaka", repo: "https://github.com/krizaka/kachizaka" }),
|
|
11
|
+
site({ id: "akinaza", name: "Akinaza", url: "https://www.akinaza.com", kind: "product", group: "products", status: "development", mark: "akinaza", repo: "https://github.com/krizaka/akinaza" }),
|
|
12
12
|
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" }),
|
|
13
13
|
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" }),
|
|
14
14
|
// The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.
|
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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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 site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://dev.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://dev.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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://www.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://www.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://www.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://www.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://www.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://www.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://www.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\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.
|
|
3
|
+
"version": "3.41.1",
|
|
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",
|
|
@@ -745,7 +745,7 @@
|
|
|
745
745
|
],
|
|
746
746
|
"peerDependencies": {
|
|
747
747
|
"@krizaka/query": ">=0.2.1",
|
|
748
|
-
"@krizaka/tailwind": ">=3.
|
|
748
|
+
"@krizaka/tailwind": ">=3.41.1",
|
|
749
749
|
"@maplibre/maplibre-react-native": ">=11",
|
|
750
750
|
"@shopify/flash-list": ">=2",
|
|
751
751
|
"expo-camera": ">=16",
|
|
@@ -805,8 +805,8 @@
|
|
|
805
805
|
"typescript": "^5.9.0",
|
|
806
806
|
"vitest": "^4.1.11",
|
|
807
807
|
"zod": "^4.6.5",
|
|
808
|
-
"@krizaka/
|
|
809
|
-
"@krizaka/
|
|
808
|
+
"@krizaka/config": "0.4.1",
|
|
809
|
+
"@krizaka/query": "0.2.1"
|
|
810
810
|
},
|
|
811
811
|
"engines": {
|
|
812
812
|
"node": ">=22"
|
|
@@ -2550,7 +2550,7 @@
|
|
|
2550
2550
|
"tailwind-variants": "^3.3.1",
|
|
2551
2551
|
"@krizaka/intl": "^0.2.1",
|
|
2552
2552
|
"@krizaka/pacer": "^0.1.0",
|
|
2553
|
-
"@krizaka/tokens": "^3.
|
|
2553
|
+
"@krizaka/tokens": "^3.41.1"
|
|
2554
2554
|
},
|
|
2555
2555
|
"scripts": {
|
|
2556
2556
|
"build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/examples',{recursive:true}))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/examples/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css');require('node:fs').copyFileSync('src/blocks.css','dist/blocks.css');require('node:fs').copyFileSync('src/story.css','dist/story.css')\" && node scripts/build-registry.mjs",
|
package/registry/action-bar.json
CHANGED
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"description": "Discuss, propose, report: icon buttons with their words in a tooltip.",
|
|
128
128
|
"path": "examples/action-bar/icons.tsx",
|
|
129
129
|
"module": "@krizaka/ui/registry/examples/action-bar/icons",
|
|
130
|
-
"code": "import { ActionBar } from \"@krizaka/ui/action-bar\";\n\nconst glyph = (d: string) => (\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst repo = \"https://github.com/krizaka/krizaka-build\";\n\n// `icons`: where room is short (the foot of a card) — the words are the accessible name and a tooltip on hover and focus.\nexport default function ActionBarIcons() {\n return (\n <ActionBar\n variant=\"icons\"\n label=\"Talk about Krizaka Java\"\n actions={[\n { href:
|
|
130
|
+
"code": "import { ActionBar } from \"@krizaka/ui/action-bar\";\n\nconst glyph = (d: string) => (\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst repo = \"https://github.com/krizaka/krizaka-build\";\n\n// `icons`: where room is short (the foot of a card) — the words are the accessible name and a tooltip on hover and focus.\nexport default function ActionBarIcons() {\n return (\n <ActionBar\n variant=\"icons\"\n label=\"Talk about Krizaka Java\"\n actions={[\n { href: `https://github.com/krizaka/krizaka-ui/discussions`, label: \"Discuss\", icon: glyph(\"M4 5h16v11H9l-5 4V5Z\") },\n { href: `${repo}/issues/new?template=idea.yml`, label: \"Propose\", icon: glyph(\"M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z\") },\n { href: `${repo}/issues/new?template=bug.yml`, label: \"Report a problem\", icon: glyph(\"M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1\") },\n ]}\n />\n );\n}\n"
|
|
131
131
|
}
|
|
132
132
|
]
|
|
133
133
|
},
|
|
@@ -12,7 +12,7 @@ function ActionBarIcons() {
|
|
|
12
12
|
variant: "icons",
|
|
13
13
|
label: "Talk about Krizaka Java",
|
|
14
14
|
actions: [
|
|
15
|
-
{ href:
|
|
15
|
+
{ href: `https://github.com/krizaka/krizaka-ui/discussions`, label: "Discuss", icon: glyph("M4 5h16v11H9l-5 4V5Z") },
|
|
16
16
|
{ href: `${repo}/issues/new?template=idea.yml`, label: "Propose", icon: glyph("M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z") },
|
|
17
17
|
{ href: `${repo}/issues/new?template=bug.yml`, label: "Report a problem", icon: glyph("M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1") }
|
|
18
18
|
]
|
|
@@ -14,7 +14,7 @@ export default function ActionBarIcons() {
|
|
|
14
14
|
variant="icons"
|
|
15
15
|
label="Talk about Krizaka Java"
|
|
16
16
|
actions={[
|
|
17
|
-
{ href:
|
|
17
|
+
{ href: `https://github.com/krizaka/krizaka-ui/discussions`, label: "Discuss", icon: glyph("M4 5h16v11H9l-5 4V5Z") },
|
|
18
18
|
{ href: `${repo}/issues/new?template=idea.yml`, label: "Propose", icon: glyph("M12 3v4M5 12H3m18 0h-2M8 17h8m-6 4h4M12 7a5 5 0 0 0-3 9h6a5 5 0 0 0-3-9Z") },
|
|
19
19
|
{ href: `${repo}/issues/new?template=bug.yml`, label: "Report a problem", icon: glyph("M12 4 2.5 20h19L12 4Zm0 6v4m0 3v.1") },
|
|
20
20
|
]}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Progress } from "@krizaka/ui/progress";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
function ProgressBarSizes() {
|
|
7
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
7
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-64 max-w-full flex-col gap-4", children: [
|
|
8
8
|
/* @__PURE__ */ jsx(Progress, { label: "Small", size: "sm", value: 30 }),
|
|
9
9
|
/* @__PURE__ */ jsx(Progress, { label: "Medium", size: "md", value: 55 }),
|
|
10
10
|
/* @__PURE__ */ jsx(Progress, { label: "Large", size: "lg", value: 80 })
|
|
@@ -2,7 +2,7 @@ import { Progress } from "@krizaka/ui/progress";
|
|
|
2
2
|
|
|
3
3
|
export default function ProgressBarSizes() {
|
|
4
4
|
return (
|
|
5
|
-
<div className="flex flex-col gap-4">
|
|
5
|
+
<div className="flex w-64 max-w-full flex-col gap-4">
|
|
6
6
|
<Progress label="Small" size="sm" value={30} />
|
|
7
7
|
<Progress label="Medium" size="md" value={55} />
|
|
8
8
|
<Progress label="Large" size="lg" value={80} />
|
|
@@ -16,7 +16,7 @@ function SpecimenTagProduct() {
|
|
|
16
16
|
{ label: "on Orazaka", value: "document checks" }
|
|
17
17
|
],
|
|
18
18
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19
|
-
/* @__PURE__ */ jsx("a", { href: "https://
|
|
19
|
+
/* @__PURE__ */ jsx("a", { href: "https://www.yomizaka.com", children: "www.yomizaka.com \u2197" }),
|
|
20
20
|
/* @__PURE__ */ jsx("a", { href: "https://github.com/krizaka/yomizaka", children: "source" })
|
|
21
21
|
] }),
|
|
22
22
|
children: /* @__PURE__ */ jsx("p", { children: "Leases, contracts and invoices, read and checked. Every finding quotes its passage." })
|
|
@@ -14,7 +14,7 @@ export default function SpecimenTagProduct() {
|
|
|
14
14
|
]}
|
|
15
15
|
footer={
|
|
16
16
|
<>
|
|
17
|
-
<a href="https://
|
|
17
|
+
<a href="https://www.yomizaka.com">www.yomizaka.com ↗</a>
|
|
18
18
|
<a href="https://github.com/krizaka/yomizaka">source</a>
|
|
19
19
|
</>
|
|
20
20
|
}
|
package/registry/flock.json
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"One flock per page, under the words and never behind them: the birds are the scene, not the message.",
|
|
44
44
|
"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.",
|
|
45
45
|
"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.",
|
|
46
|
+
"Make it the page's own: `active` is the bird of the product the reader is on (it looks up and jumps), `greet` has every bird jump once as the flock comes into view, `tempo` makes the moments more frequent.",
|
|
46
47
|
"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves."
|
|
47
48
|
],
|
|
48
49
|
"accessibility": {
|
|
@@ -74,6 +75,13 @@
|
|
|
74
75
|
"description": "The birds on the stage.",
|
|
75
76
|
"required": true
|
|
76
77
|
},
|
|
78
|
+
{
|
|
79
|
+
"name": "active",
|
|
80
|
+
"type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\" | null",
|
|
81
|
+
"default": "null",
|
|
82
|
+
"description": "A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes.",
|
|
83
|
+
"required": false
|
|
84
|
+
},
|
|
77
85
|
{
|
|
78
86
|
"name": "courier",
|
|
79
87
|
"type": "boolean",
|
|
@@ -88,6 +96,13 @@
|
|
|
88
96
|
"description": "Two neighbours look at each other now and then. Default `true`.",
|
|
89
97
|
"required": false
|
|
90
98
|
},
|
|
99
|
+
{
|
|
100
|
+
"name": "greet",
|
|
101
|
+
"type": "boolean",
|
|
102
|
+
"default": "false",
|
|
103
|
+
"description": "The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right.",
|
|
104
|
+
"required": false
|
|
105
|
+
},
|
|
91
106
|
{
|
|
92
107
|
"name": "random",
|
|
93
108
|
"type": "(() => number)",
|
|
@@ -109,6 +124,13 @@
|
|
|
109
124
|
"description": "A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`.",
|
|
110
125
|
"required": false
|
|
111
126
|
},
|
|
127
|
+
{
|
|
128
|
+
"name": "tempo",
|
|
129
|
+
"type": "number",
|
|
130
|
+
"default": "1",
|
|
131
|
+
"description": "How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers.",
|
|
132
|
+
"required": false
|
|
133
|
+
},
|
|
112
134
|
{
|
|
113
135
|
"name": "tone",
|
|
114
136
|
"type": "\"brand\" | \"own\"",
|
|
@@ -142,7 +164,7 @@
|
|
|
142
164
|
"files": [
|
|
143
165
|
{
|
|
144
166
|
"path": "flock/flock.tsx",
|
|
145
|
-
"content": "\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number]) => lo + random() * (hi - lo);\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS));\n }, ms);\n };\n schedule(COURIER_FIRST_MS);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"
|
|
167
|
+
"content": "\"use client\";\n\n// The flock, alive: birds standing on a stage, each at ease in character (Mascot `life`), and the moments between\n// them — two birds glance at each other, the pigeon carries its parcel across, a bird looks up when you come near or tap\n// it. Transform and opacity only: CSS for each bird's own life, a timer for the moments (no frame loop), and nothing at\n// all when the flock is off screen, the tab is hidden or the person asked for less motion.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport type { MascotBird, MascotLook } from \"../mascots\";\nimport { Mascot } from \"../mascots\";\n\nexport interface FlockBird {\n /** Which bird. */\n bird: MascotBird;\n /** Where its feet are, across the stage: 0 to 100 (% of the stage's width). */\n x: number;\n /** Where its feet are, down the stage: 0 to 100 (% of the stage's height). */\n y: number;\n /** How wide it is: % of the stage's width. */\n size: number;\n}\n\nexport interface FlockMoments {\n /** Two neighbours look at each other now and then. Default `true`. */\n glance?: boolean;\n /** The pigeon, when the flock has one, carries its parcel across to another bird and comes back. Default `true`. */\n courier?: boolean;\n /** A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`. */\n react?: boolean;\n}\n\nexport type FlockProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n FlockMoments & {\n /** The birds on the stage. */\n birds: readonly FlockBird[];\n /** The stage's shape, width over height (default 16 / 9). It keeps its ratio at any width, so the birds keep their places. */\n ratio?: number;\n /** The birds' colours: the host's accent (`brand`, default) or the Krizaka story's (`own`). */\n tone?: \"brand\" | \"own\";\n /** The source of chance (default `Math.random`): give it a fixed one in a test. */\n random?: () => number;\n /** How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers. */\n tempo?: number;\n /** A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes. */\n active?: MascotBird | null;\n /** The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right. */\n greet?: boolean;\n };\n\n// @fixed: the rhythm of the moments — how long a glance lasts, how often one happens, how long a delivery takes. A\n// choreography's pace, not a business parameter.\nconst GLANCE_HOLD_MS = [1500, 2400] as const;\nconst GLANCE_EVERY_MS = [4200, 9000] as const;\nconst COURIER_FIRST_MS = 9000;\nconst COURIER_EVERY_MS = [26000, 38000] as const;\nconst FLIGHT_MS = 2600;\nconst HAND_OVER_MS = 1300;\nconst LOOK_BACK_MS = 1600;\n\nconst between = (random: () => number, [lo, hi]: readonly [number, number], tempo = 1) => (lo + random() * (hi - lo)) / tempo;\nconst GREET_STEP_MS = 110;\nconst POKE_MS = 700;\n\n/** `true` while the page may move: no reduced-motion request, the tab visible. */\nfunction useMayMove(): boolean {\n const [may, setMay] = React.useState(false);\n React.useEffect(() => {\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setMay(!query.matches && document.visibilityState !== \"hidden\");\n update();\n query.addEventListener(\"change\", update);\n document.addEventListener(\"visibilitychange\", update);\n return () => {\n query.removeEventListener(\"change\", update);\n document.removeEventListener(\"visibilitychange\", update);\n };\n }, []);\n return may;\n}\n\n/** `true` while the element is on screen. */\nfunction useOnScreen(ref: React.RefObject<Element | null>): boolean {\n const [on, setOn] = React.useState(false);\n React.useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => setOn(Boolean(entry?.isIntersecting)), { threshold: 0.1 });\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref]);\n return on;\n}\n\n/**\n * The flock on its stage, alive. Decorative (`aria-hidden`): put the words beside it. Each bird idles in character; every\n * few seconds two neighbours glance at each other; every half minute the pigeon carries its parcel to another bird and\n * returns; a bird looks up when you come near. All of it pauses off screen and in a hidden tab, and the flock is still\n * under `prefers-reduced-motion`.\n */\nexport function Flock({ birds, ratio = 16 / 9, tone = \"brand\", glance = true, courier = true, react = true, random = Math.random, tempo = 1, active = null, greet = false, className, style, ...props }: FlockProps) {\n const stage = React.useRef<HTMLDivElement>(null);\n const bodies = React.useRef<(HTMLDivElement | null)[]>([]);\n const may = useMayMove();\n const onScreen = useOnScreen(stage);\n const running = may && onScreen;\n const [looks, setLooks] = React.useState<Record<number, MascotLook>>({});\n const [flying, setFlying] = React.useState<{ index: number; bank: number } | null>(null);\n const [poked, setPoked] = React.useState<Record<number, boolean>>({});\n\n // Left to right: a bird's neighbours are the birds beside it on the stage.\n const order = React.useMemo(() => birds.map((_, i) => i).sort((a, b) => birds[a]!.x - birds[b]!.x), [birds]);\n const look = (index: number, to: MascotLook) => setLooks((l) => (l[index] === to ? l : { ...l, [index]: to }));\n const rest = (...indexes: number[]) => setLooks((l) => Object.fromEntries(Object.entries(l).filter(([k]) => !indexes.includes(Number(k)))));\n\n // Glances: two neighbours turn towards each other, hold, and look away.\n React.useEffect(() => {\n if (!running || !glance || order.length < 2) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let back: ReturnType<typeof setTimeout> | undefined;\n const next = () => {\n timer = setTimeout(() => {\n const at = Math.floor(random() * (order.length - 1));\n const [a, b] = [order[at]!, order[at + 1]!];\n look(a, \"right\");\n look(b, \"left\");\n back = setTimeout(() => {\n rest(a, b);\n next();\n }, between(random, GLANCE_HOLD_MS));\n }, between(random, GLANCE_EVERY_MS, tempo));\n };\n next();\n return () => {\n clearTimeout(timer);\n clearTimeout(back);\n };\n }, [running, glance, order, random, tempo]);\n\n // The delivery: the pigeon takes its parcel across to the farthest bird, hands it over, and flies back.\n React.useEffect(() => {\n const pigeon = birds.findIndex((b) => b.bird === \"pigeon\");\n if (!running || !courier || pigeon < 0 || birds.length < 2) return;\n let alive = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let animation: Animation | undefined;\n const wait = (ms: number) => new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));\n const trip = async () => {\n const el = bodies.current[pigeon];\n const box = stage.current?.getBoundingClientRect();\n if (!el || !box || typeof el.animate !== \"function\") return;\n // The farthest bird from the pigeon, so the parcel crosses the whole stage.\n const from = birds[pigeon]!;\n const target = birds.reduce((far, b, i) => (i !== pigeon && Math.abs(b.x - from.x) > Math.abs(birds[far]!.x - from.x) ? i : far), pigeon === 0 ? 1 : 0);\n const to = birds[target]!;\n const side = to.x > from.x ? 1 : -1;\n // It lands beside the receiver, not on it.\n const dx = ((to.x - from.x) / 100) * box.width - side * (to.size / 100) * box.width * 0.55;\n const lift = (from.size / 100) * box.width * 0.35;\n const fly = async (fromX: number, toX: number) => {\n setFlying({ index: pigeon, bank: Math.sign(toX - fromX) });\n animation = el.animate(\n [\n { transform: `translate(${fromX}px, 0)`, easing: \"ease-out\" },\n { transform: `translate(${(fromX + toX) / 2}px, ${-lift}px)`, easing: \"ease-in\", offset: 0.5 },\n { transform: `translate(${toX}px, 0)` },\n ],\n { duration: FLIGHT_MS, fill: \"forwards\" },\n );\n await animation.finished.catch(() => undefined);\n setFlying(null);\n };\n look(target, side > 0 ? \"left\" : \"right\");\n await fly(0, dx);\n if (!alive) return;\n // The hand-over: the receiver looks at the pigeon, the pigeon hops on the doorstep.\n setPoked((p) => ({ ...p, [pigeon]: true }));\n await wait(HAND_OVER_MS);\n if (!alive) return;\n setPoked((p) => ({ ...p, [pigeon]: false }));\n look(target, \"up\");\n await fly(dx, 0);\n animation?.cancel();\n rest(target);\n };\n const schedule = (ms: number) => {\n timer = setTimeout(async () => {\n await trip();\n if (alive) schedule(between(random, COURIER_EVERY_MS, tempo));\n }, ms);\n };\n schedule(COURIER_FIRST_MS / tempo);\n return () => {\n alive = false;\n clearTimeout(timer);\n animation?.cancel();\n setFlying(null);\n };\n }, [running, courier, birds, random, tempo]);\n\n // The greeting: the first time the flock is on screen, each bird jumps in turn.\n const greeted = React.useRef(false);\n React.useEffect(() => {\n if (!running || !greet || greeted.current) return;\n greeted.current = true;\n const timers: ReturnType<typeof setTimeout>[] = [];\n order.forEach((index, n) => {\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: true })), n * GREET_STEP_MS));\n timers.push(setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), n * GREET_STEP_MS + POKE_MS));\n });\n return () => timers.forEach(clearTimeout);\n }, [running, greet, order]);\n\n // The bird the page points at looks up and jumps.\n React.useEffect(() => {\n const index = active ? birds.findIndex((b) => b.bird === active) : -1;\n if (!running || index < 0) return;\n const start = setTimeout(() => {\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n }, 0);\n const t = setTimeout(() => setPoked((p) => ({ ...p, [index]: false })), POKE_MS);\n return () => {\n clearTimeout(start);\n clearTimeout(t);\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n };\n }, [active, running, birds]);\n\n // A bird looks up when you come near, and jumps when you tap it.\n const hover = (index: number, event: React.PointerEvent) => {\n if (!running || !react || event.pointerType !== \"mouse\") return;\n look(index, \"up\");\n };\n const leave = (index: number) => {\n if (!react) return;\n rest(index);\n };\n const tap = (index: number) => {\n if (!running || !react) return;\n look(index, \"up\");\n setPoked((p) => ({ ...p, [index]: true }));\n setTimeout(() => {\n setPoked((p) => ({ ...p, [index]: false }));\n rest(index);\n }, LOOK_BACK_MS);\n };\n\n return (\n <div\n ref={stage}\n aria-hidden\n data-flock=\"\"\n data-running={running ? \"\" : undefined}\n className={cn(\"relative w-full\", className)}\n style={{ aspectRatio: String(ratio), containerType: \"inline-size\", ...style }}\n {...props}\n >\n {birds.map((b, i) => (\n <div\n key={`${b.bird}-${i}`}\n ref={(el) => {\n bodies.current[i] = el;\n }}\n data-flock-bird={b.bird}\n className=\"absolute touch-manipulation\"\n style={{ left: `${b.x - b.size / 2}%`, width: `${b.size}%`, top: `calc(${b.y}% - ${b.size * 0.9}cqw)`, aspectRatio: \"1\", pointerEvents: react ? \"auto\" : \"none\" }}\n onPointerEnter={(event) => hover(i, event)}\n onPointerLeave={() => leave(i)}\n onPointerDown={() => tap(i)}\n >\n <Mascot\n bird={b.bird}\n tone={tone}\n life\n look={looks[i]}\n flying={flying?.index === i}\n bank={flying?.index === i ? flying.bank : 0}\n poked={poked[i]}\n size={120}\n style={{ width: \"100%\", height: \"100%\", display: \"block\", overflow: \"visible\" }}\n />\n </div>\n ))}\n </div>\n );\n}\n"
|
|
146
168
|
},
|
|
147
169
|
{
|
|
148
170
|
"path": "flock/index.ts",
|
|
@@ -150,7 +172,7 @@
|
|
|
150
172
|
},
|
|
151
173
|
{
|
|
152
174
|
"path": "flock/meta.ts",
|
|
153
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Flock\",\n summary:\n \"The Krizaka birds standing on a stage, alive: each at ease in character, two neighbours glancing at each other, the pigeon carrying its parcel across, a bird looking up when you come near or tap it.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: {\n native: { status: \"n/a\", reason: \"A decorative stage for a public page's first screen; an app screen speaks with MascotState.\", use: \"mascot-state\" },\n desktop: \"web\",\n },\n whenToUse: [\n \"On a company's home, where the flock is the scene: five birds on a ridge, under the words.\",\n \"On an about or story page, where the birds are the subject and a still drawing would feel dead.\",\n ],\n whenNotToUse: [\n { when: \"A bird that answers a state (a wait, an error, an empty list).\", use: \"mascot-state\" },\n { when: \"One bird at ease inside a card or beside a rule.\", use: \"mascots\" },\n { when: \"The flock as a still plate with its promises.\", use: \"bird-plate\" },\n ],\n bestPractices: [\n \"One flock per page, under the words and never behind them: the birds are the scene, not the message.\",\n \"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.\",\n \"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.\",\n \"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative (`aria-hidden`): it never carries information; put the words beside it.\",\n \"Under `prefers-reduced-motion` the birds are still: no idle motion, no glance, no delivery, no reaction.\",\n \"The moments pause while the flock is off screen and while the tab is hidden; there is no frame loop.\",\n ],\n },\n related: [\"mascots\", \"mascot-state\", \"bird-plate\", \"landscape\"],\n web: {\n imports: [\"Flock\"],\n examples: [\n { name: \"ridge\", title: \"The flock on its ridge\", description: \"Five birds across a stage, each at ease; neighbours glance, the pigeon delivers.\" },\n { name: \"pair\", title: \"Two birds\", description: \"The owl and the duck: they look at each other now and then.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
175
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Flock\",\n summary:\n \"The Krizaka birds standing on a stage, alive: each at ease in character, two neighbours glancing at each other, the pigeon carrying its parcel across, a bird looking up when you come near or tap it.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: {\n native: { status: \"n/a\", reason: \"A decorative stage for a public page's first screen; an app screen speaks with MascotState.\", use: \"mascot-state\" },\n desktop: \"web\",\n },\n whenToUse: [\n \"On a company's home, where the flock is the scene: five birds on a ridge, under the words.\",\n \"On an about or story page, where the birds are the subject and a still drawing would feel dead.\",\n ],\n whenNotToUse: [\n { when: \"A bird that answers a state (a wait, an error, an empty list).\", use: \"mascot-state\" },\n { when: \"One bird at ease inside a card or beside a rule.\", use: \"mascots\" },\n { when: \"The flock as a still plate with its promises.\", use: \"bird-plate\" },\n ],\n bestPractices: [\n \"One flock per page, under the words and never behind them: the birds are the scene, not the message.\",\n \"Give the stage the ratio of its backdrop (`ratio`) and the birds' places in percent: they keep their positions at any width.\",\n \"Keep each bird to its job: the falcon watches, the owl reads, the duck builds, the flamingo answers, the pigeon delivers.\",\n \"Make it the page's own: `active` is the bird of the product the reader is on (it looks up and jumps), `greet` has every bird jump once as the flock comes into view, `tempo` makes the moments more frequent.\",\n \"Turn a moment off with `glance={false}`, `courier={false}` or `react={false}` on a page that already moves.\",\n ],\n accessibility: {\n keyboard: [],\n notes: [\n \"Decorative (`aria-hidden`): it never carries information; put the words beside it.\",\n \"Under `prefers-reduced-motion` the birds are still: no idle motion, no glance, no delivery, no reaction.\",\n \"The moments pause while the flock is off screen and while the tab is hidden; there is no frame loop.\",\n ],\n },\n related: [\"mascots\", \"mascot-state\", \"bird-plate\", \"landscape\"],\n web: {\n imports: [\"Flock\"],\n examples: [\n { name: \"ridge\", title: \"The flock on its ridge\", description: \"Five birds across a stage, each at ease; neighbours glance, the pigeon delivers.\" },\n { name: \"pair\", title: \"Two birds\", description: \"The owl and the duck: they look at each other now and then.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
154
176
|
},
|
|
155
177
|
{
|
|
156
178
|
"path": "meta.ts",
|
|
@@ -178,6 +200,13 @@
|
|
|
178
200
|
"description": "The birds on the stage.",
|
|
179
201
|
"required": true
|
|
180
202
|
},
|
|
203
|
+
{
|
|
204
|
+
"name": "active",
|
|
205
|
+
"type": "\"falcon\" | \"owl\" | \"duck\" | \"flamingo\" | \"pigeon\" | \"lovebird\" | \"crane\" | \"magpie\" | \"beaver\" | null",
|
|
206
|
+
"default": "null",
|
|
207
|
+
"description": "A bird the page points at (a product the reader is on): it looks up and jumps once each time it changes.",
|
|
208
|
+
"required": false
|
|
209
|
+
},
|
|
181
210
|
{
|
|
182
211
|
"name": "courier",
|
|
183
212
|
"type": "boolean",
|
|
@@ -192,6 +221,13 @@
|
|
|
192
221
|
"description": "Two neighbours look at each other now and then. Default `true`.",
|
|
193
222
|
"required": false
|
|
194
223
|
},
|
|
224
|
+
{
|
|
225
|
+
"name": "greet",
|
|
226
|
+
"type": "boolean",
|
|
227
|
+
"default": "false",
|
|
228
|
+
"description": "The birds greet the reader once as the flock first comes into view: each jumps in turn, left to right.",
|
|
229
|
+
"required": false
|
|
230
|
+
},
|
|
195
231
|
{
|
|
196
232
|
"name": "random",
|
|
197
233
|
"type": "(() => number)",
|
|
@@ -213,6 +249,13 @@
|
|
|
213
249
|
"description": "A bird looks up when the pointer comes near, and jumps a little when tapped. Default `true`.",
|
|
214
250
|
"required": false
|
|
215
251
|
},
|
|
252
|
+
{
|
|
253
|
+
"name": "tempo",
|
|
254
|
+
"type": "number",
|
|
255
|
+
"default": "1",
|
|
256
|
+
"description": "How lively the moments are: 1 is calm (default), 2 doubles how often neighbours glance and the pigeon delivers.",
|
|
257
|
+
"required": false
|
|
258
|
+
},
|
|
216
259
|
{
|
|
217
260
|
"name": "tone",
|
|
218
261
|
"type": "\"brand\" | \"own\"",
|
package/registry/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.41.1",
|
|
4
4
|
"templatePurposes": {
|
|
5
5
|
"convince": {
|
|
6
6
|
"title": "Convince",
|
|
@@ -3142,7 +3142,7 @@
|
|
|
3142
3142
|
"native": 2
|
|
3143
3143
|
},
|
|
3144
3144
|
"dependencies": [
|
|
3145
|
-
"@krizaka/tokens@^3.
|
|
3145
|
+
"@krizaka/tokens@^3.41.1",
|
|
3146
3146
|
"@protomaps/basemaps@5.7.2",
|
|
3147
3147
|
"tailwind-variants@^3.3.1"
|
|
3148
3148
|
],
|
|
@@ -3581,7 +3581,7 @@
|
|
|
3581
3581
|
"native": 0
|
|
3582
3582
|
},
|
|
3583
3583
|
"dependencies": [
|
|
3584
|
-
"@krizaka/tokens@^3.
|
|
3584
|
+
"@krizaka/tokens@^3.41.1",
|
|
3585
3585
|
"radix-ui@^1.7.0",
|
|
3586
3586
|
"tailwind-variants@^3.3.1"
|
|
3587
3587
|
],
|
|
@@ -4244,7 +4244,7 @@
|
|
|
4244
4244
|
"native": 0
|
|
4245
4245
|
},
|
|
4246
4246
|
"dependencies": [
|
|
4247
|
-
"@krizaka/tokens@^3.
|
|
4247
|
+
"@krizaka/tokens@^3.41.1",
|
|
4248
4248
|
"radix-ui@^1.7.0",
|
|
4249
4249
|
"tailwind-variants@^3.3.1"
|
|
4250
4250
|
],
|
|
@@ -4587,7 +4587,7 @@
|
|
|
4587
4587
|
"native": 0
|
|
4588
4588
|
},
|
|
4589
4589
|
"dependencies": [
|
|
4590
|
-
"@krizaka/tokens@^3.
|
|
4590
|
+
"@krizaka/tokens@^3.41.1",
|
|
4591
4591
|
"radix-ui@^1.7.0",
|
|
4592
4592
|
"tailwind-variants@^3.3.1"
|
|
4593
4593
|
],
|
package/registry/map.json
CHANGED
package/registry/nav-bar.json
CHANGED
package/registry/progress.json
CHANGED
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
"description": "`sm`, `md`, `lg`.",
|
|
201
201
|
"path": "examples/progress/bar-sizes.tsx",
|
|
202
202
|
"module": "@krizaka/ui/registry/examples/progress/bar-sizes",
|
|
203
|
-
"code": "import { Progress } from \"@krizaka/ui/progress\";\n\nexport default function ProgressBarSizes() {\n return (\n <div className=\"flex flex-col gap-4\">\n <Progress label=\"Small\" size=\"sm\" value={30} />\n <Progress label=\"Medium\" size=\"md\" value={55} />\n <Progress label=\"Large\" size=\"lg\" value={80} />\n </div>\n );\n}\n"
|
|
203
|
+
"code": "import { Progress } from \"@krizaka/ui/progress\";\n\nexport default function ProgressBarSizes() {\n return (\n <div className=\"flex w-64 max-w-full flex-col gap-4\">\n <Progress label=\"Small\" size=\"sm\" value={30} />\n <Progress label=\"Medium\" size=\"md\" value={55} />\n <Progress label=\"Large\" size=\"lg\" value={80} />\n </div>\n );\n}\n"
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
"name": "bar-indeterminate",
|
package/registry/rail-nav.json
CHANGED
package/registry/sites.json
CHANGED
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "sites/sites.ts",
|
|
96
|
-
"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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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 site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://dev.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://dev.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\n },\n};\n"
|
|
96
|
+
"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\" | \"motion\" | \"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 offered (a QA-only app may run; the public page says so). `development`: being built in the\n * open, with a dev site (said \"In development\"). A site with none yet would use its repository as its address.\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://www.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://www.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://www.yasakia.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://www.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://www.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://www.kachizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://www.akinaza.com\", 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 // The motion lab: built in the open on GitHub Pages; what proves itself there moves into Krizaka UI.\n site({ id: \"motion\", name: \"Krizaka Motion\", url: \"https://krizaka.github.io/krizaka-motion\", kind: \"open-source\", group: \"open-source\", status: \"development\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-motion\" }),\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 company's own pages, the footer's « Krizaka » column (the brand's home is the signature line's link). */\nexport type CompanyLinkId = \"story\" | \"blog\" | \"contact\";\nexport const companyLinks: readonly { id: CompanyLinkId; url: Record<SiteLocale, string> }[] = [\n { id: \"story\", url: { en: \"https://www.krizaka.com/story\", fr: \"https://www.krizaka.com/fr/story\" } },\n { id: \"blog\", url: { en: \"https://blog.krizaka.com\", fr: \"https://blog.krizaka.com\" } },\n { id: \"contact\", url: { en: \"https://www.krizaka.com/contact\", fr: \"https://www.krizaka.com/fr/contact\" } },\n];\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 /** The language of these words: the company links open in it. @default \"en\" */\n lang?: SiteLocale;\n /** The company's pages in the footer's « Krizaka » column. @default the English words */\n company?: Record<CompanyLinkId, 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: chat, documents, images and video — and the engine under the other products.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"Meet real people: introductions, and photos checked before you see them.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Yomizaka reads your leases, contracts and invoices.\",\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 motion: \"The motion lab: the kit's motion, played live with its knobs.\",\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 lang: \"en\",\n company: { story: \"Our story\", blog: \"Blog\", contact: \"Contact us\" },\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 : conversation, documents, images et vidéo — et le moteur des autres produits.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"Rencontrez de vraies personnes : des présentations, et des photos vérifiées avant que vous les voyiez.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Yomizaka lit vos baux, contrats et factures.\",\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 motion: \"Le labo du mouvement : le mouvement du kit, joué en direct, avec ses réglages.\",\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 lang: \"fr\",\n company: { story: \"Notre histoire\", blog: \"Blogue\", contact: \"Nous écrire\" },\n },\n};\n"
|
|
97
97
|
}
|
|
98
98
|
],
|
|
99
99
|
"dependencies": [],
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"description": "A catalogue number, the stamp, a sentence, two fields and the ways out.",
|
|
143
143
|
"path": "examples/specimen-tag/product.tsx",
|
|
144
144
|
"module": "@krizaka/ui/registry/examples/specimen-tag/product",
|
|
145
|
-
"code": "import { SpecimenTag } from \"@krizaka/ui/specimen-tag\";\n\n// A product labelled like a specimen: the catalogue number, the stamp for its honest status, a sentence and its record.\nexport default function SpecimenTagProduct() {\n return (\n <SpecimenTag\n className=\"max-w-sm\"\n code=\"KZ-02\"\n title=\"Yomizaka\"\n status={{ label: \"in development\", tone: \"wip\" }}\n fields={[\n { label: \"reads\", value: \"leases, contracts, invoices\" },\n { label: \"on Orazaka\", value: \"document checks\" },\n ]}\n footer={\n <>\n <a href=\"https://
|
|
145
|
+
"code": "import { SpecimenTag } from \"@krizaka/ui/specimen-tag\";\n\n// A product labelled like a specimen: the catalogue number, the stamp for its honest status, a sentence and its record.\nexport default function SpecimenTagProduct() {\n return (\n <SpecimenTag\n className=\"max-w-sm\"\n code=\"KZ-02\"\n title=\"Yomizaka\"\n status={{ label: \"in development\", tone: \"wip\" }}\n fields={[\n { label: \"reads\", value: \"leases, contracts, invoices\" },\n { label: \"on Orazaka\", value: \"document checks\" },\n ]}\n footer={\n <>\n <a href=\"https://www.yomizaka.com\">www.yomizaka.com ↗</a>\n <a href=\"https://github.com/krizaka/yomizaka\">source</a>\n </>\n }\n >\n <p>Leases, contracts and invoices, read and checked. Every finding quotes its passage.</p>\n </SpecimenTag>\n );\n}\n"
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
148
|
"name": "wire",
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
],
|
|
188
188
|
"demo": {
|
|
189
189
|
"path": "examples/specimen-tag/product.tsx",
|
|
190
|
-
"content": "import { SpecimenTag } from \"@krizaka/ui/specimen-tag\";\n\n// A product labelled like a specimen: the catalogue number, the stamp for its honest status, a sentence and its record.\nexport default function SpecimenTagProduct() {\n return (\n <SpecimenTag\n className=\"max-w-sm\"\n code=\"KZ-02\"\n title=\"Yomizaka\"\n status={{ label: \"in development\", tone: \"wip\" }}\n fields={[\n { label: \"reads\", value: \"leases, contracts, invoices\" },\n { label: \"on Orazaka\", value: \"document checks\" },\n ]}\n footer={\n <>\n <a href=\"https://
|
|
190
|
+
"content": "import { SpecimenTag } from \"@krizaka/ui/specimen-tag\";\n\n// A product labelled like a specimen: the catalogue number, the stamp for its honest status, a sentence and its record.\nexport default function SpecimenTagProduct() {\n return (\n <SpecimenTag\n className=\"max-w-sm\"\n code=\"KZ-02\"\n title=\"Yomizaka\"\n status={{ label: \"in development\", tone: \"wip\" }}\n fields={[\n { label: \"reads\", value: \"leases, contracts, invoices\" },\n { label: \"on Orazaka\", value: \"document checks\" },\n ]}\n footer={\n <>\n <a href=\"https://www.yomizaka.com\">www.yomizaka.com ↗</a>\n <a href=\"https://github.com/krizaka/yomizaka\">source</a>\n </>\n }\n >\n <p>Leases, contracts and invoices, read and checked. Every finding quotes its passage.</p>\n </SpecimenTag>\n );\n}\n"
|
|
191
191
|
},
|
|
192
192
|
"props": [
|
|
193
193
|
{
|