@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,147 @@
1
+ <script lang="ts">
2
+ // Skill list, import → full-text preview → confirm, view, edit, duplicate, reorder, delete (docs/16 §4.3).
3
+ import { Button } from '#lib/components/ui/button/index.js'
4
+ import * as Empty from '#lib/components/ui/empty/index.js'
5
+ import { getKitContext } from '../context'
6
+ import type { SkillDto, SkillMeta } from '../ports'
7
+ import { errorText } from './error'
8
+ import SkillEditor from './SkillEditor.svelte'
9
+ import SkillPreviewDialog from './SkillPreviewDialog.svelte'
10
+ import SkillAdd from './SkillAdd.svelte'
11
+
12
+ const { ports, i18n, notify } = getKitContext()
13
+ const { t } = i18n
14
+ const port = ports.skills
15
+
16
+ let skills = $state<SkillMeta[]>([])
17
+ let busy = $state(false)
18
+ let viewing = $state<SkillDto | null>(null)
19
+ let viewOpen = $state(false)
20
+ let editing = $state<SkillDto | null>(null)
21
+ let editOpen = $state(false)
22
+
23
+ async function act(run: () => Promise<void>) {
24
+ busy = true
25
+ try {
26
+ await run()
27
+ } catch (error) {
28
+ notify.toast('error', errorText(t, error))
29
+ } finally {
30
+ busy = false
31
+ }
32
+ }
33
+
34
+ if (port) void act(async () => void (skills = await port.list()))
35
+
36
+ function view(id: string) {
37
+ if (!port) return
38
+ void act(async () => {
39
+ viewing = await port.get(id)
40
+ viewOpen = true
41
+ })
42
+ }
43
+
44
+ function edit(id: string | null) {
45
+ if (!port) return
46
+ void act(async () => {
47
+ editing = id === null ? null : await port.get(id)
48
+ editOpen = true
49
+ })
50
+ }
51
+
52
+ function duplicate(skill: SkillMeta) {
53
+ if (!port) return
54
+ void act(async () => void (skills = await port.duplicate(skill.id, `${skill.name}-copy`)))
55
+ }
56
+
57
+ function move(index: number, by: number) {
58
+ if (!port) return
59
+ const ids = skills.map((s) => s.id)
60
+ const [id] = ids.splice(index, 1)
61
+ if (id === undefined) return
62
+ ids.splice(index + by, 0, id)
63
+ void act(async () => void (skills = await port.reorder(ids)))
64
+ }
65
+
66
+ async function remove(skill: SkillMeta) {
67
+ if (!port) return
68
+ const ok = await notify.confirm({
69
+ title: t('skills.delete'),
70
+ body: t('skills.delete_confirm', { name: skill.name }),
71
+ danger: true,
72
+ })
73
+ if (ok) void act(async () => void (skills = await port.remove(skill.id)))
74
+ }
75
+ </script>
76
+
77
+ {#if port}
78
+ <section class="flex flex-col gap-4" aria-label={t('skills.title')}>
79
+ <SkillAdd disabled={busy} oninstalled={(next) => (skills = next)} onmanual={() => edit(null)} />
80
+ {#if skills.length === 0}
81
+ <Empty.Root>
82
+ <Empty.Header>
83
+ <Empty.Title>{t('skills.empty')}</Empty.Title>
84
+ <Empty.Description>{t('skills.empty_hint')}</Empty.Description>
85
+ </Empty.Header>
86
+ </Empty.Root>
87
+ {:else}
88
+ <ul class="flex flex-col gap-2" aria-label={t('skills.list')}>
89
+ {#each skills as skill, index (skill.id)}
90
+ <li class="flex items-center gap-3 rounded-md border p-3">
91
+ <div class="flex min-w-0 flex-1 flex-col">
92
+ <span class="flex min-w-0 items-center gap-2">
93
+ <span class="truncate font-mono text-sm">{skill.name}</span>
94
+ {#if skill.hasScripts}
95
+ <span class="shrink-0 rounded border px-1.5 text-xs"
96
+ >{t('skills.has_scripts')}</span
97
+ >
98
+ {/if}
99
+ </span>
100
+ <span class="text-muted-foreground truncate text-xs">
101
+ {skill.version ?? t('skills.unversioned')} · {t(`skills.origin.${skill.origin}`)} ·
102
+ {t('skills.revision', { revision: skill.revision })} · {t('skills.chars', {
103
+ count: skill.bodyChars,
104
+ })}
105
+ {#if skill.fileCount > 0}· {t('skills.file_count', { count: skill.fileCount })}{/if}
106
+ </span>
107
+ </div>
108
+ <Button
109
+ size="sm"
110
+ variant="ghost"
111
+ disabled={busy || index === 0}
112
+ aria-label={t('skills.up', { name: skill.name })}
113
+ onclick={() => move(index, -1)}>↑</Button
114
+ >
115
+ <Button
116
+ size="sm"
117
+ variant="ghost"
118
+ disabled={busy || index === skills.length - 1}
119
+ aria-label={t('skills.down', { name: skill.name })}
120
+ onclick={() => move(index, 1)}>↓</Button
121
+ >
122
+ <Button size="sm" variant="outline" disabled={busy} onclick={() => view(skill.id)}
123
+ >{t('skills.view')}</Button
124
+ >
125
+ {#if skill.origin === 'builtin'}
126
+ <Button size="sm" variant="outline" disabled={busy} onclick={() => duplicate(skill)}
127
+ >{t('skills.duplicate')}</Button
128
+ >
129
+ {:else}
130
+ <Button size="sm" variant="outline" disabled={busy} onclick={() => edit(skill.id)}
131
+ >{t('skills.edit')}</Button
132
+ >
133
+ <Button
134
+ size="sm"
135
+ variant="destructive"
136
+ disabled={busy}
137
+ onclick={() => void remove(skill)}>{t('skills.delete')}</Button
138
+ >
139
+ {/if}
140
+ </li>
141
+ {/each}
142
+ </ul>
143
+ {/if}
144
+ </section>
145
+ <SkillPreviewDialog bind:open={viewOpen} skill={viewing} title={viewing?.name ?? ''} />
146
+ <SkillEditor bind:open={editOpen} {port} skill={editing} onsaved={(next) => (skills = next)} />
147
+ {/if}
@@ -0,0 +1,32 @@
1
+ // Error text for skill commands: the docs/14 reasons the user can act on, else the generic code text.
2
+ import { isKitClientError } from '../ports'
3
+ import type { KitI18n } from '../i18n'
4
+
5
+ export function errorText(t: KitI18n['t'], error: unknown): string {
6
+ if (!isKitClientError(error)) return t('errors.internal')
7
+ const { reason, field, limit } = error.details
8
+ switch (reason) {
9
+ case 'exists':
10
+ return t('skills.err.exists')
11
+ case 'builtin_name':
12
+ return t('skills.err.builtin_name')
13
+ case 'revision_changed':
14
+ return t('skills.err.revision_changed')
15
+ case 'secret_detected':
16
+ return t('skills.err.secret_detected', { field: field ?? '' })
17
+ case 'unsupported_files':
18
+ return t('skills.err.unsupported_files', { field: field ?? '' })
19
+ case 'too_large':
20
+ return t('skills.err.too_large', { field: field ?? '', limit: limit ?? '' })
21
+ case 'frontmatter_unsupported':
22
+ return t('skills.err.frontmatter', { field: field ?? '' })
23
+ case 'invalid_name':
24
+ return t('skills.err.invalid_name', { field: field ?? '' })
25
+ case 'too_deep':
26
+ return t('skills.err.too_deep', { field: field ?? '', limit: limit ?? '' })
27
+ case 'not_utf8':
28
+ return t('skills.err.not_utf8', { field: field ?? '' })
29
+ default:
30
+ return field ? `${t(`errors.${error.code}`)} (${field})` : t(`errors.${error.code}`)
31
+ }
32
+ }
@@ -0,0 +1,4 @@
1
+ // SkillsSettings, SkillPreviewDialog, SkillEditor (docs/16 §4.3). SkillPicker lives in ChatPanel.
2
+ export { default as SkillEditor } from './SkillEditor.svelte'
3
+ export { default as SkillPreviewDialog } from './SkillPreviewDialog.svelte'
4
+ export { default as SkillsSettings } from './SkillsSettings.svelte'
@@ -0,0 +1,139 @@
1
+ import { render, screen, fireEvent } from '@testing-library/svelte'
2
+ import { createI18n } from '../../i18n'
3
+ import {
4
+ createFakePorts,
5
+ type FakePortsOptions,
6
+ type Preview,
7
+ type RunEvent,
8
+ type SkillAuthoringPort,
9
+ type SkillDto,
10
+ type SkillMeta,
11
+ type Source,
12
+ } from '../../ports'
13
+ import { vi } from 'vitest'
14
+ import Harness from '../Harness.test.svelte'
15
+
16
+ export const meta = (
17
+ revision: number,
18
+ files: Pick<SkillMeta, 'hasScripts' | 'fileCount'> = { hasScripts: false, fileCount: 0 },
19
+ ): SkillMeta => ({
20
+ id: 's1',
21
+ name: 'writer',
22
+ description: 'Writes',
23
+ version: null,
24
+ origin: 'imported',
25
+ revision,
26
+ bodyChars: 4,
27
+ updatedAt: 0,
28
+ ...files,
29
+ })
30
+
31
+ export const preview = (body: string): Preview => ({
32
+ digest: `d-${body}`,
33
+ name: 'writer',
34
+ description: 'Writes',
35
+ version: '1.0.0',
36
+ body,
37
+ license: null,
38
+ extra: {},
39
+ files: [],
40
+ action: 'install',
41
+ current: null,
42
+ })
43
+
44
+ export function mount(options: FakePortsOptions, skills?: { id: string; revision: number }[]) {
45
+ const ports = createFakePorts({
46
+ providers: {
47
+ modelOptions: () =>
48
+ Promise.resolve([
49
+ {
50
+ endpointId: 'e1',
51
+ modelId: 'm1',
52
+ label: 'm1',
53
+ name: null,
54
+ modelType: 'chat',
55
+ supportsTools: null,
56
+ reasoning: null,
57
+ nativeWebSearch: false,
58
+ canVision: false,
59
+ },
60
+ ]),
61
+ },
62
+ ...options,
63
+ })
64
+ const notify = { toast: vi.fn(), confirm: () => Promise.resolve(true) }
65
+ const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }
66
+ const view = render(Harness, {
67
+ kit: { ports, i18n: createI18n('en'), notify, logger },
68
+ ...(skills ? { skills } : {}),
69
+ })
70
+ return { ...notify, unmount: view.unmount }
71
+ }
72
+
73
+ export async function addFrom(name: string) {
74
+ await fireEvent.click(screen.getByRole('button', { name: 'Add skill' }))
75
+ await fireEvent.click(await screen.findByRole('menuitem', { name }))
76
+ }
77
+
78
+ export async function selectAuthoringModel() {
79
+ await fireEvent.click(await screen.findByLabelText('Select a model'))
80
+ await fireEvent.click(await screen.findByRole('option', { name: /m1/ }))
81
+ }
82
+
83
+ export const creator: SkillDto = {
84
+ ...meta(1),
85
+ id: 'builtin-creator',
86
+ name: 'skill-creator',
87
+ origin: 'builtin',
88
+ body: 'Complete creator instructions: capture intent, draft, evaluate, improve, package.',
89
+ license: null,
90
+ extra: {},
91
+ files: [{ path: 'scripts/quick_validate.py', kind: 'script', size: 10 }],
92
+ }
93
+
94
+ export function authoring(overrides: Partial<SkillAuthoringPort> = {}): SkillAuthoringPort {
95
+ return {
96
+ create: vi.fn(async () => 'authoring-1'),
97
+ send: vi.fn<SkillAuthoringPort['send']>(async (_session, _input, _model, emit) => {
98
+ emit({ kind: 'end', status: 'completed' })
99
+ return { runId: 'authoring-run', conversationId: null, turnId: null }
100
+ }),
101
+ approve: vi.fn(async () => {}),
102
+ cancel: vi.fn(async () => {}),
103
+ prepare: vi.fn(async () => ({
104
+ source: { bytes: { fileName: 'writer.zip', bytes: [1] } },
105
+ origin: 'Authoring workspace',
106
+ })),
107
+ close: vi.fn(async () => {}),
108
+ ...overrides,
109
+ }
110
+ }
111
+
112
+ export function mountAuthoring(api: SkillAuthoringPort, options: FakePortsOptions = {}) {
113
+ return mount({
114
+ ...options,
115
+ skillSources: { ...options.skillSources, authoring: api },
116
+ skills: {
117
+ list: async () => [creator],
118
+ get: async () => creator,
119
+ ...options.skills,
120
+ },
121
+ })
122
+ }
123
+
124
+ export async function startAuthoring() {
125
+ await addFrom('Create with Agent')
126
+ await fireEvent.click(await screen.findByRole('button', { name: 'Review skill-creator' }))
127
+ await screen.findByText(creator.body)
128
+ await fireEvent.click(screen.getByRole('button', { name: 'Confirm skill-creator and start' }))
129
+ await screen.findByLabelText('Message to the skill creation assistant')
130
+ }
131
+
132
+ export async function authorMessage(text: string) {
133
+ await fireEvent.input(screen.getByLabelText('Message to the skill creation assistant'), {
134
+ target: { value: text },
135
+ })
136
+ await fireEvent.click(screen.getByRole('button', { name: /^Send$/ }))
137
+ }
138
+
139
+ export type { Preview, RunEvent, SkillAuthoringPort, Source }
@@ -0,0 +1,137 @@
1
+ @import 'tailwindcss';
2
+ @import 'tw-animate-css';
3
+ @import 'shadcn-svelte/tailwind.css';
4
+ @import './tokens.css';
5
+ /* Generated by shadcn-svelte init; font import removed (fonts belong to the host). Hosts without shadcn import this file (docs/16 §3). */
6
+ @source "../";
7
+
8
+ @custom-variant dark (&:is(.dark *, [data-theme="dark"] *));
9
+
10
+ :root {
11
+ --background: oklch(0.995 0.002 265);
12
+ --foreground: oklch(0.21 0.01 265);
13
+ --card: oklch(0.995 0.002 265);
14
+ --card-foreground: oklch(0.21 0.01 265);
15
+ --popover: oklch(0.995 0.002 265);
16
+ --popover-foreground: oklch(0.21 0.01 265);
17
+ --primary: oklch(0.25 0.012 265);
18
+ --primary-foreground: oklch(0.985 0.002 265);
19
+ --secondary: oklch(0.965 0.004 265);
20
+ --secondary-foreground: oklch(0.25 0.012 265);
21
+ --muted: oklch(0.965 0.004 265);
22
+ --muted-foreground: oklch(0.5 0.01 265);
23
+ --accent: oklch(0.965 0.004 265);
24
+ --accent-foreground: oklch(0.25 0.012 265);
25
+ --destructive: oklch(0.577 0.245 27.325);
26
+ --border: oklch(0.915 0.005 265);
27
+ --input: oklch(0.915 0.005 265);
28
+ --ring: oklch(0.7 0.01 265);
29
+ --chart-1: oklch(0.87 0.005 265);
30
+ --chart-2: oklch(0.556 0.01 265);
31
+ --chart-3: oklch(0.439 0.01 265);
32
+ --chart-4: oklch(0.371 0.01 265);
33
+ --chart-5: oklch(0.269 0.01 265);
34
+ --success: oklch(0.6 0.15 150);
35
+ --warning: oklch(0.7 0.15 75);
36
+ --info: oklch(0.58 0.14 250);
37
+ --radius: var(--kit-radius);
38
+ --sidebar: oklch(0.98 0.003 265);
39
+ --sidebar-foreground: oklch(0.21 0.01 265);
40
+ --sidebar-primary: oklch(0.25 0.012 265);
41
+ --sidebar-primary-foreground: oklch(0.985 0.002 265);
42
+ --sidebar-accent: oklch(0.965 0.004 265);
43
+ --sidebar-accent-foreground: oklch(0.25 0.012 265);
44
+ --sidebar-border: oklch(0.915 0.005 265);
45
+ --sidebar-ring: oklch(0.7 0.01 265);
46
+ }
47
+
48
+ .dark,
49
+ [data-theme='dark'] {
50
+ --background: oklch(0.17 0.008 265);
51
+ --foreground: oklch(0.93 0.004 265);
52
+ --card: oklch(0.215 0.01 265);
53
+ --card-foreground: oklch(0.93 0.004 265);
54
+ --popover: oklch(0.215 0.01 265);
55
+ --popover-foreground: oklch(0.93 0.004 265);
56
+ --primary: oklch(0.9 0.006 265);
57
+ --primary-foreground: oklch(0.21 0.01 265);
58
+ --secondary: oklch(0.27 0.011 265);
59
+ --secondary-foreground: oklch(0.93 0.004 265);
60
+ --muted: oklch(0.27 0.011 265);
61
+ --muted-foreground: oklch(0.7 0.008 265);
62
+ --accent: oklch(0.27 0.011 265);
63
+ --accent-foreground: oklch(0.93 0.004 265);
64
+ --destructive: oklch(0.704 0.191 22.216);
65
+ --border: oklch(1 0 0 / 10%);
66
+ --input: oklch(1 0 0 / 15%);
67
+ --ring: oklch(0.556 0.01 265);
68
+ --chart-1: oklch(0.87 0.005 265);
69
+ --chart-2: oklch(0.556 0.01 265);
70
+ --chart-3: oklch(0.439 0.01 265);
71
+ --chart-4: oklch(0.371 0.01 265);
72
+ --chart-5: oklch(0.269 0.01 265);
73
+ --sidebar: oklch(0.215 0.01 265);
74
+ --sidebar-foreground: oklch(0.93 0.004 265);
75
+ --sidebar-primary: oklch(0.488 0.243 264.376);
76
+ --sidebar-primary-foreground: oklch(0.985 0.002 265);
77
+ --sidebar-accent: oklch(0.27 0.011 265);
78
+ --sidebar-accent-foreground: oklch(0.93 0.004 265);
79
+ --sidebar-border: oklch(1 0 0 / 10%);
80
+ --sidebar-ring: oklch(0.556 0.01 265);
81
+ --success: oklch(0.72 0.15 150);
82
+ --warning: oklch(0.8 0.15 80);
83
+ --info: oklch(0.72 0.13 250);
84
+ }
85
+
86
+ @theme static inline {
87
+ --color-success: var(--success);
88
+ --color-warning: var(--warning);
89
+ --color-info: var(--info);
90
+ --color-sidebar-ring: var(--sidebar-ring);
91
+ --color-sidebar-border: var(--sidebar-border);
92
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
93
+ --color-sidebar-accent: var(--sidebar-accent);
94
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
95
+ --color-sidebar-primary: var(--sidebar-primary);
96
+ --color-sidebar-foreground: var(--sidebar-foreground);
97
+ --color-sidebar: var(--sidebar);
98
+ --color-chart-5: var(--chart-5);
99
+ --color-chart-4: var(--chart-4);
100
+ --color-chart-3: var(--chart-3);
101
+ --color-chart-2: var(--chart-2);
102
+ --color-chart-1: var(--chart-1);
103
+ --color-ring: var(--ring);
104
+ --color-input: var(--input);
105
+ --color-border: var(--border);
106
+ --color-destructive: var(--destructive);
107
+ --color-accent-foreground: var(--accent-foreground);
108
+ --color-accent: var(--accent);
109
+ --color-muted-foreground: var(--muted-foreground);
110
+ --color-muted: var(--muted);
111
+ --color-secondary-foreground: var(--secondary-foreground);
112
+ --color-secondary: var(--secondary);
113
+ --color-primary-foreground: var(--primary-foreground);
114
+ --color-primary: var(--primary);
115
+ --color-popover-foreground: var(--popover-foreground);
116
+ --color-popover: var(--popover);
117
+ --color-card-foreground: var(--card-foreground);
118
+ --color-card: var(--card);
119
+ --color-foreground: var(--foreground);
120
+ --color-background: var(--background);
121
+ --radius-sm: calc(var(--radius) * 0.6);
122
+ --radius-md: calc(var(--radius) * 0.8);
123
+ --radius-lg: var(--radius);
124
+ --radius-xl: calc(var(--radius) * 1.4);
125
+ --radius-2xl: calc(var(--radius) * 1.8);
126
+ --radius-3xl: calc(var(--radius) * 2.2);
127
+ --radius-4xl: calc(var(--radius) * 2.6);
128
+ }
129
+
130
+ @layer base {
131
+ * {
132
+ @apply border-border outline-ring/50;
133
+ }
134
+ body {
135
+ @apply bg-background text-foreground;
136
+ }
137
+ }
@@ -0,0 +1,22 @@
1
+ /* Size tokens and their Tailwind utilities (docs/16 §3). Hosts with their own Tailwind entry import this after tailwindcss and override the --kit-* values. */
2
+ :root {
3
+ --kit-text-xs: 11.5px;
4
+ --kit-text-sm: 12px;
5
+ --kit-text-base: 13px;
6
+ --kit-text-lg: 13.5px;
7
+ --kit-control-h: 30px;
8
+ --kit-control-h-sm: 28px;
9
+ --kit-radius: 8px;
10
+ --kit-radius-sm: 6px;
11
+ }
12
+
13
+ @theme inline {
14
+ --text-kit-xs: var(--kit-text-xs);
15
+ --text-kit-sm: var(--kit-text-sm);
16
+ --text-kit-base: var(--kit-text-base);
17
+ --text-kit-lg: var(--kit-text-lg);
18
+ --spacing-kit-control: var(--kit-control-h);
19
+ --spacing-kit-control-sm: var(--kit-control-h-sm);
20
+ --radius-kit: var(--kit-radius);
21
+ --radius-kit-sm: var(--kit-radius-sm);
22
+ }