@ouraihub/aether-ui 0.0.0-stage → 0.1.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.
Files changed (212) hide show
  1. package/LICENSE +789 -0
  2. package/fixtures/model-classify.json +42 -0
  3. package/fixtures/pricing-cost.json +56 -0
  4. package/fixtures/reasoning-infer.json +44 -0
  5. package/fixtures/sse/anthropic-messages/chinese.sse +24 -0
  6. package/fixtures/sse/anthropic-messages/multi-tool.sse +33 -0
  7. package/fixtures/sse/anthropic-messages/single-tool.sse +39 -0
  8. package/fixtures/sse/anthropic-messages/text.sse +27 -0
  9. package/fixtures/sse/anthropic-messages/truncated.sse +21 -0
  10. package/fixtures/sse/anthropic-messages/upstream-error.sse +12 -0
  11. package/fixtures/sse/expect.json +20 -0
  12. package/fixtures/sse/openai-chat/chinese.sse +14 -0
  13. package/fixtures/sse/openai-chat/multi-tool.sse +18 -0
  14. package/fixtures/sse/openai-chat/single-tool.sse +22 -0
  15. package/fixtures/sse/openai-chat/text.sse +14 -0
  16. package/fixtures/sse/openai-chat/truncated.sse +12 -0
  17. package/fixtures/sse/openai-chat/upstream-error.sse +6 -0
  18. package/fixtures/sse/openai-responses/chinese.sse +33 -0
  19. package/fixtures/sse/openai-responses/multi-tool.sse +36 -0
  20. package/fixtures/sse/openai-responses/single-tool.sse +27 -0
  21. package/fixtures/sse/openai-responses/text.sse +33 -0
  22. package/fixtures/sse/openai-responses/truncated.sse +30 -0
  23. package/fixtures/sse/openai-responses/upstream-error.sse +9 -0
  24. package/package.json +71 -3
  25. package/src/CopyInput.svelte +33 -0
  26. package/src/KitNotifier.svelte +33 -0
  27. package/src/chat/ApprovalCard.svelte +29 -0
  28. package/src/chat/ChatComposer.svelte +277 -0
  29. package/src/chat/ChatMessage.svelte +262 -0
  30. package/src/chat/ChatPanel.css +386 -0
  31. package/src/chat/ChatPanel.svelte +496 -0
  32. package/src/chat/ChatPanelApprovals.svelte +28 -0
  33. package/src/chat/ChatPanelComposer.svelte +317 -0
  34. package/src/chat/ChatPanelDialogs.svelte +242 -0
  35. package/src/chat/ChatPanelHeader.svelte +132 -0
  36. package/src/chat/ChatPanelMessages.svelte +166 -0
  37. package/src/chat/ChatPanelMode.svelte +190 -0
  38. package/src/chat/ChatPanelState.svelte.ts +1 -0
  39. package/src/chat/ChatPanelToolbar.svelte +425 -0
  40. package/src/chat/ChatSurface.svelte +168 -0
  41. package/src/chat/MessageItem.svelte +480 -0
  42. package/src/chat/ReasoningControl.svelte +286 -0
  43. package/src/chat/SkillPicker.svelte +133 -0
  44. package/src/chat/ToolCallItem.svelte +23 -0
  45. package/src/chat/UsageControl.svelte +216 -0
  46. package/src/chat/UsageDetails.svelte +76 -0
  47. package/src/chat/chat-attachments.svelte.ts +74 -0
  48. package/src/chat/chat-panel-history.svelte.ts +430 -0
  49. package/src/chat/chat-panel-props.ts +32 -0
  50. package/src/chat/index.ts +6 -0
  51. package/src/chat/run-session-history.ts +56 -0
  52. package/src/chat/run-session-types.ts +69 -0
  53. package/src/chat/run-session.svelte.ts +494 -0
  54. package/src/chat/test-support/chat-fixture.ts +178 -0
  55. package/src/chat/test-support/conversation-fixtures.ts +32 -0
  56. package/src/chat/test-support/run-session-fixture.ts +112 -0
  57. package/src/components/ui/alert/alert-action.svelte +20 -0
  58. package/src/components/ui/alert/alert-description.svelte +23 -0
  59. package/src/components/ui/alert/alert-title.svelte +23 -0
  60. package/src/components/ui/alert/alert.svelte +43 -0
  61. package/src/components/ui/alert/index.ts +17 -0
  62. package/src/components/ui/alert-dialog/alert-dialog-action.svelte +27 -0
  63. package/src/components/ui/alert-dialog/alert-dialog-cancel.svelte +27 -0
  64. package/src/components/ui/alert-dialog/alert-dialog-content.svelte +32 -0
  65. package/src/components/ui/alert-dialog/alert-dialog-description.svelte +17 -0
  66. package/src/components/ui/alert-dialog/alert-dialog-footer.svelte +23 -0
  67. package/src/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
  68. package/src/components/ui/alert-dialog/alert-dialog-media.svelte +20 -0
  69. package/src/components/ui/alert-dialog/alert-dialog-overlay.svelte +17 -0
  70. package/src/components/ui/alert-dialog/alert-dialog-portal.svelte +7 -0
  71. package/src/components/ui/alert-dialog/alert-dialog-title.svelte +17 -0
  72. package/src/components/ui/alert-dialog/alert-dialog-trigger.svelte +7 -0
  73. package/src/components/ui/alert-dialog/alert-dialog.svelte +7 -0
  74. package/src/components/ui/alert-dialog/index.ts +40 -0
  75. package/src/components/ui/button/button.svelte +81 -0
  76. package/src/components/ui/button/index.ts +17 -0
  77. package/src/components/ui/dialog/dialog-close.svelte +11 -0
  78. package/src/components/ui/dialog/dialog-content.svelte +48 -0
  79. package/src/components/ui/dialog/dialog-description.svelte +17 -0
  80. package/src/components/ui/dialog/dialog-footer.svelte +32 -0
  81. package/src/components/ui/dialog/dialog-header.svelte +20 -0
  82. package/src/components/ui/dialog/dialog-overlay.svelte +17 -0
  83. package/src/components/ui/dialog/dialog-portal.svelte +7 -0
  84. package/src/components/ui/dialog/dialog-title.svelte +17 -0
  85. package/src/components/ui/dialog/dialog-trigger.svelte +11 -0
  86. package/src/components/ui/dialog/dialog.svelte +7 -0
  87. package/src/components/ui/dialog/index.ts +34 -0
  88. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  89. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  90. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +31 -0
  91. package/src/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  92. package/src/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  93. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  94. package/src/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
  95. package/src/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  96. package/src/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +17 -0
  97. package/src/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  98. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  99. package/src/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  100. package/src/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  101. package/src/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  102. package/src/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  103. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  104. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  105. package/src/components/ui/dropdown-menu/index.ts +54 -0
  106. package/src/components/ui/empty/empty-content.svelte +23 -0
  107. package/src/components/ui/empty/empty-description.svelte +23 -0
  108. package/src/components/ui/empty/empty-header.svelte +20 -0
  109. package/src/components/ui/empty/empty-media.svelte +41 -0
  110. package/src/components/ui/empty/empty-title.svelte +20 -0
  111. package/src/components/ui/empty/empty.svelte +23 -0
  112. package/src/components/ui/empty/index.ts +22 -0
  113. package/src/components/ui/field/field-content.svelte +20 -0
  114. package/src/components/ui/field/field-description.svelte +25 -0
  115. package/src/components/ui/field/field-error.svelte +58 -0
  116. package/src/components/ui/field/field-group.svelte +23 -0
  117. package/src/components/ui/field/field-label.svelte +25 -0
  118. package/src/components/ui/field/field-legend.svelte +24 -0
  119. package/src/components/ui/field/field-separator.svelte +35 -0
  120. package/src/components/ui/field/field-set.svelte +20 -0
  121. package/src/components/ui/field/field-title.svelte +20 -0
  122. package/src/components/ui/field/field.svelte +47 -0
  123. package/src/components/ui/field/index.ts +33 -0
  124. package/src/components/ui/input/index.ts +7 -0
  125. package/src/components/ui/input/input.svelte +48 -0
  126. package/src/components/ui/label/index.ts +7 -0
  127. package/src/components/ui/label/label.svelte +20 -0
  128. package/src/components/ui/select/index.ts +40 -0
  129. package/src/components/ui/select/select-content.svelte +45 -0
  130. package/src/components/ui/select/select-group-heading.svelte +21 -0
  131. package/src/components/ui/select/select-group.svelte +17 -0
  132. package/src/components/ui/select/select-item.svelte +43 -0
  133. package/src/components/ui/select/select-label.svelte +20 -0
  134. package/src/components/ui/select/select-portal.svelte +7 -0
  135. package/src/components/ui/select/select-scroll-down-button.svelte +20 -0
  136. package/src/components/ui/select/select-scroll-up-button.svelte +20 -0
  137. package/src/components/ui/select/select-separator.svelte +18 -0
  138. package/src/components/ui/select/select-trigger.svelte +29 -0
  139. package/src/components/ui/select/select-value.svelte +7 -0
  140. package/src/components/ui/select/select.svelte +11 -0
  141. package/src/components/ui/separator/index.ts +7 -0
  142. package/src/components/ui/separator/separator.svelte +21 -0
  143. package/src/components/ui/sonner/index.ts +1 -0
  144. package/src/components/ui/sonner/sonner.svelte +40 -0
  145. package/src/components/ui/switch/index.ts +7 -0
  146. package/src/components/ui/switch/switch.svelte +34 -0
  147. package/src/components/ui/textarea/index.ts +7 -0
  148. package/src/components/ui/textarea/textarea.svelte +22 -0
  149. package/src/context.ts +34 -0
  150. package/src/dev/Demo.svelte +47 -0
  151. package/src/dev/main.ts +7 -0
  152. package/src/i18n/en.ts +645 -0
  153. package/src/i18n/index.ts +28 -0
  154. package/src/i18n/zh-CN.ts +610 -0
  155. package/src/index.ts +10 -0
  156. package/src/logs/LogPanel.svelte +103 -0
  157. package/src/logs/LogSettings.svelte +58 -0
  158. package/src/logs/index.ts +5 -0
  159. package/src/logs/logger.ts +96 -0
  160. package/src/markdown.ts +8 -0
  161. package/src/mcp/McpClientEditor.svelte +204 -0
  162. package/src/mcp/McpClientSettings.svelte +418 -0
  163. package/src/mcp/McpServerSettings.svelte +363 -0
  164. package/src/mcp/config.ts +26 -0
  165. package/src/mcp/index.ts +3 -0
  166. package/src/mcp/input.ts +74 -0
  167. package/src/notify.svelte.ts +43 -0
  168. package/src/ports/contract.ts +174 -0
  169. package/src/ports/fake-optional.ts +81 -0
  170. package/src/ports/fake.ts +488 -0
  171. package/src/ports/index.ts +7 -0
  172. package/src/ports/search.ts +56 -0
  173. package/src/ports/skill-sources.ts +45 -0
  174. package/src/ports/test-support/fake-fixture.ts +63 -0
  175. package/src/providers/EndpointKeysDialog.svelte +357 -0
  176. package/src/providers/EndpointModels.svelte +311 -0
  177. package/src/providers/EndpointPanel.svelte +266 -0
  178. package/src/providers/ModelPickerDialog.svelte +230 -0
  179. package/src/providers/ModelPricing.svelte +125 -0
  180. package/src/providers/ModelSelect.svelte +448 -0
  181. package/src/providers/ModelSettingsDialog.svelte +388 -0
  182. package/src/providers/ProviderDialogs.svelte +120 -0
  183. package/src/providers/ProviderList.svelte +159 -0
  184. package/src/providers/ProvidersSettings.svelte +491 -0
  185. package/src/providers/RequestConfigDialog.svelte +278 -0
  186. package/src/providers/SecretInput.svelte +284 -0
  187. package/src/providers/draft.ts +355 -0
  188. package/src/providers/edit-session.ts +170 -0
  189. package/src/providers/index.ts +5 -0
  190. package/src/providers/reasoning.ts +55 -0
  191. package/src/providers/revision.svelte.ts +6 -0
  192. package/src/providers/test-support/providers-fixture.ts +120 -0
  193. package/src/search/SearchAdvancedTools.svelte +182 -0
  194. package/src/search/SearchProviderCard.svelte +195 -0
  195. package/src/search/SearchSettings.svelte +500 -0
  196. package/src/search/index.ts +13 -0
  197. package/src/search/settings-flow.ts +67 -0
  198. package/src/session.ts +20 -0
  199. package/src/skills/SkillAdd.svelte +380 -0
  200. package/src/skills/SkillAuthoring.svelte +430 -0
  201. package/src/skills/SkillEditor.svelte +104 -0
  202. package/src/skills/SkillPreviewDialog.svelte +136 -0
  203. package/src/skills/SkillsSettings.svelte +147 -0
  204. package/src/skills/error.ts +32 -0
  205. package/src/skills/index.ts +4 -0
  206. package/src/skills/test-support/skills-fixture.ts +139 -0
  207. package/src/styles/kit.css +137 -0
  208. package/src/styles/tokens.css +22 -0
  209. package/src/tauri/index.ts +298 -0
  210. package/src/utils.ts +22 -0
  211. package/src/web/index.ts +441 -0
  212. package/README.md +0 -3
