@chai-ui/vue 0.0.0-stage → 0.7.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/DESIGN.md +235 -0
- package/LICENSE +21 -0
- package/README.md +122 -2
- package/dist/ChaiProvider.vue.d.ts +24 -0
- package/dist/ChaiProvider.vue.d.ts.map +1 -0
- package/dist/Composer.vue.d.ts +94 -0
- package/dist/Composer.vue.d.ts.map +1 -0
- package/dist/EditCard.vue.d.ts +38 -0
- package/dist/EditCard.vue.d.ts.map +1 -0
- package/dist/MediaAnalyzer.vue.d.ts +54 -0
- package/dist/MediaAnalyzer.vue.d.ts.map +1 -0
- package/dist/PageSlide.vue.d.ts +23 -0
- package/dist/PageSlide.vue.d.ts.map +1 -0
- package/dist/ResultCard.vue.d.ts +29 -0
- package/dist/ResultCard.vue.d.ts.map +1 -0
- package/dist/composer/AttachMenu.vue.d.ts +17 -0
- package/dist/composer/AttachMenu.vue.d.ts.map +1 -0
- package/dist/composer/Chip.vue.d.ts +14 -0
- package/dist/composer/Chip.vue.d.ts.map +1 -0
- package/dist/dismiss.d.ts +4 -0
- package/dist/dismiss.d.ts.map +1 -0
- package/dist/edit-card/ExpandedImage.vue.d.ts +12 -0
- package/dist/edit-card/ExpandedImage.vue.d.ts.map +1 -0
- package/dist/edit-card/RegionShape.vue.d.ts +20 -0
- package/dist/edit-card/RegionShape.vue.d.ts.map +1 -0
- package/dist/icons.d.ts +43 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2521 -0
- package/dist/index.js.map +1 -0
- package/dist/page-slide.d.ts +31 -0
- package/dist/page-slide.d.ts.map +1 -0
- package/dist/primitives/Pagination.vue.d.ts +26 -0
- package/dist/primitives/Pagination.vue.d.ts.map +1 -0
- package/dist/primitives/SearchMenu.vue.d.ts +51 -0
- package/dist/primitives/SearchMenu.vue.d.ts.map +1 -0
- package/dist/primitives/Toggle.vue.d.ts +16 -0
- package/dist/primitives/Toggle.vue.d.ts.map +1 -0
- package/dist/primitives/search-menu.d.ts +19 -0
- package/dist/primitives/search-menu.d.ts.map +1 -0
- package/dist/reasoning.d.ts +6 -0
- package/dist/reasoning.d.ts.map +1 -0
- package/dist/result-card/IconButton.vue.d.ts +21 -0
- package/dist/result-card/IconButton.vue.d.ts.map +1 -0
- package/dist/result-card/MoreMenu.vue.d.ts +13 -0
- package/dist/result-card/MoreMenu.vue.d.ts.map +1 -0
- package/dist/result-card/ResultMedia.vue.d.ts +28 -0
- package/dist/result-card/ResultMedia.vue.d.ts.map +1 -0
- package/dist/result-card/ResultText.vue.d.ts +8 -0
- package/dist/result-card/ResultText.vue.d.ts.map +1 -0
- package/dist/result-card/VideoPlayer.vue.d.ts +18 -0
- package/dist/result-card/VideoPlayer.vue.d.ts.map +1 -0
- package/dist/result-card/types.d.ts +9 -0
- package/dist/result-card/types.d.ts.map +1 -0
- package/dist/style.css +2378 -0
- package/dist/useComposer.d.ts +22 -0
- package/dist/useComposer.d.ts.map +1 -0
- package/dist/useMediaAnalyzer.d.ts +17 -0
- package/dist/useMediaAnalyzer.d.ts.map +1 -0
- package/package.json +79 -3
- package/src/ChaiProvider.vue +26 -0
- package/src/Composer.vue +372 -0
- package/src/EditCard.vue +520 -0
- package/src/MediaAnalyzer.vue +216 -0
- package/src/PageSlide.vue +36 -0
- package/src/ResultCard.vue +297 -0
- package/src/composer/AttachMenu.vue +66 -0
- package/src/composer/Chip.vue +26 -0
- package/src/dismiss.ts +22 -0
- package/src/edit-card/ExpandedImage.vue +39 -0
- package/src/edit-card/RegionShape.vue +39 -0
- package/src/icons.ts +55 -0
- package/src/index.ts +77 -0
- package/src/page-slide.ts +32 -0
- package/src/primitives/Pagination.vue +76 -0
- package/src/primitives/SearchMenu.vue +167 -0
- package/src/primitives/Toggle.vue +23 -0
- package/src/primitives/search-menu.ts +20 -0
- package/src/reasoning.ts +10 -0
- package/src/result-card/IconButton.vue +18 -0
- package/src/result-card/MoreMenu.vue +35 -0
- package/src/result-card/ResultMedia.vue +77 -0
- package/src/result-card/ResultText.vue +37 -0
- package/src/result-card/VideoPlayer.vue +90 -0
- package/src/result-card/types.ts +10 -0
- package/src/useComposer.ts +38 -0
- package/src/useMediaAnalyzer.ts +33 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# CHAI UI — DESIGN.md
|
|
2
|
+
|
|
3
|
+
The rules for building with `@chai-ui/react` or `@chai-ui/vue`, written for
|
|
4
|
+
the people and the coding agents who integrate them. Both packages ship
|
|
5
|
+
this file. It uses React's names; "In Vue" below maps them. The API reference is the JSDoc on each
|
|
6
|
+
component's props and the docs site's `llms.txt`. This file covers how to
|
|
7
|
+
use the pieces correctly: which component to pick, which tokens to use,
|
|
8
|
+
which states you get for free, and what not to build.
|
|
9
|
+
|
|
10
|
+
If this file and the code disagree, the code wins. Please report the
|
|
11
|
+
mismatch.
|
|
12
|
+
|
|
13
|
+
## 1. Pick the component, don't rebuild it
|
|
14
|
+
|
|
15
|
+
| You need | Use | Not |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| A prompt box with attach, model and aspect-ratio pickers, submit | `Composer` | A hand-built textarea + button row |
|
|
18
|
+
| Media (image, video, audio) in, a generation-ready prompt out | `MediaAnalyzer` + `useMediaAnalyzer` | A file input plus your own model call |
|
|
19
|
+
| One prompt's generated output (image, video, audio, text) | `ResultCard` | A custom media card |
|
|
20
|
+
| Editing an image: marked regions, each with its own instruction | `EditCard` + `EDIT_IMAGE_USE_CASE` + `useComposer`'s `editImage` | A canvas or box-drawing library of your own |
|
|
21
|
+
| Submit → engine → result state, wired together | `useComposer` | Your own fetch/loading/error state |
|
|
22
|
+
| One reasoning model shared by every component | `ChaiProvider`, `useReasoning` | A text model picked per component |
|
|
23
|
+
| A switch, an option menu, a pager | `Toggle`, `SearchMenu`, `Pagination` | Another UI kit's versions inside a CHAI surface |
|
|
24
|
+
|
|
25
|
+
These don't exist yet. Don't import them, and don't invent stand-ins that
|
|
26
|
+
use these names: `Results`, `Compare`, `ModelPicker`,
|
|
27
|
+
`ParamControls`, `Presets`, `RunSummary`, `MediaViewer`, `createHttpEngine`.
|
|
28
|
+
|
|
29
|
+
## 2. Integration rules
|
|
30
|
+
|
|
31
|
+
1. **Load the CSS in order:** `@chai-ui/tokens/css` first, then
|
|
32
|
+
`@chai-ui/react/style.css` (or `@chai-ui/vue/style.css`). Component
|
|
33
|
+
styles read the token variables.
|
|
34
|
+
2. **Everything is controlled.** Every value comes in as a prop and every
|
|
35
|
+
change comes out as a callback (`value` / `onChange`). Keep the state in
|
|
36
|
+
your app.
|
|
37
|
+
3. **Advanced selection is the builder's opt-in.** `multiSelectModels`
|
|
38
|
+
and `multiSelectUseCases` are off by default. Turn them on only where
|
|
39
|
+
comparing models or making several media types at once is the point.
|
|
40
|
+
4. **A callback's presence is the opt-in.** Omit `onEnhance` and there's
|
|
41
|
+
no optimize button. Omit `onAbort` and there's no stop button. Omit
|
|
42
|
+
`moreActions` and there's no ⋯ menu. Don't pass no-op handlers just to
|
|
43
|
+
make a control appear.
|
|
44
|
+
5. **API keys never reach the browser.** Engines call one same-origin
|
|
45
|
+
route, `/api/chai/*`, served by `createChaiHandler` from
|
|
46
|
+
`@chai-ui/core/server`, which adds the key. Never pass a key to
|
|
47
|
+
`createFalEngine` or `createOpenRouterEngine`. In production, set the
|
|
48
|
+
handler's `authorize` and `allowedModels`.
|
|
49
|
+
6. **Media generation and reasoning are separate choices.** `engine`
|
|
50
|
+
generates media and defaults to Fal.ai. `ChaiProvider`'s
|
|
51
|
+
reasoning models power Enhance, routing and text requests. Defaults:
|
|
52
|
+
Claude Opus 5 for text and images, Gemini 3.8 Flash for video and
|
|
53
|
+
audio, on OpenRouter. They're suggestions; respect a builder's own
|
|
54
|
+
`reasoningModel`, `reasoningModelByKind` and `reasoningEngine`.
|
|
55
|
+
7. **Suggest models, never require them.** With no `models`, Composer and
|
|
56
|
+
MediaAnalyzer use the suggested lists (`suggestedModels`,
|
|
57
|
+
`SUGGESTED_ANALYSIS_MODELS`), each model carrying its verified aspect
|
|
58
|
+
ratios and Fal input format. A builder's own list always wins. Don't
|
|
59
|
+
make a builder write a model list to get a working Model menu.
|
|
60
|
+
8. **Only show what's real.** Show cost only when the engine reports it.
|
|
61
|
+
Don't show model rankings or leaderboard positions you can't keep up to
|
|
62
|
+
date. Don't show a control the selected model can't honor.
|
|
63
|
+
|
|
64
|
+
### In Vue
|
|
65
|
+
|
|
66
|
+
The same components, rules and behavior, with Vue's names:
|
|
67
|
+
|
|
68
|
+
- `value` / `onChange` is `v-model`; every other `x` / `onXChange` pair is
|
|
69
|
+
`v-model:x` (`v-model:attachments`, `v-model:model-id`, `v-model:regions`, …).
|
|
70
|
+
- Other callbacks are events: `@submit`, `@action`, `@enhance`, `@abort`.
|
|
71
|
+
Rule 4 holds: a control that needs a callback shows only with its
|
|
72
|
+
listener (`@enhance` for the optimize button, `@abort` for stop,
|
|
73
|
+
`@update:model-id` for the Model menu).
|
|
74
|
+
- `useComposer` and `useMediaAnalyzer` are composables returning refs.
|
|
75
|
+
Pass a getter for options that change: `useComposer(() => ({ engine, editImage: image.value }))`.
|
|
76
|
+
- `ChaiProvider` is a component with the same props.
|
|
77
|
+
|
|
78
|
+
## 3. Material components, and where Chai uses its own
|
|
79
|
+
|
|
80
|
+
Chai builds on Material Design 3's web components where they fit, and
|
|
81
|
+
uses its own component where they don't. Both kinds take every color from
|
|
82
|
+
Chai tokens (§4), so they look like one system.
|
|
83
|
+
|
|
84
|
+
| Native Material component | Where Chai uses it |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `<md-fab>` | The submit, retry and stop button |
|
|
87
|
+
| `<md-switch>` (`Toggle`) | Auto-select model, and any switch you add |
|
|
88
|
+
| `<md-checkbox>` | The checkboxes on rows in a multi-select model menu |
|
|
89
|
+
| `<md-outlined-text-field>` | The search field in searchable menus |
|
|
90
|
+
| `<md-focus-ring>` | The focus ring on all of the above |
|
|
91
|
+
|
|
92
|
+
Chai's own components are the menus (`SearchMenu` and the attach menu),
|
|
93
|
+
the use-case chips, `Pagination`, and the cards. The menus are the main
|
|
94
|
+
difference, because Material has an `<md-menu>`:
|
|
95
|
+
|
|
96
|
+
- **Placement.** `<md-menu>` opens as a floating popover anchored to a
|
|
97
|
+
corner of its trigger. Chai's menus open as a panel directly under their
|
|
98
|
+
trigger, inside the component, as the Figma designs show.
|
|
99
|
+
- **Features `<md-menu>` doesn't have.** Chai's model menus need a search
|
|
100
|
+
field, grouped sections with headings (one per use case), a switch
|
|
101
|
+
pinned at the top (auto-select), and multi-select. `<md-menu>` closes
|
|
102
|
+
when an item is picked and has none of these.
|
|
103
|
+
- **Portability.** Chai isn't tied to Material. Its own components restyle
|
|
104
|
+
entirely through tokens, which keeps the door open to other design
|
|
105
|
+
systems later.
|
|
106
|
+
|
|
107
|
+
They still follow Material's conventions where they apply: a trailing
|
|
108
|
+
check mark for single-select, MD3's own checkbox for multi-select,
|
|
109
|
+
and Material's type scale. Each option is a plain button: reach it with
|
|
110
|
+
Tab, pick it with Enter or Space; `aria-pressed` marks what's picked.
|
|
111
|
+
|
|
112
|
+
## 4. Tokens
|
|
113
|
+
|
|
114
|
+
All visual values come from `@chai-ui/tokens`, which builds from one JSON
|
|
115
|
+
source into CSS variables, JS/TS and a Figma token set. **It is the only
|
|
116
|
+
source of color:** no component contains a hard-coded color, and Material's
|
|
117
|
+
colors are driven by Chai tokens, not the other way around.
|
|
118
|
+
|
|
119
|
+
| Layer | Pattern | Use it for |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| Root palette | `--chai-color-root-{gray, green, amber, red}-*` | Nothing directly. It only feeds the layers below. `green-50` (`#009747`) is the brand green. |
|
|
122
|
+
| Brand and state | `--chai-color-semantic-{accent, accent-hover, accent-subtle, accent-soft, accent-text, text-on-accent, warning, warning-subtle, danger, danger-subtle}` | The brand green and state colors. Use `accent` for fills, icons and focus rings, and `accent-text` for green text (`accent` is only 3.8:1 on white). |
|
|
123
|
+
| Controls | `--chai-color-semantic-control-{surface, surface-hover, surface-raised, track, divider, border, border-soft, border-faint, text-strong, text, text-muted}` | The pure grays the components are built from. |
|
|
124
|
+
| Overlays and media | `--chai-color-semantic-{overlay, on-overlay, scrim, media-backdrop}` | Buttons and backdrops on top of images and video |
|
|
125
|
+
| Space | `--chai-space-0` … `--chai-space-8` (0, 4, 8, 12, 16, 24, 32, 48, 64px) | Padding, gaps, margins |
|
|
126
|
+
| Radius | `--chai-radius-{sm, md, lg, full}` | Corners |
|
|
127
|
+
| Type | `--chai-type-{family, size, weight, leading, tracking}-*` | Text |
|
|
128
|
+
| Motion | `--chai-motion-duration-{fast, base, slow, steep}`, `--chai-motion-easing-{standard, growth}` | Transitions |
|
|
129
|
+
| Effects | `--chai-shadow-menu`, `--chai-gradient-ai` | Floating menus, and the AI shimmer after prompt optimization |
|
|
130
|
+
| Component | `--chai-composer-{bg, border, hover, placeholder, text}`, `--chai-media-analyzer-{bg, border, hover, text}` | Restyling that one component. Each defaults to a control role. |
|
|
131
|
+
| Material | `--md-sys-color-*` | Nothing in your code. They exist so the Material switch, button and text field follow Chai tokens. |
|
|
132
|
+
|
|
133
|
+
Rules:
|
|
134
|
+
|
|
135
|
+
- **Never invent a token name.** If it isn't listed above or in
|
|
136
|
+
`@chai-ui/tokens/css`, it doesn't exist, and `var()` on an unknown name
|
|
137
|
+
silently falls back to nothing.
|
|
138
|
+
- **Never put a literal color on a component.** Use a semantic role. If a
|
|
139
|
+
design needs a color that has no role yet, add the role to
|
|
140
|
+
`packages/tokens/src/tokens.json` instead of writing the hex.
|
|
141
|
+
- **Restyle by overriding variables, not selectors.** Override the
|
|
142
|
+
semantic roles on `:root` to restyle everything, including the Material
|
|
143
|
+
components, or set a component variable like `--chai-composer-bg` on
|
|
144
|
+
`.chai-composer` to restyle one component. Don't target internal
|
|
145
|
+
`.chai-*__*` class names. Those are implementation details and can
|
|
146
|
+
change without notice.
|
|
147
|
+
|
|
148
|
+
## 5. AI states you get for free — use them, don't replace them
|
|
149
|
+
|
|
150
|
+
| Situation | Built-in behavior | Your part |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| Generation failed | `Composer`'s submit button becomes a retry action and an inline message appears (`submitError`) | Set `submitError`, and clear it on success or when the prompt changes. **No error dialogs or toasts for a failed generation.** |
|
|
153
|
+
| Request in flight | Submit becomes a stop button (`submitting` + `onAbort`) | Pass `useComposer`'s `cancel` as `onAbort`; results show "Stopped" |
|
|
154
|
+
| Prompt too thin | Optimize button rewrites the prompt, then offers undo (`onEnhance`) | Run the rewrite and call `onChange` with the new text |
|
|
155
|
+
| No use case picked | The prompt is a text request and gets a text reply | Set `defaultUseCase` if your app's default should be image or video |
|
|
156
|
+
| Result pending | `ResultCard` shows a spinner for `queued` / `running` | Nothing. `useComposer` sets the status. |
|
|
157
|
+
| Long text answer | `ResultCard` keeps line breaks, scrolls past 400px with a visible scrollbar, and follows streaming text unless the person scrolls up | Change the height with `textMaxHeight` if needed |
|
|
158
|
+
| Text streaming in | `ResultCard` renders partial text with a caret, marked `aria-busy` | Use an engine that calls `onText` (OpenRouter does, and so does the mock text engine). `useComposer` wires it. |
|
|
159
|
+
| Result failed | `ResultCard` shows the error message in place | Nothing |
|
|
160
|
+
| Image edit in progress | `EditCard` shows a spinner over the image being edited, then the edit as a new version | Nothing. `useComposer` adds the version. |
|
|
161
|
+
| Edit model's region limit | `EditCard` disables "New region" at `maxRegions` | Pass the chosen model's `maxRegions` |
|
|
162
|
+
| Try again | `ResultCard`'s retry fires `onAction("retry", result)` | Re-run the **same** model call, not a different one |
|
|
163
|
+
| Feedback | Like/dislike fire `onAction`, and the displayed vote comes from `getVote` | Store votes yourself |
|
|
164
|
+
| Details | The flip side shows prompt, model, tokens, cost and duration | Supply `models` so ids resolve to labels |
|
|
165
|
+
| Quality checks | Each result's `evaluations` render on the flip side: green with a check if it passed, red with a cross if it failed | Run your checks in your backend proxy and add a top-level `evaluations: [{ id, label, passed }]` field to the JSON it returns (for a stream, send it as its own `data:` chunk). The built-in Fal and OpenRouter engines read that field, a custom engine returns it from `generate`, and `useComposer` puts it on the matching `Result`. CHAI never runs or scores anything itself, and there are no confidence scores. |
|
|
166
|
+
|
|
167
|
+
`ResultCard` is where a run ends. Don't add a "use as input" action that
|
|
168
|
+
feeds a result back into a `Composer`. Chaining steps like that is
|
|
169
|
+
deliberately out of scope. The one exception is `EditCard`: an edit's result
|
|
170
|
+
is a new version of the same image, and the next edit applies to the version
|
|
171
|
+
showing.
|
|
172
|
+
|
|
173
|
+
## 6. Accessibility
|
|
174
|
+
|
|
175
|
+
- Every icon-only button in CHAI has an accessible name. In `moreActions`,
|
|
176
|
+
`label` is the item's visible and announced text, so pass an `icon` that
|
|
177
|
+
is `aria-hidden`.
|
|
178
|
+
- Animations (the optimize reveal, the streaming caret) are disabled under
|
|
179
|
+
`prefers-reduced-motion`. Your own motion around CHAI should do the same.
|
|
180
|
+
- **Focus rings are on by default, as WCAG 2.2 AA requires** (2.4.7 Focus
|
|
181
|
+
Visible). Every control shows a ring when it
|
|
182
|
+
has keyboard focus: buttons and menu items use the browser's own ring,
|
|
183
|
+
MD3 elements use `<md-focus-ring>`, and the Composer rings its whole pill
|
|
184
|
+
while the prompt field is focused. Hover fills don't replace these,
|
|
185
|
+
because hover never appears for someone navigating with a keyboard.
|
|
186
|
+
- **To hide them**, set `data-chai-focus-ring="hidden"` on any ancestor.
|
|
187
|
+
Every CHAI component inside it stops drawing focus rings, and your own
|
|
188
|
+
controls under the same ancestor are unaffected. Remove the attribute to
|
|
189
|
+
turn them back on:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<div data-chai-focus-ring={showFocusRings ? undefined : "hidden"}>…</div>
|
|
193
|
+
// or page-wide:
|
|
194
|
+
document.documentElement.dataset.chaiFocusRing = "hidden";
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Hiding focus rings breaks WCAG 2.2 AA (and 2.1 AA) compliance.** Keyboard users
|
|
198
|
+
lose any indication of where they are. Only hide them on showcase or
|
|
199
|
+
preview surfaces, like the docs site's component previews, not in a
|
|
200
|
+
production app. Don't set `tabIndex={-1}` on CHAI controls either.
|
|
201
|
+
- Streaming text is `aria-busy` until it settles, so screen readers read
|
|
202
|
+
the finished text once instead of every chunk.
|
|
203
|
+
- **Supports WCAG 2.2 AA.** Chai's components are built to support WCAG 2.2 AA and are tested
|
|
204
|
+
against it with automated checks: axe (WCAG 2.0, 2.1 and 2.2, levels A
|
|
205
|
+
and AA) on every component state in the test suite, plus keyboard and
|
|
206
|
+
focus tests in a browser. They haven't been tested by hand with a
|
|
207
|
+
screen reader, so this is support, not a certification. Compliance is
|
|
208
|
+
judged for a whole app, so test yours, including with a screen reader.
|
|
209
|
+
|
|
210
|
+
What that covers: contrast, focus rings, a name for every control
|
|
211
|
+
(`Composer`'s `promptLabel`, `Toggle`'s `ariaLabel`), alt text for
|
|
212
|
+
generated images (`ResultCard`'s `altText`, default the prompt), 24px
|
|
213
|
+
targets, and keyboard use: video play/pause (Tab to the video, Space)
|
|
214
|
+
and seeking (a slider: arrow keys, Home, End), paging (`Pagination` is
|
|
215
|
+
one slider rather than a button per dot, so the dots can sit 16px apart:
|
|
216
|
+
arrow keys, Home, End), the turned-away side of a
|
|
217
|
+
flipped card is `inert` and focus follows the flip, and the expanded
|
|
218
|
+
view is a dialog (focus inside, Escape closes, focus returns to Expand).
|
|
219
|
+
|
|
220
|
+
## 7. What's enforced
|
|
221
|
+
|
|
222
|
+
Inside this repo, `pnpm lint` runs `scripts/lint-tokens.mjs` over the
|
|
223
|
+
packages' CSS:
|
|
224
|
+
|
|
225
|
+
- It fails on `var(--…)` names that don't exist (invented tokens).
|
|
226
|
+
- It fails on any hard-coded color, custom properties included.
|
|
227
|
+
- It fails on direct use of Material's `--md-sys-color-*` roles, which
|
|
228
|
+
must go through the Chai token they map to.
|
|
229
|
+
|
|
230
|
+
TypeScript enforces the component APIs: controlled props, typed submit
|
|
231
|
+
payloads, and typed engine and result shapes. `ResultCard`'s `onAction`
|
|
232
|
+
is a plain `string` so it can carry your own `moreActions` ids, which
|
|
233
|
+
means a misspelled action name isn't caught. Nothing checks your
|
|
234
|
+
app's own CSS or markup yet, so sections 1–6 above are guidance there, not
|
|
235
|
+
guarantees.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kazarazat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,123 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://chai-ui.com"><img src="https://chai-ui.com/chai-wordmark.svg" alt="Chai UI" width="220" /></a>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@chai-ui/vue"><img src="https://img.shields.io/npm/v/@chai-ui/vue?label=npm&color=009747" alt="npm version" /></a>
|
|
7
|
+
<a href="https://github.com/kazarazat/chai-ui/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-009747" alt="MIT license" /></a>
|
|
8
|
+
<a href="https://github.com/kazarazat/chai-ui/actions/workflows/ci.yml"><img src="https://github.com/kazarazat/chai-ui/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
9
|
+
<a href="https://codecov.io/gh/kazarazat/chai-ui"><img src="https://codecov.io/gh/kazarazat/chai-ui/graph/badge.svg" alt="Coverage" /></a>
|
|
10
|
+
<a href="https://chai-ui.com/docs/"><img src="https://img.shields.io/badge/docs-chai--ui.com-009747" alt="Docs" /></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
# @chai-ui/vue
|
|
14
|
+
|
|
15
|
+
Vue components for the input and output surfaces of generative-media
|
|
16
|
+
apps: a prompt composer, a media-to-prompt analyzer, a result card and an
|
|
17
|
+
edit card, with the AI work built in and wired to Fal.ai, OpenRouter or
|
|
18
|
+
your own engine. Everything is controlled: you own the state with
|
|
19
|
+
`v-model`, and the components render it.
|
|
20
|
+
|
|
21
|
+
These are the same components as [`@chai-ui/react`](https://www.npmjs.com/package/@chai-ui/react),
|
|
22
|
+
with the same behavior. Both packages are thin layers over
|
|
23
|
+
`@chai-ui/core` and run one shared test suite, including the WCAG 2.2 AA
|
|
24
|
+
checks (axe, keyboard and focus).
|
|
25
|
+
|
|
26
|
+
Building with it, or pointing a coding agent at it? Read
|
|
27
|
+
[`DESIGN.md`](./DESIGN.md) (shipped in the package) for the usage rules,
|
|
28
|
+
and the docs site's [`llms.txt`](https://chai-ui.com/docs/llms.txt) for
|
|
29
|
+
the API. The code boxes on [chai-ui.com/docs](https://chai-ui.com/docs)
|
|
30
|
+
switch between React and Vue.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm add @chai-ui/vue @chai-ui/core @chai-ui/tokens
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Vue 3.5 or later, Nuxt included. No Chai-specific build setup.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// main.ts: tokens first, then the component styles that read them
|
|
42
|
+
import "@chai-ui/tokens/css";
|
|
43
|
+
import "@chai-ui/vue/style.css";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<!-- App.vue -->
|
|
50
|
+
<template>
|
|
51
|
+
<ChaiProvider><Create /></ChaiProvider>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<!-- Create.vue -->
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { computed, ref } from "vue";
|
|
57
|
+
import { Composer, ResultCard, createFalEngine, useComposer } from "@chai-ui/vue";
|
|
58
|
+
|
|
59
|
+
const engine = createFalEngine();
|
|
60
|
+
const value = ref("");
|
|
61
|
+
const { run, submit, cancel, enhance, enhancing } = useComposer({ engine });
|
|
62
|
+
const busy = computed(() => run.value?.results.some((r) => r.status === "queued" || r.status === "running") ?? false);
|
|
63
|
+
|
|
64
|
+
async function onEnhance(prompt: string) {
|
|
65
|
+
value.value = await enhance.value!(prompt);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<Composer v-model="value" :submitting="busy" :enhancing="enhancing" @submit="submit" @abort="cancel" @enhance="onEnhance" />
|
|
71
|
+
<ResultCard v-if="run" :results="run.results" :prompt="run.request.prompt" @action="() => {}" />
|
|
72
|
+
</template>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To try it before setting up a provider, leave out `engine` and
|
|
76
|
+
`ChaiProvider`: everything then runs on built-in mocks, with no network
|
|
77
|
+
calls and no keys.
|
|
78
|
+
|
|
79
|
+
## The Vue names
|
|
80
|
+
|
|
81
|
+
The props are React's, with Vue's conventions:
|
|
82
|
+
|
|
83
|
+
| React | Vue |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `value` + `onChange` | `v-model` |
|
|
86
|
+
| `x` + `onXChange` | `v-model:x`, e.g. `v-model:attachments`, `v-model:model-id`, `v-model:regions` |
|
|
87
|
+
| `onSubmit`, `onAction`, `onEnhance`, `onAbort` | `@submit`, `@action`, `@enhance`, `@abort` |
|
|
88
|
+
| `useComposer` / `useMediaAnalyzer` hooks | composables returning refs; pass a getter for options that change |
|
|
89
|
+
| `<EditCard {...edit} />` | `edit`'s fields one by one, with `@update:active-version` and `@update:regions` |
|
|
90
|
+
|
|
91
|
+
Some controls show only with a listener, as in React: the optimize button
|
|
92
|
+
with `@enhance`, stop with `@abort`, the Model menu with `@update:model-id`.
|
|
93
|
+
|
|
94
|
+
## What's in the package
|
|
95
|
+
|
|
96
|
+
- **`Composer`**: prompt, attachments, use cases, model and aspect-ratio
|
|
97
|
+
menus with suggested models, prompt optimization, auto-select routing,
|
|
98
|
+
stop and retry.
|
|
99
|
+
- **`MediaAnalyzer`** + **`useMediaAnalyzer`**: media in, a
|
|
100
|
+
generation-ready prompt out.
|
|
101
|
+
- **`ResultCard`**: one prompt's results, paged, with votes, retry,
|
|
102
|
+
download, share, details and streaming text.
|
|
103
|
+
- **`EditCard`**: marked regions with their own instructions, and each
|
|
104
|
+
edit as a new version.
|
|
105
|
+
- **`useComposer`**, **`ChaiProvider`**, and the `Toggle`, `SearchMenu`
|
|
106
|
+
and `Pagination` building blocks.
|
|
107
|
+
- The engines and types from `@chai-ui/core`, re-exported.
|
|
108
|
+
|
|
109
|
+
## Your API keys stay on your server
|
|
110
|
+
|
|
111
|
+
The engines call one route on your server, which adds the key. In Nuxt:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// server/api/chai/[...path].ts
|
|
115
|
+
import { createChaiHandler } from "@chai-ui/core/server";
|
|
116
|
+
|
|
117
|
+
const chai = createChaiHandler();
|
|
118
|
+
export default defineEventHandler((event) => chai.handle(toWebRequest(event)));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then set `FAL_KEY` and `OPENROUTER_API_KEY` on your server. Other servers,
|
|
122
|
+
and `authorize` and `allowedModels` for production, are in the
|
|
123
|
+
[`@chai-ui/core` README](https://github.com/kazarazat/chai-ui/tree/main/packages/core#the-server-route-chai-uicoreserver).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type GenerationEngine, type MediaKind } from "@chai-ui/core";
|
|
2
|
+
/** The reasoning engine and models every CHAI component inside shares. Same props as React's `ChaiProvider`. */
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
/** Text, image and routing model. Defaults to `DEFAULT_REASONING_MODEL`. */
|
|
5
|
+
reasoningModel?: string;
|
|
6
|
+
/** Per-attachment-kind models, merged over `DEFAULT_REASONING_MODEL_BY_KIND` one kind at a time. */
|
|
7
|
+
reasoningModelByKind?: Partial<Record<MediaKind, string>>;
|
|
8
|
+
/** The engine the reasoning models run on. Defaults to OpenRouter, independent of the generation engine. */
|
|
9
|
+
reasoningEngine?: GenerationEngine;
|
|
10
|
+
};
|
|
11
|
+
declare var __VLS_1: {};
|
|
12
|
+
type __VLS_Slots = {} & {
|
|
13
|
+
default?: (props: typeof __VLS_1) => any;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
19
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
20
|
+
new (): {
|
|
21
|
+
$slots: S;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=ChaiProvider.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChaiProvider.vue.d.ts","sourceRoot":"","sources":["../src/ChaiProvider.vue"],"names":[],"mappings":"AA8BA,OAAO,EAAuC,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAG3G,gHAAgH;AAChH,KAAK,WAAW,GAAG;IACjB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oGAAoG;IACpG,oBAAoB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,4GAA4G;IAC5G,eAAe,CAAC,EAAE,gBAAgB,CAAC;CACpC,CAAC;AAwBF,QAAA,IAAI,OAAO,IAAW,CAAC;AACvB,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAC/C,QAAA,MAAM,UAAU,kSAEd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compose bar for generative AI input: prompt field, attachments, use
|
|
3
|
+
* case, model and aspect-ratio menus, Enhance, and submit, all driven by
|
|
4
|
+
* which props are given. Same props as React's `Composer`, with `v-model`
|
|
5
|
+
* for the prompt and `v-model:…` for attachments, use cases, models, aspect
|
|
6
|
+
* ratio, auto-select and regions.
|
|
7
|
+
*
|
|
8
|
+
* Some controls show only with somewhere for them to go, so these listeners
|
|
9
|
+
* are props, checked for: `@update:attachments` (add and remove media),
|
|
10
|
+
* `@update:model-id` / `@update:model-ids` (the Model menu),
|
|
11
|
+
* `@update:aspect-ratio`, `@update:use-cases` and `@clear-use-case` (chip
|
|
12
|
+
* remove), `@update:regions`, `@enhance` (the Enhance button), `@abort` (stop
|
|
13
|
+
* while submitting) and `@attach-menu-select` (the + menu's use cases).
|
|
14
|
+
*/
|
|
15
|
+
import "@material/web/fab/fab.js";
|
|
16
|
+
import { type ComposerAttachment, type ComposerAttachMenuAction, type ComposerSubmitPayload, type ComposerUseCase, type EditRegion, type MediaKind, type ModelOption, type ParameterOption } from "@chai-ui/core";
|
|
17
|
+
type __VLS_Props = {
|
|
18
|
+
/** The prompt text. Controlled: use `v-model`. */
|
|
19
|
+
modelValue: string;
|
|
20
|
+
/** Defaults to "Describe media to create", or edit wording when the use case is an edit. */
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
/** The prompt field's accessible name. Defaults to `placeholder`, which disappears once the person types. */
|
|
23
|
+
promptLabel?: string;
|
|
24
|
+
attachments?: ComposerAttachment[];
|
|
25
|
+
"onUpdate:attachments"?: (next: ComposerAttachment[]) => void;
|
|
26
|
+
/** The use-case chip. Aspect ratio only renders for an image or video use case. */
|
|
27
|
+
useCase?: ComposerUseCase | null;
|
|
28
|
+
onClearUseCase?: () => void;
|
|
29
|
+
/** What a submit means with no use case picked. Defaults to `TEXT_USE_CASE`. Never shown as a chip. */
|
|
30
|
+
defaultUseCase?: ComposerUseCase;
|
|
31
|
+
/** Several use cases at once, at most one per kind, with `useCases` and `modelsByKind`. */
|
|
32
|
+
multiSelectUseCases?: boolean;
|
|
33
|
+
useCases?: ComposerUseCase[];
|
|
34
|
+
"onUpdate:useCases"?: (next: ComposerUseCase[]) => void;
|
|
35
|
+
/** Models per use-case kind, for `multiSelectUseCases`. A kind left out gets Chai's suggested models. */
|
|
36
|
+
modelsByKind?: Partial<Record<MediaKind, ModelOption[]>>;
|
|
37
|
+
/** The end user can pick several models, each producing its own result, with `modelIds`. */
|
|
38
|
+
multiSelectModels?: boolean;
|
|
39
|
+
modelIds?: string[];
|
|
40
|
+
"onUpdate:modelIds"?: (ids: string[]) => void;
|
|
41
|
+
/** Models for the use case. Leave out for Chai's suggested Fal models; `[]` for no Model menu. */
|
|
42
|
+
models?: ModelOption[];
|
|
43
|
+
modelId?: string | null;
|
|
44
|
+
"onUpdate:modelId"?: (id: string | null) => void;
|
|
45
|
+
/** The model is chosen at submit: `useComposer` asks the reasoning model. Turning it on clears the pick. */
|
|
46
|
+
autoSelectModel?: boolean;
|
|
47
|
+
/** An "Auto-select model" switch at the top of the Model menu, for the end user. */
|
|
48
|
+
showAutoSelectToggle?: boolean;
|
|
49
|
+
"onUpdate:autoSelectModel"?: (value: boolean) => void;
|
|
50
|
+
/** Your own labels or a shorter list; ratios the models don't take are left out either way. */
|
|
51
|
+
aspectRatios?: ParameterOption<string>[];
|
|
52
|
+
aspectRatio?: string | null;
|
|
53
|
+
"onUpdate:aspectRatio"?: (value: string) => void;
|
|
54
|
+
/** Shows the Enhance button; your handler runs the rewrite and updates the prompt. Gets the current prompt. */
|
|
55
|
+
onEnhance?: (value: string) => void;
|
|
56
|
+
/** True while your Enhance handler is running. */
|
|
57
|
+
enhancing?: boolean;
|
|
58
|
+
/** The last submit's failure: submit becomes retry, and the message shows inline. */
|
|
59
|
+
submitError?: string | null;
|
|
60
|
+
/** Overrides the built-in "empty prompt and no attachments" check. Ignored while `submitError` is set. */
|
|
61
|
+
submitDisabled?: boolean;
|
|
62
|
+
/** True while a request is in flight: with `@abort`, submit becomes stop; without, it's disabled. */
|
|
63
|
+
submitting?: boolean;
|
|
64
|
+
onAbort?: () => void;
|
|
65
|
+
onAttachMenuSelect?: (action: ComposerAttachMenuAction) => void;
|
|
66
|
+
/** Which + menu items to show. Each still shows only when it can work. */
|
|
67
|
+
attachMenuActions?: ComposerAttachMenuAction[];
|
|
68
|
+
/** An edit's regions (from `useComposer`'s `edit`), shown as colored chips while editing. */
|
|
69
|
+
regions?: EditRegion[];
|
|
70
|
+
"onUpdate:regions"?: (next: EditRegion[]) => void;
|
|
71
|
+
disabled?: boolean;
|
|
72
|
+
};
|
|
73
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
74
|
+
submit: (payload: ComposerSubmitPayload) => any;
|
|
75
|
+
"update:modelValue": (value: string) => any;
|
|
76
|
+
unsupportedFile: (fileName: string) => any;
|
|
77
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
78
|
+
onSubmit?: ((payload: ComposerSubmitPayload) => any) | undefined;
|
|
79
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
80
|
+
onUnsupportedFile?: ((fileName: string) => any) | undefined;
|
|
81
|
+
}>, {
|
|
82
|
+
attachments: ComposerAttachment[];
|
|
83
|
+
useCase: ComposerUseCase | null;
|
|
84
|
+
useCases: ComposerUseCase[];
|
|
85
|
+
modelIds: string[];
|
|
86
|
+
modelId: string | null;
|
|
87
|
+
aspectRatio: string | null;
|
|
88
|
+
submitError: string | null;
|
|
89
|
+
submitDisabled: boolean;
|
|
90
|
+
regions: EditRegion[];
|
|
91
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
92
|
+
declare const _default: typeof __VLS_export;
|
|
93
|
+
export default _default;
|
|
94
|
+
//# sourceMappingURL=Composer.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Composer.vue.d.ts","sourceRoot":"","sources":["../src/Composer.vue"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type EditRegion, type EditVersion } from "@chai-ui/core";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** The original image first, then each edit. Paged with the dots in the card's bottom row. */
|
|
4
|
+
versions: EditVersion[];
|
|
5
|
+
activeVersion: number;
|
|
6
|
+
/** The new regions on the image, each committed with its instruction. Share them with the Composer's `regions`. */
|
|
7
|
+
regions: EditRegion[];
|
|
8
|
+
/** The most regions the chosen model takes (`ModelOption.maxRegions`). "New region" is disabled at this many. */
|
|
9
|
+
maxRegions?: number;
|
|
10
|
+
/** A fixed starting width in px, fitted within `maxWidth` and `maxHeight`. */
|
|
11
|
+
width?: number;
|
|
12
|
+
/** Sizes the card from the image's own pixel width: `0.3` makes it 30% as wide as the image. */
|
|
13
|
+
scale?: number;
|
|
14
|
+
/** The widest the card gets, in px. With no `width` or `scale`, the card fills `maxWidth` × `maxHeight`, keeping the image's shape. */
|
|
15
|
+
maxWidth?: number;
|
|
16
|
+
/** The tallest the card gets, in px. */
|
|
17
|
+
maxHeight?: number;
|
|
18
|
+
/** Alt text for the image. */
|
|
19
|
+
alt?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
23
|
+
"update:activeVersion": (index: number) => any;
|
|
24
|
+
"update:regions": (next: EditRegion[]) => any;
|
|
25
|
+
action: (action: "download" | "share", version: EditVersion) => any;
|
|
26
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
|
+
"onUpdate:activeVersion"?: ((index: number) => any) | undefined;
|
|
28
|
+
"onUpdate:regions"?: ((next: EditRegion[]) => any) | undefined;
|
|
29
|
+
onAction?: ((action: "download" | "share", version: EditVersion) => any) | undefined;
|
|
30
|
+
}>, {
|
|
31
|
+
alt: string;
|
|
32
|
+
maxRegions: number;
|
|
33
|
+
maxWidth: number;
|
|
34
|
+
maxHeight: number;
|
|
35
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
36
|
+
declare const _default: typeof __VLS_export;
|
|
37
|
+
export default _default;
|
|
38
|
+
//# sourceMappingURL=EditCard.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditCard.vue.d.ts","sourceRoot":"","sources":["../src/EditCard.vue"],"names":[],"mappings":"AA2hBA,OAAO,EAiBL,KAAK,UAAU,EACf,KAAK,WAAW,EAKjB,MAAM,eAAe,CAAC;AAoBvB,KAAK,WAAW,GAAG;IACf,8FAA8F;IAC9F,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,mHAAmH;IACnH,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,iHAAiH;IACjH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uIAAuI;IACvI,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA60BJ,QAAA,MAAM,YAAY;;;;;;;;;;;;;6EAIhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns attached media into a prompt: a drop zone for images, a video or
|
|
3
|
+
* audio, a model menu with auto-select, a prompt-length menu, and submit.
|
|
4
|
+
* Pair with `useMediaAnalyzer`. Same props as React's `MediaAnalyzer`, with
|
|
5
|
+
* `v-model:attachments`, `v-model:model-id`, `v-model:auto-select-model`
|
|
6
|
+
* and `v-model:prompt-length`.
|
|
7
|
+
*/
|
|
8
|
+
import "@material/web/fab/fab.js";
|
|
9
|
+
import { type MediaAnalysisPromptLength, type MediaAnalyzerAttachment, type MediaAnalyzerSubmitPayload, type MediaKind, type ModelOption } from "@chai-ui/core";
|
|
10
|
+
type __VLS_Props = {
|
|
11
|
+
attachments: MediaAnalyzerAttachment[];
|
|
12
|
+
maxAttachmentsByKind?: Record<MediaKind, number>;
|
|
13
|
+
/** Fallback roster for a kind `modelsByKind` doesn't list. */
|
|
14
|
+
models?: ModelOption[];
|
|
15
|
+
/** Per-kind rosters; once a kind is attached, only its models show. Defaults to Chai's suggested OpenRouter models. */
|
|
16
|
+
modelsByKind?: Partial<Record<MediaKind, ModelOption[]>>;
|
|
17
|
+
/** The picked model. While `autoSelectModel` is on it's `null` until a submit routes, then the routed model. */
|
|
18
|
+
modelId: string | null;
|
|
19
|
+
/** The "Auto-select model" switch inside the model menu. Turning it on clears the pick. */
|
|
20
|
+
autoSelectModel: boolean;
|
|
21
|
+
/** `null` until a person picks one: the trigger says "Prompt length" rather than pre-filling a choice. */
|
|
22
|
+
promptLength: MediaAnalysisPromptLength | null;
|
|
23
|
+
promptLengthOptions: {
|
|
24
|
+
value: MediaAnalysisPromptLength;
|
|
25
|
+
label: string;
|
|
26
|
+
}[];
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
submitting?: boolean;
|
|
29
|
+
submitError?: string | null;
|
|
30
|
+
/** With a listener, submit turns into stop while `submitting` (wire it to `useMediaAnalyzer`'s `cancel`); without one, submit is just disabled. */
|
|
31
|
+
onAbort?: () => void;
|
|
32
|
+
};
|
|
33
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
34
|
+
submit: (payload: MediaAnalyzerSubmitPayload) => any;
|
|
35
|
+
unsupportedFile: (fileName: string) => any;
|
|
36
|
+
"update:attachments": (next: MediaAnalyzerAttachment[]) => any;
|
|
37
|
+
"update:modelId": (id: string | null) => any;
|
|
38
|
+
"update:autoSelectModel": (next: boolean) => any;
|
|
39
|
+
"update:promptLength": (value: MediaAnalysisPromptLength) => any;
|
|
40
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
41
|
+
onSubmit?: ((payload: MediaAnalyzerSubmitPayload) => any) | undefined;
|
|
42
|
+
onUnsupportedFile?: ((fileName: string) => any) | undefined;
|
|
43
|
+
"onUpdate:attachments"?: ((next: MediaAnalyzerAttachment[]) => any) | undefined;
|
|
44
|
+
"onUpdate:modelId"?: ((id: string | null) => any) | undefined;
|
|
45
|
+
"onUpdate:autoSelectModel"?: ((next: boolean) => any) | undefined;
|
|
46
|
+
"onUpdate:promptLength"?: ((value: MediaAnalysisPromptLength) => any) | undefined;
|
|
47
|
+
}>, {
|
|
48
|
+
modelsByKind: Partial<Record<MediaKind, ModelOption[]>>;
|
|
49
|
+
models: ModelOption[];
|
|
50
|
+
maxAttachmentsByKind: Record<MediaKind, number>;
|
|
51
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
52
|
+
declare const _default: typeof __VLS_export;
|
|
53
|
+
export default _default;
|
|
54
|
+
//# sourceMappingURL=MediaAnalyzer.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaAnalyzer.vue.d.ts","sourceRoot":"","sources":["../src/MediaAnalyzer.vue"],"names":[],"mappings":"AA2NA;;;;;;GAMG;AACH,OAAO,0BAA0B,CAAC;AAElC,OAAO,EASL,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,eAAe,CAAC;AAIvB,KAAK,WAAW,GAAG;IACf,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACvC,oBAAoB,CAAC,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjD,8DAA8D;IAC9D,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,uHAAuH;IACvH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IACzD,gHAAgH;IAChH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,2FAA2F;IAC3F,eAAe,EAAE,OAAO,CAAC;IACzB,0GAA0G;IAC1G,YAAY,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC/C,mBAAmB,EAAE;QAAE,KAAK,EAAE,yBAAyB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mJAAmJ;IACnJ,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAmaJ,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;6EAIhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Slide } from "./page-slide.js";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
slide: Slide | null;
|
|
4
|
+
pageClass?: string;
|
|
5
|
+
};
|
|
6
|
+
declare var __VLS_1: {};
|
|
7
|
+
type __VLS_Slots = {} & {
|
|
8
|
+
default?: (props: typeof __VLS_1) => any;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
|
+
end: () => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onEnd?: (() => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
18
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
19
|
+
new (): {
|
|
20
|
+
$slots: S;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=PageSlide.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageSlide.vue.d.ts","sourceRoot":"","sources":["../src/PageSlide.vue"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA+C/D,QAAA,IAAI,OAAO,IAAW,CAAC;AACvB,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAG/C,QAAA,MAAM,UAAU;;;;kFAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|