@krizaka/sovkora-design-system 0.0.0-stage → 4.0.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/CHANGELOG.md +244 -0
- package/LICENSE +202 -0
- package/README.md +82 -2
- package/package.json +112 -3
- package/registry/badge.json +97 -0
- package/registry/button.json +140 -0
- package/registry/chat-showcase.json +113 -0
- package/registry/clip-player.json +120 -0
- package/registry/command-palette.json +116 -0
- package/registry/examples/badge/model-state.tsx +16 -0
- package/registry/examples/button/send.tsx +18 -0
- package/registry/examples/chat-showcase/french.tsx +20 -0
- package/registry/examples/chat-showcase/sovereign-answer.tsx +20 -0
- package/registry/examples/clip-player/tea-pour.tsx +27 -0
- package/registry/examples/command-palette/workspace.tsx +30 -0
- package/registry/examples/icon/engine.tsx +26 -0
- package/registry/examples/icon/in-a-button.tsx +12 -0
- package/registry/examples/run-facts/clip-run.tsx +15 -0
- package/registry/examples/sentinel-mini/sidebar.tsx +11 -0
- package/registry/examples/skeleton/answer-loading.tsx +11 -0
- package/registry/examples/video-result/assistant.tsx +36 -0
- package/registry/examples/video-result/tea-pour.tsx +40 -0
- package/registry/foundations.json +2067 -0
- package/registry/icon.json +109 -0
- package/registry/index.json +242 -0
- package/registry/run-facts.json +75 -0
- package/registry/sentinel-mini.json +81 -0
- package/registry/skeleton.json +116 -0
- package/registry/video-result.json +167 -0
- package/src/components/Badge.tsx +36 -0
- package/src/components/Button.tsx +26 -0
- package/src/components/ChatShowcase.tsx +248 -0
- package/src/components/ClipPlayer.tsx +223 -0
- package/src/components/CommandPalette.tsx +104 -0
- package/src/components/CommandPalette.types.ts +33 -0
- package/src/components/Input.tsx +14 -0
- package/src/components/RunFacts.tsx +36 -0
- package/src/components/SentinelMini.tsx +61 -0
- package/src/components/Skeleton.tsx +50 -0
- package/src/components/VideoResult.tsx +123 -0
- package/src/components/badge.meta.ts +34 -0
- package/src/components/button.meta.ts +35 -0
- package/src/components/chat-showcase.css.ts +19 -0
- package/src/components/chat-showcase.meta.ts +50 -0
- package/src/components/clip-player.meta.ts +48 -0
- package/src/components/command-palette.meta.ts +47 -0
- package/src/components/icon.meta.ts +45 -0
- package/src/components/run-facts.meta.ts +32 -0
- package/src/components/sentinel-mini.meta.ts +39 -0
- package/src/components/skeleton.meta.ts +42 -0
- package/src/components/video-result.meta.ts +53 -0
- package/src/icon.registry.tsx +737 -0
- package/src/icon.tsx +74 -0
- package/src/index.ts +43 -0
- package/src/meta.ts +76 -0
- package/src/theme.css +859 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Button",
|
|
5
|
+
summary:
|
|
6
|
+
"The @krizaka/ui button with the Sovkora 1.x default kept: without a `variant`, it is the primary (orange) button.",
|
|
7
|
+
why: "Sovkora 1.x buttons were primary by default; the platform's default is `secondary`. This adapter keeps the 1.x default so the apps written against it look the same — everything else is the primitive's.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "actions",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: ["ui/button"],
|
|
12
|
+
whenToUse: ["In code already importing `Button` from `@krizaka/sovkora-design-system`."],
|
|
13
|
+
whenNotToUse: [
|
|
14
|
+
{ when: "In new code: import `Button` from `@krizaka/ui/button` and say the variant.", use: "ui/button" },
|
|
15
|
+
],
|
|
16
|
+
bestPractices: [
|
|
17
|
+
'One primary action per view: pass `variant="secondary"` or `"ghost"` to the others.',
|
|
18
|
+
"Lock it while sending (`disabled` + `loading`): the input-blocking rule (ERR-126).",
|
|
19
|
+
],
|
|
20
|
+
accessibility: {
|
|
21
|
+
keyboard: [],
|
|
22
|
+
notes: ["The primitive's: a native `<button>`, its focus ring, `loading` sets `aria-busy`."],
|
|
23
|
+
},
|
|
24
|
+
related: ["ui/button", "icon"],
|
|
25
|
+
web: {
|
|
26
|
+
imports: ["Button"],
|
|
27
|
+
examples: [
|
|
28
|
+
{
|
|
29
|
+
name: "send",
|
|
30
|
+
title: "Send a prompt",
|
|
31
|
+
description: "The primary by default; locked while the answer is generated.",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The showcase's own keyframes (the pulse, the typing dots, the caret), all stopped under reduced motion. */
|
|
2
|
+
export const CHAT_SHOWCASE_CSS = `
|
|
3
|
+
@keyframes kzc-pulse {
|
|
4
|
+
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--kz-success) 55%, transparent); }
|
|
5
|
+
70%, 100% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--kz-success) 0%, transparent); }
|
|
6
|
+
}
|
|
7
|
+
@keyframes kzc-typing {
|
|
8
|
+
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
9
|
+
30% { transform: translateY(-4px); opacity: 1; }
|
|
10
|
+
}
|
|
11
|
+
@keyframes kzc-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
|
|
12
|
+
.kzc-dot { animation: kzc-pulse 2s ease-out infinite; }
|
|
13
|
+
.kzc-typing { animation: kzc-typing 1.1s ease-in-out infinite; }
|
|
14
|
+
.kzc-typing:nth-child(2) { animation-delay: 0.18s; } .kzc-typing:nth-child(3) { animation-delay: 0.36s; }
|
|
15
|
+
.kzc-caret { animation: kzc-blink 1s steps(2) infinite; }
|
|
16
|
+
@media (prefers-reduced-motion: reduce) {
|
|
17
|
+
.kzc-dot, .kzc-typing, .kzc-caret { animation: none; }
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Chat showcase",
|
|
5
|
+
summary:
|
|
6
|
+
"A self-playing sovereign AI chat: a question, the answer typed out, the interceptor pipeline it went through, and the promise that nothing left the premises.",
|
|
7
|
+
why: "Sovkora sells one idea — your AI answers on your own machines, through a deterministic pipeline, with zero data egress. A chat bubble from @krizaka/ui cannot tell that story; this composite stages it the same way on the website, the client app and the console: the persona asks, the engine answers locally, the pipeline chips land, the privacy line closes.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "data-display",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: [],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"On a home or a product page, to show what Sovkora does in one glance.",
|
|
14
|
+
"In an empty state of the chat (first visit), as a demonstration before the first question.",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "For a real conversation: this is a scripted mockup, not a chat." },
|
|
18
|
+
{ when: "For a short status of the engine (online, model).", use: "ui/badge" },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
'Speak as a named persona with a real need ("Eric, finance lead"), never an admin or a test account.',
|
|
22
|
+
"The pipeline names steps the reader understands (`PII masked`, `Routed locally`), not class names.",
|
|
23
|
+
"Every word is a prop (`labels`, `question`, `answer`, `pipeline`): the app translates them.",
|
|
24
|
+
"One per page: it moves, and two moving mockups compete.",
|
|
25
|
+
],
|
|
26
|
+
accessibility: {
|
|
27
|
+
keyboard: [],
|
|
28
|
+
notes: [
|
|
29
|
+
'`role="img"` with the question and the answer as its name: a screen reader hears the exchange once, not the typing.',
|
|
30
|
+
"Under `prefers-reduced-motion` the final state shows at once: no typing, no pulse, no caret.",
|
|
31
|
+
"The composer is the kit's `MessageComposer` (pill), `inert` and `aria-hidden`: it is the same component as the app's chat, not a field here.",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
related: ["icon", "sentinel-mini", "ui/card"],
|
|
35
|
+
web: {
|
|
36
|
+
imports: ["ChatShowcase"],
|
|
37
|
+
examples: [
|
|
38
|
+
{
|
|
39
|
+
name: "sovereign-answer",
|
|
40
|
+
title: "A sovereign answer",
|
|
41
|
+
description: "Eric asks about a contract; the answer is computed on premises and the pipeline shows the steps.",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "french",
|
|
45
|
+
title: "In the reader's language",
|
|
46
|
+
description: "The same mockup with the words of another locale — every word is a prop.",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Clip player",
|
|
5
|
+
summary:
|
|
6
|
+
"A short clip that is plainly a clip: a muted loop that plays in view, a badge with its length, play/pause and sound buttons, and a click that opens it larger.",
|
|
7
|
+
why: "A 5-second generated clip is shown as a loop, not as a video page: it must read as a video at a glance (badge), play by itself only while it is on screen, stay quiet, give the visitor the controls on hover and focus, and open large with the native controls. @krizaka/ui's player is built for long videos; this one is the product's way to show a result.",
|
|
8
|
+
status: "beta",
|
|
9
|
+
category: "data-display",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: ["ui/button", "ui/dialog"],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"A generated clip of a few seconds in a gallery, a result page or a card.",
|
|
14
|
+
"Anywhere a loop must start and stop with the viewport and respect reduced motion.",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "A long video, subtitles, chapters or streaming.", use: "ui/video-player" },
|
|
18
|
+
{ when: "A decorative background loop with no controls: a muted `<video>` is enough." },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
"Give both files: `webm` is tried first, `mp4` is the fallback every browser reads.",
|
|
22
|
+
"Give the poster: it is what reduced motion and the first paint show.",
|
|
23
|
+
"Name the clip in a sentence (`title`): it is the video's accessible name and the dialog's title.",
|
|
24
|
+
],
|
|
25
|
+
accessibility: {
|
|
26
|
+
keyboard: [
|
|
27
|
+
{ keys: "Tab", action: "Reaches the frame (open larger), play/pause and sound." },
|
|
28
|
+
{ keys: "Enter / Space", action: "Activates the focused button." },
|
|
29
|
+
{ keys: "Escape", action: "Closes the large view." },
|
|
30
|
+
],
|
|
31
|
+
notes: [
|
|
32
|
+
"Nothing plays by itself under `prefers-reduced-motion`: the poster and a play button.",
|
|
33
|
+
"The clip is muted and `playsInline`; the sound button is a toggle (`aria-pressed`).",
|
|
34
|
+
"A pause by hand sticks: scrolling away and back does not restart it.",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
related: ["video-result", "run-facts", "ui/video-player"],
|
|
38
|
+
web: {
|
|
39
|
+
imports: ["ClipPlayer"],
|
|
40
|
+
examples: [
|
|
41
|
+
{
|
|
42
|
+
name: "tea-pour",
|
|
43
|
+
title: "A loop in a card",
|
|
44
|
+
description: "Eric's tea-pour shot: badge, controls on hover, opens larger on click.",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Command palette",
|
|
5
|
+
summary:
|
|
6
|
+
"The Sovkora ⌘K palette: jump to a studio, a chat, a setting — sections, registry icons and a footer of hints, on the platform's command dialog.",
|
|
7
|
+
why: "`CommandDialog` from @krizaka/ui gives the combobox, the filtering, the focus trap and Escape. Sovkora adds what is its own: the ⌘K / Ctrl+K shortcut, entries grouped by section with the Sovkora icon registry, and the footer of keyboard hints its consoles share.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "navigation",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: ["ui/command", "ui/kbd", "ui/dialog"],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"Once per app shell: the client app and the console, for power users who navigate by keyboard.",
|
|
14
|
+
"To reach an action that has no place in the navigation (new chat, switch theme).",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "As the only way to reach a page: everything stays reachable from the navigation." },
|
|
18
|
+
{ when: "For a search over content (documents, conversations) with results to read.", use: "ui/command" },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
"Pass `commands` and `labels` translated (both required since 3.0).",
|
|
22
|
+
"Give `onNavigate` outside Next.js (or in tests); inside, it uses the router.",
|
|
23
|
+
"Short labels, verbs for actions (`New chat`), nouns for places (`Studios`).",
|
|
24
|
+
],
|
|
25
|
+
accessibility: {
|
|
26
|
+
keyboard: [
|
|
27
|
+
{ keys: "⌘ K / Ctrl K", action: "Opens and closes the palette." },
|
|
28
|
+
{ keys: "↑ ↓", action: "Moves through the results." },
|
|
29
|
+
{ keys: "Enter", action: "Runs the highlighted entry." },
|
|
30
|
+
{ keys: "Esc", action: "Closes it and returns the focus." },
|
|
31
|
+
],
|
|
32
|
+
notes: [
|
|
33
|
+
"A dialog with a combobox and a listbox (cmdk on Radix): named by `labels.label`, results by `labels.results`.",
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
related: ["icon", "ui/command", "ui/kbd"],
|
|
37
|
+
web: {
|
|
38
|
+
imports: ["CommandPalette"],
|
|
39
|
+
examples: [
|
|
40
|
+
{
|
|
41
|
+
name: "workspace",
|
|
42
|
+
title: "Eric's workspace",
|
|
43
|
+
description: "Studios, chats and settings, opened with ⌘K or the button.",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Icon",
|
|
5
|
+
summary:
|
|
6
|
+
"The Sovkora icon registry: one `Icon` component and a typed `name` for every product concept — studios, models, the pipeline, RAG, MCP.",
|
|
7
|
+
why: "Sovkora's screens name AI concepts (a router, a refiner, a memory, a cost shield) that no generic set draws. The contract centralises them in one registry, so an app never imports an icon library directly and every concept has one picture across the client, the console and the website.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "foundations",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: [],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"Every icon of an Sovkora screen: navigation, buttons, lists, the palette.",
|
|
14
|
+
"A concept of the engine (model, pipeline, router) shown next to its name.",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "For a Krizaka platform concept on the website (the signature set): use @krizaka/icons." },
|
|
18
|
+
{ when: "As the product's logo: use `SovkoraLogo` from @krizaka/ui." },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
"Never import lucide or another set in an app: add the icon to the registry.",
|
|
22
|
+
"An icon-only button carries an `aria-label` (`IconButton` from @krizaka/ui requires one).",
|
|
23
|
+
"Colour through the text role (`text-fg-secondary`, `text-accent`): the strokes are `currentColor`.",
|
|
24
|
+
],
|
|
25
|
+
accessibility: {
|
|
26
|
+
keyboard: [],
|
|
27
|
+
notes: ["Decorative by default (`aria-hidden`): the text or the button around it names it."],
|
|
28
|
+
},
|
|
29
|
+
related: ["command-palette", "sentinel-mini", "ui/button"],
|
|
30
|
+
web: {
|
|
31
|
+
imports: ["Icon"],
|
|
32
|
+
examples: [
|
|
33
|
+
{
|
|
34
|
+
name: "engine",
|
|
35
|
+
title: "The engine's concepts",
|
|
36
|
+
description: "Router, pipeline, model, RAG, memory, MCP — the vocabulary of Sovkora.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "in-a-button",
|
|
40
|
+
title: "In a button",
|
|
41
|
+
description: "An icon beside a label, coloured by the button's role.",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Run facts",
|
|
5
|
+
summary: "What a run recorded, as a description list: label left, value right, in mono.",
|
|
6
|
+
why: "Sovkora shows what proves a result — model, seed, frame, length, time, cost, run id — the same way everywhere. A long id breaks instead of overflowing, so the same list reads on a phone and in a wide card.",
|
|
7
|
+
status: "beta",
|
|
8
|
+
category: "data-display",
|
|
9
|
+
platforms: "web",
|
|
10
|
+
builtOn: [],
|
|
11
|
+
whenToUse: [
|
|
12
|
+
"Under a generated result (a clip, an image) to say how it was made.",
|
|
13
|
+
"In a run's detail, beside its steps.",
|
|
14
|
+
],
|
|
15
|
+
whenNotToUse: [{ when: "Many rows to compare side by side." }],
|
|
16
|
+
bestPractices: [
|
|
17
|
+
"Say the real source (`Made by`, `Run by`), never a flattering one.",
|
|
18
|
+
"Values are as the run recorded them: never rounded to look better.",
|
|
19
|
+
],
|
|
20
|
+
accessibility: { keyboard: [], notes: ["A `dl`: a screen reader reads each label with its value."] },
|
|
21
|
+
related: ["video-result", "clip-player"],
|
|
22
|
+
web: {
|
|
23
|
+
imports: ["RunFacts"],
|
|
24
|
+
examples: [
|
|
25
|
+
{
|
|
26
|
+
name: "clip-run",
|
|
27
|
+
title: "The run of a clip",
|
|
28
|
+
description: "Model, seed, frame, length and the run id of Eric's tea-pour shot.",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Sentinel mini",
|
|
5
|
+
summary:
|
|
6
|
+
"The Sentinel, Sovkora's guardian, at 24 px: a core, a ring and an orbit in the accent — for a sidebar, a header, a loading corner.",
|
|
7
|
+
why: "The Sovkora mark (`SovkoraLogo`, @krizaka/ui) signs the product; the Sentinel stands for what watches over the requests — the interceptors. It is drawn once here, in the accent role, so it follows the brand and every named theme.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "foundations",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: [],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"Next to a section that the governance watches (the console's pipeline, the audit trail).",
|
|
14
|
+
"As a small brand accent in a dense sidebar where the mark would be too much.",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "As the product's logo: use `SovkoraLogo` from @krizaka/ui." },
|
|
18
|
+
{ when: "As a loading indicator.", use: "ui/spinner" },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
"Decorative: put it next to a text that says what it means.",
|
|
22
|
+
"Keep it small (16–32 px): its strokes are drawn for that size.",
|
|
23
|
+
],
|
|
24
|
+
accessibility: {
|
|
25
|
+
keyboard: [],
|
|
26
|
+
notes: ["`aria-hidden`: decorative.", "Strokes and fills read `--kz-accent`: it follows the theme."],
|
|
27
|
+
},
|
|
28
|
+
related: ["chat-showcase", "icon", "ui/mascots"],
|
|
29
|
+
web: {
|
|
30
|
+
imports: ["SentinelMini"],
|
|
31
|
+
examples: [
|
|
32
|
+
{
|
|
33
|
+
name: "sidebar",
|
|
34
|
+
title: "In a sidebar heading",
|
|
35
|
+
description: "Beside the name of the section the governance watches.",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Skeleton",
|
|
5
|
+
summary:
|
|
6
|
+
"The @krizaka/ui skeleton (default shape `rect`) with the 1.x shorthands, and `SkeletonGroup` — a paragraph of text lines.",
|
|
7
|
+
why: "Sovkora 1.x sized skeletons with `width` / `height` and chose the shape with `variant`; this adapter keeps them (deprecated) on the primitive, and adds `SkeletonGroup`, the paragraph its chat and document views wait on.",
|
|
8
|
+
status: "stable",
|
|
9
|
+
category: "feedback",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: ["ui/skeleton"],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"While an answer, a document or a list loads: the shape of what is coming.",
|
|
14
|
+
"`SkeletonGroup` for a paragraph (an answer being prepared).",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{
|
|
18
|
+
when: "In new code for a single block: import `Skeleton` from `@krizaka/ui/skeleton` and size it with `className`.",
|
|
19
|
+
use: "ui/skeleton",
|
|
20
|
+
},
|
|
21
|
+
{ when: "For an action in progress (a button, a send).", use: "ui/spinner" },
|
|
22
|
+
],
|
|
23
|
+
bestPractices: [
|
|
24
|
+
"Size with `className` (`h-4 w-32`), not `width` / `height` (deprecated).",
|
|
25
|
+
"Match the layout that replaces it, so nothing jumps.",
|
|
26
|
+
],
|
|
27
|
+
accessibility: {
|
|
28
|
+
keyboard: [],
|
|
29
|
+
notes: ["Hidden from assistive technology: announce the loading state elsewhere (`aria-busy` on the region)."],
|
|
30
|
+
},
|
|
31
|
+
related: ["ui/skeleton", "ui/spinner"],
|
|
32
|
+
web: {
|
|
33
|
+
imports: ["Skeleton", "SkeletonGroup"],
|
|
34
|
+
examples: [
|
|
35
|
+
{
|
|
36
|
+
name: "answer-loading",
|
|
37
|
+
title: "An answer loading",
|
|
38
|
+
description: "The avatar and three lines while the engine prepares the answer.",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
} satisfies ComponentMeta;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ComponentMeta } from "../meta";
|
|
2
|
+
|
|
3
|
+
export const meta = {
|
|
4
|
+
title: "Video result",
|
|
5
|
+
summary:
|
|
6
|
+
"What a video run of a Studio leaves: the clip in its player (a muted loop in view, badge, controls, opens larger), the exact prompt, what the run recorded, the command that makes it again, and a download.",
|
|
7
|
+
why: "Sovkora's promise is that a result is proven, not claimed: a generated clip comes with its prompt, its model, its seed, its frame, its length, its time, its cost and the id of the run that made it, and says how to make it again. @krizaka/ui has the player and the code block; this composite is the product's way of showing a run's result once, the same on every screen.",
|
|
8
|
+
status: "beta",
|
|
9
|
+
category: "data-display",
|
|
10
|
+
platforms: "web",
|
|
11
|
+
builtOn: ["ui/code-block", "ui/button"],
|
|
12
|
+
whenToUse: [
|
|
13
|
+
"On the page of a finished run of a video Studio, in the console and in the gallery of a site.",
|
|
14
|
+
"Anywhere a generated clip must come with what proves where it came from.",
|
|
15
|
+
],
|
|
16
|
+
whenNotToUse: [
|
|
17
|
+
{ when: "For a short looping accent with no words: a muted `<video>` that plays in view is enough." },
|
|
18
|
+
{ when: "For a clip the visitor must control in depth (subtitles, speed, chapters).", use: "ui/video-player" },
|
|
19
|
+
],
|
|
20
|
+
bestPractices: [
|
|
21
|
+
"Say the real source in the facts (`Made by DigitalOcean serverless inference · run by Sovkora`), never a flattering one.",
|
|
22
|
+
"Give the frame the clip was really made in (`orientation`), never crop a landscape clip into a portrait one.",
|
|
23
|
+
"Every word is a prop (`labels`, `facts`): the app translates them; the prompt stays as the model received it.",
|
|
24
|
+
],
|
|
25
|
+
accessibility: {
|
|
26
|
+
keyboard: [
|
|
27
|
+
{ keys: "Enter / Space", action: "Activates the focused button: open larger, play or pause, sound." },
|
|
28
|
+
{ keys: "Tab", action: "Moves through the player controls, the copy button and the download link." },
|
|
29
|
+
],
|
|
30
|
+
notes: [
|
|
31
|
+
"The video is named (`title`); it is a muted loop that plays only in view, never under reduced motion (poster and a play button).",
|
|
32
|
+
"The facts are a description list; the command is a code block with a polite copy confirmation.",
|
|
33
|
+
'The prompt is marked `lang="en"`: it is what the model received, whatever the page\'s language.',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
related: ["clip-player", "run-facts", "ui/video-player", "ui/code-block"],
|
|
37
|
+
web: {
|
|
38
|
+
imports: ["VideoResult"],
|
|
39
|
+
examples: [
|
|
40
|
+
{
|
|
41
|
+
name: "tea-pour",
|
|
42
|
+
title: "A landscape clip with its run",
|
|
43
|
+
description:
|
|
44
|
+
"Eric's tea-pour shot: the poster, the prompt, the facts of the run, the command and the download.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "assistant",
|
|
48
|
+
title: "A clip without its command",
|
|
49
|
+
description: "The player, the prompt and the facts of the run; no command, no download.",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
} satisfies ComponentMeta;
|