@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,166 @@
1
+ <script lang="ts">
2
+ import { Button } from '#lib/components/ui/button/index.js'
3
+ import * as Empty from '#lib/components/ui/empty/index.js'
4
+ import * as Alert from '#lib/components/ui/alert/index.js'
5
+ import type { ConversationClient, ImageRef, ModelOption, ModelRef } from '../ports'
6
+ import type { ChatTurn, RunSession } from './run-session.svelte'
7
+ import MessageItem from './MessageItem.svelte'
8
+ import type { KitI18n } from '../i18n'
9
+
10
+ let {
11
+ i18n,
12
+ session,
13
+ groups,
14
+ purpose,
15
+ history,
16
+ restoreBlocked,
17
+ actionBusy,
18
+ modelOptions,
19
+ modelError,
20
+ capability,
21
+ noModels,
22
+ onRetryModels,
23
+ onOpenSettings,
24
+ onInsert,
25
+ onSaveNote,
26
+ onExportMarkdown,
27
+ sourceLabel,
28
+ sourceModel,
29
+ canEdit,
30
+ onRetry,
31
+ onEdit,
32
+ onBranch,
33
+ onCopyConversation,
34
+ onSwitchModel,
35
+ onTranslate,
36
+ onDelete,
37
+ onUseContext,
38
+ }: {
39
+ i18n: KitI18n
40
+ session: RunSession
41
+ groups: [string, ChatTurn[]][]
42
+ purpose: string
43
+ history: ConversationClient | undefined
44
+ restoreBlocked: boolean
45
+ actionBusy: boolean
46
+ modelOptions: ModelOption[] | null
47
+ modelError: string | null
48
+ noModels: boolean
49
+ capability: ModelOption | null
50
+ onRetryModels: () => void
51
+ onOpenSettings?: (() => void) | undefined
52
+ onInsert?: ((markdown: string) => void) | undefined
53
+ onSaveNote?: ((markdown: string, images: ImageRef[]) => Promise<void> | void) | undefined
54
+ onExportMarkdown?: ((markdown: string) => Promise<void> | void) | undefined
55
+ sourceLabel: (turn: ChatTurn) => string
56
+ sourceModel: (turn: ChatTurn) => ModelRef | null
57
+ canEdit: (turn: ChatTurn) => boolean
58
+ onRetry?: (turn: ChatTurn) => void
59
+ onEdit?: (turn: ChatTurn) => void
60
+ onBranch?: (turn: ChatTurn) => void
61
+ onCopyConversation?: (turn: ChatTurn) => void
62
+ onSwitchModel?: (turn: ChatTurn, model: ModelRef) => void
63
+ onTranslate?: (turn: ChatTurn) => void
64
+ onDelete?: (turn: ChatTurn) => void
65
+ onUseContext?: (turn: ChatTurn) => void
66
+ } = $props()
67
+ const t = $derived(i18n.t)
68
+ </script>
69
+
70
+ {#if session.turns.length > 0}
71
+ <ol class="turns">
72
+ {#each groups as [questionId, replies] (questionId)}
73
+ {@const candidates = replies.filter((reply) => reply.kind === 'reply').length > 1}
74
+ {#each replies as turn, i (turn.id)}
75
+ <MessageItem
76
+ {turn}
77
+ {onInsert}
78
+ {onSaveNote}
79
+ {onExportMarkdown}
80
+ latest={turn.id === session.turns.at(-1)?.id}
81
+ modelLabel={turn.kind === 'translation'
82
+ ? `${t('chat.translationReply')} · ${sourceLabel(turn)}`
83
+ : sourceLabel(turn)}
84
+ showInput={i === 0 && turn.kind !== 'translation'}
85
+ chosen={candidates && session.selectedTurnId === turn.id}
86
+ busy={actionBusy}
87
+ editable={canEdit(turn)}
88
+ onRetry={purpose === 'chat' && (turn.persisted || capability !== null)
89
+ ? () => onRetry?.(turn)
90
+ : undefined}
91
+ onEdit={purpose === 'chat' && history && session.conversationId && turn.persisted
92
+ ? () => onEdit?.(turn)
93
+ : undefined}
94
+ onBranch={purpose === 'chat' && history && session.conversationId && turn.persisted
95
+ ? () => onBranch?.(turn)
96
+ : undefined}
97
+ onCopyConversation={purpose === 'chat' &&
98
+ history &&
99
+ session.conversationId &&
100
+ turn.persisted
101
+ ? () => onCopyConversation?.(turn)
102
+ : undefined}
103
+ onSwitchModel={purpose === 'chat' && history && session.conversationId && turn.persisted
104
+ ? (picked: ModelRef) => onSwitchModel?.(turn, picked)
105
+ : undefined}
106
+ modelOptions={turn.images.length > 0
107
+ ? (modelOptions?.filter((option) => option.canVision) ?? null)
108
+ : modelOptions}
109
+ sourceModel={sourceModel(turn)}
110
+ onTranslate={purpose === 'chat' && history && session.conversationId && turn.persisted
111
+ ? () => onTranslate?.(turn)
112
+ : undefined}
113
+ onDelete={purpose === 'chat' && history && session.conversationId && turn.persisted
114
+ ? () => onDelete?.(turn)
115
+ : undefined}
116
+ onUseContext={candidates &&
117
+ purpose === 'chat' &&
118
+ history &&
119
+ session.conversationId &&
120
+ turn.persisted
121
+ ? () => onUseContext?.(turn)
122
+ : undefined}
123
+ />
124
+ {/each}
125
+ {/each}
126
+ </ol>
127
+ {/if}
128
+ {#if modelError}
129
+ <Alert.Root variant="destructive">
130
+ <Alert.Title>{t('chat.modelsFailed')}</Alert.Title>
131
+ <Alert.Description
132
+ >{modelError}<Button variant="outline" size="sm" onclick={onRetryModels}
133
+ >{t('chat.retryModels')}</Button
134
+ ></Alert.Description
135
+ >
136
+ </Alert.Root>
137
+ {:else if modelOptions === null}
138
+ <p class="empty-message" role="status">{t('chat.modelsLoading')}</p>
139
+ {:else if noModels}
140
+ <Empty.Root
141
+ ><Empty.Description>{t('chat.noModels')}</Empty.Description>{#if onOpenSettings}<Button
142
+ variant="outline"
143
+ size="sm"
144
+ onclick={onOpenSettings}>{t('chat.openSettings')}</Button
145
+ >{/if}</Empty.Root
146
+ >
147
+ {:else if session.turns.length === 0 && !restoreBlocked}
148
+ <p class="empty-message">{t('chat.empty')}</p>
149
+ {/if}
150
+
151
+ <style>
152
+ .turns {
153
+ display: flex;
154
+ flex-direction: column;
155
+ gap: 1rem;
156
+ padding: 0;
157
+ margin: 0;
158
+ list-style: none;
159
+ }
160
+ .empty-message {
161
+ color: var(--color-muted-foreground);
162
+ text-align: center;
163
+ padding: 1.5rem 0.5rem;
164
+ margin: 0;
165
+ }
166
+ </style>
@@ -0,0 +1,190 @@
1
+ <script lang="ts">
2
+ import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'
3
+ import CheckIcon from '@lucide/svelte/icons/check'
4
+ import ShieldIcon from '@lucide/svelte/icons/shield'
5
+ import HandIcon from '@lucide/svelte/icons/hand'
6
+ import RouteIcon from '@lucide/svelte/icons/route'
7
+ import FilePenIcon from '@lucide/svelte/icons/file-pen'
8
+ import ShieldCheckIcon from '@lucide/svelte/icons/shield-check'
9
+ import ShieldAlertIcon from '@lucide/svelte/icons/shield-alert'
10
+ import InfoIcon from '@lucide/svelte/icons/info'
11
+ import * as DropdownMenu from '#lib/components/ui/dropdown-menu/index.js'
12
+ import { Button } from '#lib/components/ui/button/index.js'
13
+ import type { KitI18n } from '../i18n'
14
+ import type { PermissionMode } from '../ports'
15
+ let {
16
+ i18n,
17
+ mode,
18
+ allowedModes,
19
+ running,
20
+ chooseMode,
21
+ }: {
22
+ i18n: KitI18n
23
+ mode: PermissionMode
24
+ allowedModes: PermissionMode[]
25
+ running: boolean
26
+ chooseMode: (mode: PermissionMode) => Promise<void>
27
+ } = $props()
28
+ const t = $derived(i18n.t)
29
+ </script>
30
+
31
+ {#if mode === 'plan'}<span class="plan-badge" role="status">{t('chat.mode.planBadge')}</span>{/if}
32
+ {#if allowedModes.length > 1 || allowedModes.includes('full_access')}
33
+ <DropdownMenu.Root>
34
+ <DropdownMenu.Trigger disabled={running}>
35
+ {#snippet child({ props })}<Button
36
+ {...props}
37
+ variant="ghost"
38
+ size="sm"
39
+ class={mode === 'full_access' ? 'mode-danger' : undefined}
40
+ aria-label={`${t('chat.mode.label')}: ${t(`chat.mode.${mode}`)}`}
41
+ ><ShieldIcon data-icon="inline-start" />{t(`chat.mode.${mode}`)}<ChevronDownIcon
42
+ data-icon="inline-end"
43
+ /></Button
44
+ >{/snippet}
45
+ </DropdownMenu.Trigger>
46
+ <DropdownMenu.Content side="top" align="end" class="mode-menu">
47
+ <DropdownMenu.Group aria-label={t('chat.mode.label')}>
48
+ {#each allowedModes as option (option)}
49
+ <DropdownMenu.Item
50
+ class={option === 'full_access' ? 'mode-option mode-danger' : 'mode-option'}
51
+ aria-current={option === mode ? 'true' : undefined}
52
+ onSelect={() => void chooseMode(option)}
53
+ >{#if option === 'default'}<HandIcon />
54
+ {:else if option === 'plan'}<RouteIcon />
55
+ {:else if option === 'accept_edits'}<FilePenIcon />
56
+ {:else if option === 'auto'}<ShieldCheckIcon />
57
+ {:else}<ShieldAlertIcon />{/if}
58
+ <span class="mode-name">{t(`chat.mode.${option}`)}</span>
59
+ <span class="mode-description">{t(`chat.mode.${option}Hint`)}</span>
60
+ <span class="mode-notice">
61
+ {#if option === 'auto' || option === 'full_access'}
62
+ <span
63
+ title={t(option === 'auto' ? 'chat.mode.ruleNotice' : 'chat.mode.fullAccessBody')}
64
+ >
65
+ <InfoIcon />
66
+ </span>
67
+ {/if}
68
+ </span>
69
+ <span class="mode-check"
70
+ >{#if option === mode}<CheckIcon />{/if}</span
71
+ ></DropdownMenu.Item
72
+ >
73
+ {/each}
74
+ </DropdownMenu.Group>
75
+ <div class="mode-footer">
76
+ <span>{t('chat.mode.label')}</span>
77
+ <span class="mode-keys"
78
+ ><kbd>↑↓</kbd>{t('chat.mode.navigate')}<kbd>↵</kbd>{t('chat.mode.confirm')}<kbd>Esc</kbd
79
+ >{t('chat.mode.close')}</span
80
+ >
81
+ </div>
82
+ </DropdownMenu.Content>
83
+ </DropdownMenu.Root>
84
+ {/if}
85
+
86
+ <style>
87
+ .plan-badge {
88
+ padding: 0.125rem 0.5rem;
89
+ border: 1px solid var(--color-border);
90
+ border-radius: 999px;
91
+ color: var(--color-muted-foreground);
92
+ font-size: var(--kit-font-size-sm, 0.8125rem);
93
+ white-space: nowrap;
94
+ }
95
+ :global(.mode-menu) {
96
+ width: min(35rem, calc(100vw - 1rem));
97
+ padding: 0.375rem;
98
+ border-radius: 0.75rem;
99
+ }
100
+ :global(.mode-option) {
101
+ display: grid;
102
+ grid-template-columns: 1rem 7.5rem minmax(0, 1fr) 1rem 0.875rem;
103
+ align-items: center;
104
+ column-gap: 0.5rem;
105
+ min-height: 2.125rem;
106
+ padding: 0.375rem 0.5rem;
107
+ border-radius: 0.5rem;
108
+ font-size: var(--kit-font-size-sm, 0.8125rem);
109
+ }
110
+ :global(.mode-option[aria-current='true']) {
111
+ background: var(--color-muted);
112
+ }
113
+ :global(.mode-option > svg) {
114
+ color: var(--color-muted-foreground);
115
+ width: 0.875rem;
116
+ height: 0.875rem;
117
+ }
118
+ .mode-name {
119
+ white-space: nowrap;
120
+ }
121
+ .mode-description {
122
+ color: var(--color-muted-foreground);
123
+ font-size: var(--kit-font-size-xs, 0.75rem);
124
+ text-align: right;
125
+ line-height: 1.5;
126
+ }
127
+ .mode-notice,
128
+ .mode-check {
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ color: var(--color-muted-foreground);
133
+ }
134
+ .mode-notice {
135
+ color: var(--color-warning, var(--color-muted-foreground));
136
+ }
137
+ .mode-notice :global(svg),
138
+ .mode-check :global(svg) {
139
+ display: block;
140
+ width: 0.8125rem;
141
+ height: 0.8125rem;
142
+ }
143
+ :global(.mode-danger),
144
+ :global(.mode-danger) :is(.mode-name, .mode-description, .mode-notice),
145
+ :global(.mode-option.mode-danger > svg) {
146
+ color: var(--color-destructive);
147
+ }
148
+ .mode-footer {
149
+ display: flex;
150
+ justify-content: space-between;
151
+ align-items: center;
152
+ gap: 0.75rem;
153
+ padding: 0.625rem 0.375rem 0.125rem;
154
+ color: var(--color-muted-foreground);
155
+ font-size: var(--kit-font-size-xs, 0.75rem);
156
+ }
157
+ .mode-keys {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 0.25rem;
161
+ }
162
+ .mode-keys kbd {
163
+ padding: 0 0.25rem;
164
+ border-radius: 0.25rem;
165
+ background: var(--color-muted);
166
+ font: inherit;
167
+ }
168
+ .mode-keys kbd:not(:first-child) {
169
+ margin-left: 0.375rem;
170
+ }
171
+ @media (max-width: 480px) {
172
+ :global(.mode-option) {
173
+ grid-template-columns: 1rem minmax(0, 1fr) 1rem 0.875rem;
174
+ row-gap: 0.125rem;
175
+ }
176
+ .mode-description {
177
+ grid-column: 2;
178
+ grid-row: 2;
179
+ text-align: left;
180
+ }
181
+ .mode-notice {
182
+ grid-column: 3;
183
+ grid-row: 1;
184
+ }
185
+ .mode-check {
186
+ grid-column: 4;
187
+ grid-row: 1;
188
+ }
189
+ }
190
+ </style>
@@ -0,0 +1 @@
1
+ // Chat panel state implementation.