@@ -0,0 +1,178 @@
1
+ import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'
2
+ import type { ComponentProps } from 'svelte'
3
+ import { expect, vi } from 'vitest'
4
+ import { createI18n } from '../../i18n'
5
+ import {
6
+ createFakePorts,
7
+ KitClientError,
8
+ type ConversationClient,
9
+ type Conversation,
10
+ type RunClient,
11
+ type RunEvent,
12
+ type RunRequest,
13
+ type ProvidersPort,
14
+ type SearchPort,
15
+ type SkillsPort,
16
+ } from '../../ports'
17
+ import type ChatPanel from '../ChatPanel.svelte'
18
+ import Harness from '../Harness.test.svelte'
19
+
20
+ export const model = { endpointId: 'e1', modelId: 'm1' }
21
+
22
+ export function required<T>(value: T | null | undefined): T {
23
+ if (value == null) throw new Error('Expected chat fixture or control is missing')
24
+ return value
25
+ }
26
+
27
+ export interface Run {
28
+ request: RunRequest
29
+ turnId: string
30
+ emit: (event: RunEvent) => void
31
+ signal: AbortSignal | undefined
32
+ }
33
+
34
+ export interface Extra {
35
+ props?: Partial<ComponentProps<typeof ChatPanel>>
36
+ history?: Partial<ConversationClient>
37
+ providers?: Partial<ProvidersPort>
38
+ search?: Partial<SearchPort>
39
+ skills?: Partial<SkillsPort>
40
+ chat?: Partial<RunClient>
41
+ }
42
+
43
+ export function setup(
44
+ withHistory: boolean,
45
+ context: { kind: string; id: string; label: string } | null = null,
46
+ extra: Extra = {},
47
+ ) {
48
+ const runs: Run[] = []
49
+ const stored: Conversation = {
50
+ summary: {
51
+ id: 'c1',
52
+ title: 'Test',
53
+ scope: null,
54
+ selectedTurnId: null,
55
+ createdAt: 0,
56
+ updatedAt: 0,
57
+ },
58
+ turns: [],
59
+ }
60
+ const approve = vi.fn<(runId: string, callId: string, allow: boolean) => Promise<void>>(() =>
61
+ Promise.resolve(),
62
+ )
63
+ const ports = createFakePorts({
64
+ providers: {
65
+ modelOptions: () =>
66
+ Promise.resolve([
67
+ {
68
+ ...model,
69
+ label: 'Local · main · m1',
70
+ modelType: 'chat',
71
+ name: null,
72
+ supportsTools: null,
73
+ reasoning: null,
74
+ nativeWebSearch: false,
75
+ canVision: false,
76
+ },
77
+ ]),
78
+ ...extra.providers,
79
+ },
80
+ ...(extra.search ? { search: extra.search } : {}),
81
+ ...(extra.skills ? { skills: extra.skills } : {}),
82
+ chat: {
83
+ start: (request, emit, options) => {
84
+ const id = crypto.randomUUID()
85
+ const source = stored.turns.find(
86
+ (turn) => turn.id === (request.retryTurnId ?? request.translateTurnId),
87
+ )
88
+ const turn: Conversation['turns'][number] = {
89
+ id,
90
+ parentId: request.retryTurnId
91
+ ? (source?.parentId ?? null)
92
+ : (request.translateTurnId ?? request.replyToId ?? stored.summary.selectedTurnId),
93
+ questionId: request.retryTurnId ? (source?.questionId ?? id) : id,
94
+ supersedesId: request.retryTurnId ?? null,
95
+ kind: request.translateTurnId ? 'translation' : 'reply',
96
+ input: source?.input ?? request.input,
97
+ images: source
98
+ ? source.images
99
+ : (request.images ?? []).map((image) => ({
100
+ mime: image.mime,
101
+ url: `data:${image.mime};base64,${image.data}`,
102
+ })),
103
+ generated: [],
104
+ text: '',
105
+ status: 'running',
106
+ errorCode: null,
107
+ endpointId: request.model?.endpointId ?? null,
108
+ modelId: request.model?.modelId ?? null,
109
+ reasoningEffort: request.reasoningEffort ?? 'default',
110
+ reasoningText: null,
111
+ }
112
+ stored.turns.push(turn)
113
+ const wrapped = (event: RunEvent) => {
114
+ if (event.kind === 'text_delta') turn.text += event.text
115
+ if (event.kind === 'reasoning_delta')
116
+ turn.reasoningText = (turn.reasoningText ?? '') + event.text
117
+ if (event.kind === 'usage') {
118
+ turn.usage = {
119
+ inputTokens: event.inputTokens,
120
+ outputTokens: event.outputTokens,
121
+ cacheReadTokens: event.cacheReadTokens,
122
+ cacheWriteTokens: event.cacheWriteTokens,
123
+ reasoningTokens: event.reasoningTokens,
124
+ }
125
+ turn.cost = event.cost
126
+ }
127
+ if (event.kind === 'context') turn.contextUsage = event.usage
128
+ if (event.kind === 'end') {
129
+ turn.status = event.status
130
+ turn.errorCode = event.error?.code ?? null
131
+ if (event.status === 'completed' && !request.retryTurnId)
132
+ stored.summary.selectedTurnId = id
133
+ }
134
+ emit(event)
135
+ }
136
+ runs.push({ request, turnId: id, emit: wrapped, signal: options?.signal })
137
+ options?.signal?.addEventListener('abort', () =>
138
+ wrapped({ kind: 'end', status: 'cancelled' }),
139
+ )
140
+ return Promise.resolve({
141
+ runId: `r${runs.length}`,
142
+ conversationId: withHistory ? (request.conversationId ?? 'c1') : null,
143
+ turnId: withHistory ? id : null,
144
+ })
145
+ },
146
+ approve,
147
+ ...extra.chat,
148
+ },
149
+ })
150
+ if (ports.conversations)
151
+ Object.assign(
152
+ ports.conversations,
153
+ {
154
+ get: (id: string) =>
155
+ id === stored.summary.id
156
+ ? Promise.resolve(structuredClone(stored))
157
+ : Promise.reject(
158
+ new KitClientError({ code: 'not_found', message: 'conversation not found' }),
159
+ ),
160
+ },
161
+ extra.history,
162
+ )
163
+ if (!withHistory) delete ports.conversations
164
+ const notify = { toast: vi.fn(), confirm: vi.fn(() => Promise.resolve(true)) }
165
+ const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }
166
+ const kit = { ports, i18n: createI18n('en'), notify, logger }
167
+ const view = render(Harness, { props: { kit, model, context, ...extra.props } })
168
+ return { runs, view, approve, notify }
169
+ }
170
+
171
+ export async function send(text: string) {
172
+ await fireEvent.input(screen.getByRole('textbox', { name: 'Message' }), {
173
+ target: { value: text },
174
+ })
175
+ const button = screen.getByRole('button', { name: 'Send' })
176
+ await waitFor(() => expect(button.hasAttribute('disabled')).toBe(false))
177
+ await fireEvent.click(button)
178
+ }
@@ -0,0 +1,32 @@
1
+ import type { Conversation } from '../../ports'
2
+ import { model } from './chat-fixture'
3
+
4
+ export function candidates(): Conversation {
5
+ const reply = (id: string, text: string): Conversation['turns'][number] => ({
6
+ id,
7
+ parentId: null,
8
+ questionId: 'q',
9
+ supersedesId: null,
10
+ kind: 'reply',
11
+ ...model,
12
+ input: 'Shared question',
13
+ text,
14
+ status: 'completed',
15
+ errorCode: null,
16
+ reasoningEffort: 'default',
17
+ reasoningText: null,
18
+ images: [],
19
+ generated: [],
20
+ })
21
+ return {
22
+ summary: {
23
+ id: 'saved',
24
+ title: 'Candidates',
25
+ scope: null,
26
+ selectedTurnId: 'a',
27
+ createdAt: 0,
28
+ updatedAt: 0,
29
+ },
30
+ turns: [reply('a', 'Original **reply**'), reply('b', 'Alternative reply')],
31
+ }
32
+ }
@@ -0,0 +1,112 @@
1
+ import { vi } from 'vitest'
2
+ import { createFakePorts } from '../../ports'
3
+ import type {
4
+ ConversationClient,
5
+ Conversation,
6
+ RunAccepted,
7
+ RunEvent,
8
+ RunRequest,
9
+ Turn,
10
+ } from '../../ports'
11
+ import { RunSession, type RunBase } from '../run-session.svelte'
12
+
13
+ export const sourceModel = { endpointId: 'original-endpoint', modelId: 'original-model' }
14
+ export const pickedModel = { endpointId: 'picked-endpoint', modelId: 'picked-model' }
15
+ export const base: RunBase = {
16
+ purpose: 'chat',
17
+ scope: null,
18
+ language: 'en',
19
+ model: pickedModel,
20
+ skills: [],
21
+ context: null,
22
+ }
23
+
24
+ export function required<T>(value: T | null | undefined): T {
25
+ if (value == null) throw new Error('Expected run-session fixture is missing')
26
+ return value
27
+ }
28
+
29
+ export function stored(overrides: Partial<Turn> = {}): Turn {
30
+ return {
31
+ id: 'source',
32
+ parentId: null,
33
+ questionId: 'question',
34
+ supersedesId: null,
35
+ kind: 'reply',
36
+ endpointId: sourceModel.endpointId,
37
+ modelId: sourceModel.modelId,
38
+ input: 'Original question',
39
+ text: 'Original **answer**',
40
+ status: 'completed',
41
+ errorCode: null,
42
+ reasoningText: 'Original reasoning',
43
+ reasoningEffort: 'default',
44
+ images: [],
45
+ generated: [],
46
+ ...overrides,
47
+ }
48
+ }
49
+
50
+ export function conversation(
51
+ turns: Turn[],
52
+ selectedTurnId: string | null = 'source',
53
+ ): Conversation {
54
+ return {
55
+ summary: {
56
+ id: 'conversation',
57
+ title: 'Conversation',
58
+ scope: null,
59
+ createdAt: 0,
60
+ updatedAt: 0,
61
+ selectedTurnId,
62
+ },
63
+ turns,
64
+ }
65
+ }
66
+
67
+ export interface Deferred<T> {
68
+ promise: Promise<T>
69
+ resolve: (value: T) => void
70
+ reject: (error: unknown) => void
71
+ }
72
+ export function deferred<T>(): Deferred<T> {
73
+ let resolve!: (value: T) => void
74
+ let reject!: (error: unknown) => void
75
+ const promise = new Promise<T>((yes, no) => {
76
+ resolve = yes
77
+ reject = no
78
+ })
79
+ return { promise, resolve, reject }
80
+ }
81
+
82
+ export function setup(withHistory = true) {
83
+ const runs: {
84
+ request: RunRequest
85
+ emit: (event: RunEvent) => void
86
+ accepted: Deferred<RunAccepted>
87
+ }[] = []
88
+ const get = vi.fn<ConversationClient['get']>(() => Promise.resolve(conversation([stored()])))
89
+ const cancel = vi.fn(() => Promise.resolve())
90
+ const approve = vi.fn(() => Promise.resolve())
91
+ const ports = createFakePorts({
92
+ chat: {
93
+ cancel,
94
+ approve,
95
+ start: (request, emit) => {
96
+ const accepted = deferred<RunAccepted>()
97
+ runs.push({ request, emit, accepted })
98
+ return accepted.promise
99
+ },
100
+ },
101
+ })
102
+ const chat = required(ports.chat)
103
+ if (withHistory) Object.assign(required(ports.conversations), { get })
104
+ else delete ports.conversations
105
+ return { session: new RunSession(chat, ports.conversations), runs, get, cancel, approve }
106
+ }
107
+
108
+ export const accepted: RunAccepted = {
109
+ runId: 'run',
110
+ conversationId: 'conversation',
111
+ turnId: 'candidate',
112
+ }
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-action"
16
+ class={cn("absolute top-2 right-2", className)}
17
+ {...restProps}
18
+ >
19
+ {@render children?.()}
20
+ </div>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-description"
16
+ class={cn(
17
+ "text-muted-foreground text-kit-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-title"
16
+ class={cn(
17
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>
@@ -0,0 +1,43 @@
1
+ <script lang="ts" module>
2
+ import { type VariantProps, tv } from "#lib/utils.js";
3
+
4
+ export const alertVariants = tv({
5
+ base: "grid gap-0.5 rounded-kit border px-2.5 py-2 text-left text-kit-base has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full",
6
+ variants: {
7
+ variant: {
8
+ default: "bg-card text-card-foreground",
9
+ destructive: "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
10
+ },
11
+ },
12
+ defaultVariants: {
13
+ variant: "default",
14
+ },
15
+ });
16
+
17
+ export type AlertVariant = VariantProps<typeof alertVariants>["variant"];
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ import { cn, type WithElementRef } from "#lib/utils.js";
22
+ import type { HTMLAttributes } from "svelte/elements";
23
+
24
+ let {
25
+ ref = $bindable(null),
26
+ class: className,
27
+ variant = "default",
28
+ children,
29
+ ...restProps
30
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
31
+ variant?: AlertVariant;
32
+ } = $props();
33
+ </script>
34
+
35
+ <div
36
+ bind:this={ref}
37
+ data-slot="alert"
38
+ role="alert"
39
+ class={cn(alertVariants({ variant }), className)}
40
+ {...restProps}
41
+ >
42
+ {@render children?.()}
43
+ </div>
@@ -0,0 +1,17 @@
1
+ import Action from "./alert-action.svelte";
2
+ import Description from "./alert-description.svelte";
3
+ import Title from "./alert-title.svelte";
4
+ import Root from "./alert.svelte";
5
+ export { alertVariants, type AlertVariant } from "./alert.svelte";
6
+
7
+ export {
8
+ Root,
9
+ Description,
10
+ Title,
11
+ Action,
12
+ //
13
+ Root as Alert,
14
+ Description as AlertDescription,
15
+ Title as AlertTitle,
16
+ Action as AlertAction,
17
+ };
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import {
4
+ buttonVariants,
5
+ type ButtonVariant,
6
+ type ButtonSize,
7
+ } from "#lib/components/ui/button/index.js";
8
+ import { cn } from "#lib/utils.js";
9
+
10
+ let {
11
+ ref = $bindable(null),
12
+ class: className,
13
+ variant = "default",
14
+ size = "default",
15
+ ...restProps
16
+ }: AlertDialogPrimitive.ActionProps & {
17
+ variant?: ButtonVariant;
18
+ size?: ButtonSize;
19
+ } = $props();
20
+ </script>
21
+
22
+ <AlertDialogPrimitive.Action
23
+ bind:ref
24
+ data-slot="alert-dialog-action"
25
+ class={cn(buttonVariants({ variant, size }), "", className)}
26
+ {...restProps}
27
+ />
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import {
4
+ buttonVariants,
5
+ type ButtonVariant,
6
+ type ButtonSize,
7
+ } from "#lib/components/ui/button/index.js";
8
+ import { cn } from "#lib/utils.js";
9
+
10
+ let {
11
+ ref = $bindable(null),
12
+ class: className,
13
+ variant = "outline",
14
+ size = "default",
15
+ ...restProps
16
+ }: AlertDialogPrimitive.CancelProps & {
17
+ variant?: ButtonVariant;
18
+ size?: ButtonSize;
19
+ } = $props();
20
+ </script>
21
+
22
+ <AlertDialogPrimitive.Cancel
23
+ bind:ref
24
+ data-slot="alert-dialog-cancel"
25
+ class={cn(buttonVariants({ variant, size }), "", className)}
26
+ {...restProps}
27
+ />
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import { cn, type WithoutChild, type WithoutChildrenOrChild } from "#lib/utils.js";
4
+ import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
5
+ import AlertDialogPortal from "./alert-dialog-portal.svelte";
6
+ import type { ComponentProps } from "svelte";
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ size = "default",
12
+ portalProps,
13
+ ...restProps
14
+ }: WithoutChild<AlertDialogPrimitive.ContentProps> & {
15
+ size?: "default" | "sm";
16
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
17
+ } = $props();
18
+ </script>
19
+
20
+ <AlertDialogPortal {...portalProps}>
21
+ <AlertDialogOverlay />
22
+ <AlertDialogPrimitive.Content
23
+ bind:ref
24
+ data-slot="alert-dialog-content"
25
+ data-size={size}
26
+ class={cn(
27
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/10 gap-4 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
28
+ className
29
+ )}
30
+ {...restProps}
31
+ />
32
+ </AlertDialogPortal>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import { cn } from "#lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: AlertDialogPrimitive.DescriptionProps = $props();
10
+ </script>
11
+
12
+ <AlertDialogPrimitive.Description
13
+ bind:ref
14
+ data-slot="alert-dialog-description"
15
+ class={cn("text-muted-foreground *:[a]:hover:text-foreground text-kit-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)}
16
+ {...restProps}
17
+ />
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-dialog-footer"
16
+ class={cn(
17
+ "bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-dialog-header"
16
+ class={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
17
+ {...restProps}
18
+ >
19
+ {@render children?.()}
20
+ </div>
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { cn, type WithElementRef } from "#lib/utils.js";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="alert-dialog-media"
16
+ class={cn("bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-kit sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className)}
17
+ {...restProps}
18
+ >
19
+ {@render children?.()}
20
+ </div>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import { cn } from "#lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: AlertDialogPrimitive.OverlayProps = $props();
10
+ </script>
11
+
12
+ <AlertDialogPrimitive.Overlay
13
+ bind:ref
14
+ data-slot="alert-dialog-overlay"
15
+ class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className)}
16
+ {...restProps}
17
+ />
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+
4
+ let { ...restProps }: AlertDialogPrimitive.PortalProps = $props();
5
+ </script>
6
+
7
+ <AlertDialogPrimitive.Portal {...restProps} />
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+ import { cn } from "#lib/utils.js";
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: AlertDialogPrimitive.TitleProps = $props();
10
+ </script>
11
+
12
+ <AlertDialogPrimitive.Title
13
+ bind:ref
14
+ data-slot="alert-dialog-title"
15
+ class={cn("text-kit-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
16
+ {...restProps}
17
+ />
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
3
+
4
+ let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
5
+ </script>
6
+
7
+ <AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />