@chai-ui/vue 0.0.0-stage → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/DESIGN.md +235 -0
  2. package/LICENSE +21 -0
  3. package/README.md +122 -2
  4. package/dist/ChaiProvider.vue.d.ts +24 -0
  5. package/dist/ChaiProvider.vue.d.ts.map +1 -0
  6. package/dist/Composer.vue.d.ts +94 -0
  7. package/dist/Composer.vue.d.ts.map +1 -0
  8. package/dist/EditCard.vue.d.ts +38 -0
  9. package/dist/EditCard.vue.d.ts.map +1 -0
  10. package/dist/MediaAnalyzer.vue.d.ts +54 -0
  11. package/dist/MediaAnalyzer.vue.d.ts.map +1 -0
  12. package/dist/PageSlide.vue.d.ts +23 -0
  13. package/dist/PageSlide.vue.d.ts.map +1 -0
  14. package/dist/ResultCard.vue.d.ts +29 -0
  15. package/dist/ResultCard.vue.d.ts.map +1 -0
  16. package/dist/composer/AttachMenu.vue.d.ts +17 -0
  17. package/dist/composer/AttachMenu.vue.d.ts.map +1 -0
  18. package/dist/composer/Chip.vue.d.ts +14 -0
  19. package/dist/composer/Chip.vue.d.ts.map +1 -0
  20. package/dist/dismiss.d.ts +4 -0
  21. package/dist/dismiss.d.ts.map +1 -0
  22. package/dist/edit-card/ExpandedImage.vue.d.ts +12 -0
  23. package/dist/edit-card/ExpandedImage.vue.d.ts.map +1 -0
  24. package/dist/edit-card/RegionShape.vue.d.ts +20 -0
  25. package/dist/edit-card/RegionShape.vue.d.ts.map +1 -0
  26. package/dist/icons.d.ts +43 -0
  27. package/dist/icons.d.ts.map +1 -0
  28. package/dist/index.d.ts +16 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +2515 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/page-slide.d.ts +31 -0
  33. package/dist/page-slide.d.ts.map +1 -0
  34. package/dist/primitives/Pagination.vue.d.ts +26 -0
  35. package/dist/primitives/Pagination.vue.d.ts.map +1 -0
  36. package/dist/primitives/SearchMenu.vue.d.ts +51 -0
  37. package/dist/primitives/SearchMenu.vue.d.ts.map +1 -0
  38. package/dist/primitives/Toggle.vue.d.ts +16 -0
  39. package/dist/primitives/Toggle.vue.d.ts.map +1 -0
  40. package/dist/primitives/search-menu.d.ts +19 -0
  41. package/dist/primitives/search-menu.d.ts.map +1 -0
  42. package/dist/reasoning.d.ts +6 -0
  43. package/dist/reasoning.d.ts.map +1 -0
  44. package/dist/result-card/IconButton.vue.d.ts +21 -0
  45. package/dist/result-card/IconButton.vue.d.ts.map +1 -0
  46. package/dist/result-card/MoreMenu.vue.d.ts +13 -0
  47. package/dist/result-card/MoreMenu.vue.d.ts.map +1 -0
  48. package/dist/result-card/ResultMedia.vue.d.ts +28 -0
  49. package/dist/result-card/ResultMedia.vue.d.ts.map +1 -0
  50. package/dist/result-card/ResultText.vue.d.ts +8 -0
  51. package/dist/result-card/ResultText.vue.d.ts.map +1 -0
  52. package/dist/result-card/VideoPlayer.vue.d.ts +18 -0
  53. package/dist/result-card/VideoPlayer.vue.d.ts.map +1 -0
  54. package/dist/result-card/types.d.ts +9 -0
  55. package/dist/result-card/types.d.ts.map +1 -0
  56. package/dist/style.css +2378 -0
  57. package/dist/useComposer.d.ts +22 -0
  58. package/dist/useComposer.d.ts.map +1 -0
  59. package/dist/useMediaAnalyzer.d.ts +17 -0
  60. package/dist/useMediaAnalyzer.d.ts.map +1 -0
  61. package/package.json +79 -3
  62. package/src/ChaiProvider.vue +26 -0
  63. package/src/Composer.vue +362 -0
  64. package/src/EditCard.vue +520 -0
  65. package/src/MediaAnalyzer.vue +216 -0
  66. package/src/PageSlide.vue +36 -0
  67. package/src/ResultCard.vue +297 -0
  68. package/src/composer/AttachMenu.vue +66 -0
  69. package/src/composer/Chip.vue +26 -0
  70. package/src/dismiss.ts +22 -0
  71. package/src/edit-card/ExpandedImage.vue +39 -0
  72. package/src/edit-card/RegionShape.vue +39 -0
  73. package/src/icons.ts +55 -0
  74. package/src/index.ts +77 -0
  75. package/src/page-slide.ts +32 -0
  76. package/src/primitives/Pagination.vue +76 -0
  77. package/src/primitives/SearchMenu.vue +167 -0
  78. package/src/primitives/Toggle.vue +23 -0
  79. package/src/primitives/search-menu.ts +20 -0
  80. package/src/reasoning.ts +10 -0
  81. package/src/result-card/IconButton.vue +18 -0
  82. package/src/result-card/MoreMenu.vue +35 -0
  83. package/src/result-card/ResultMedia.vue +77 -0
  84. package/src/result-card/ResultText.vue +37 -0
  85. package/src/result-card/VideoPlayer.vue +90 -0
  86. package/src/result-card/types.ts +10 -0
  87. package/src/useComposer.ts +38 -0
  88. 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
- # Temporary Holding Version
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
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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"}