@antelopejs/dms-ai 0.1.4 → 0.1.5

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 (244) hide show
  1. package/README.md +48 -29
  2. package/dist/channels/bridge.d.ts +2 -8
  3. package/dist/channels/bridge.js +26 -42
  4. package/dist/channels/bridge.js.map +1 -1
  5. package/dist/channels/channel-service.d.ts +2 -2
  6. package/dist/channels/channel-service.js +12 -31
  7. package/dist/channels/channel-service.js.map +1 -1
  8. package/dist/channels/registry.d.ts +2 -2
  9. package/dist/channels/registry.js +7 -7
  10. package/dist/channels/registry.js.map +1 -1
  11. package/dist/config.d.ts +1 -0
  12. package/dist/config.js +8 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/constants/channels.d.ts +5 -10
  15. package/dist/constants/channels.js +6 -10
  16. package/dist/constants/channels.js.map +1 -1
  17. package/dist/constants/sidecar.d.ts +2 -1
  18. package/dist/constants/sidecar.js +3 -2
  19. package/dist/constants/sidecar.js.map +1 -1
  20. package/dist/index.js +8 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lifecycle/build-id.d.ts +1 -0
  23. package/dist/lifecycle/build-id.js +32 -0
  24. package/dist/lifecycle/build-id.js.map +1 -0
  25. package/dist/lifecycle/sidecar-socket.d.ts +1 -1
  26. package/dist/lifecycle/sidecar-socket.js +2 -2
  27. package/dist/lifecycle/sidecar-socket.js.map +1 -1
  28. package/dist/lifecycle/spawn-sidecar.d.ts +0 -1
  29. package/dist/lifecycle/spawn-sidecar.js +5 -27
  30. package/dist/lifecycle/spawn-sidecar.js.map +1 -1
  31. package/dist/routes/channels.d.ts +3 -3
  32. package/dist/routes/channels.js +11 -12
  33. package/dist/routes/channels.js.map +1 -1
  34. package/dist/routes/index.d.ts +0 -1
  35. package/dist/routes/index.js +0 -1
  36. package/dist/routes/index.js.map +1 -1
  37. package/frontend-vue/.prettierignore +3 -0
  38. package/frontend-vue/.prettierrc +10 -0
  39. package/frontend-vue/app/chat/components/ChatView.vue +582 -0
  40. package/frontend-vue/app/chat/components/ComposerInput.vue +742 -0
  41. package/frontend-vue/app/chat/components/ConnectionBanner.vue +105 -0
  42. package/frontend-vue/app/chat/components/ConversationDrawer.vue +266 -0
  43. package/frontend-vue/app/chat/components/MarkdownContent.vue +196 -0
  44. package/frontend-vue/app/chat/components/MessageList.vue +474 -0
  45. package/frontend-vue/app/chat/components/PermissionRequests.vue +323 -0
  46. package/frontend-vue/app/chat/components/QuestionPrompt.vue +275 -0
  47. package/frontend-vue/app/chat/components/QueuedMessages.vue +109 -0
  48. package/frontend-vue/app/chat/components/TodoList.vue +291 -0
  49. package/frontend-vue/app/chat/components/ToolCallEntry.vue +218 -0
  50. package/frontend-vue/app/chat/components/ToolCluster.vue +282 -0
  51. package/frontend-vue/app/chat/composables/useChatChannel.ts +116 -0
  52. package/frontend-vue/app/chat/composables/useConversation.ts +838 -0
  53. package/frontend-vue/app/chat/composables/useConversationId.ts +34 -0
  54. package/frontend-vue/app/chat/composables/useConversationList.ts +56 -0
  55. package/frontend-vue/app/chat/composables/usePermissionQueue.ts +59 -0
  56. package/frontend-vue/app/chat/composables/useQuestionQueue.ts +50 -0
  57. package/frontend-vue/app/chat/composables/useRunClock.ts +28 -0
  58. package/frontend-vue/app/chat/composables/useSettings.ts +67 -0
  59. package/frontend-vue/app/chat/constants/attachments.ts +16 -0
  60. package/frontend-vue/app/chat/constants/connection-banner.ts +7 -0
  61. package/frontend-vue/app/chat/constants/conversation-drawer.ts +13 -0
  62. package/frontend-vue/app/chat/constants/conversation.ts +75 -0
  63. package/frontend-vue/app/chat/constants/permissions.ts +50 -0
  64. package/frontend-vue/app/chat/constants/protocol.ts +48 -0
  65. package/frontend-vue/app/chat/constants/run-status.ts +50 -0
  66. package/frontend-vue/app/chat/constants/settings.ts +60 -0
  67. package/frontend-vue/app/chat/types/conversation.ts +87 -0
  68. package/frontend-vue/app/chat/types/permission.ts +7 -0
  69. package/frontend-vue/app/chat/types/question.ts +17 -0
  70. package/frontend-vue/app/chat/types/settings.ts +27 -0
  71. package/frontend-vue/app/chat/utils/attachments.ts +62 -0
  72. package/frontend-vue/app/chat/utils/format.ts +14 -0
  73. package/frontend-vue/app/chat/utils/ids.ts +34 -0
  74. package/frontend-vue/app/chat/utils/markdown.ts +188 -0
  75. package/frontend-vue/app/chat/utils/run-status.ts +90 -0
  76. package/frontend-vue/app/chat/utils/todos.ts +64 -0
  77. package/frontend-vue/app/chat/utils/tool-summary.ts +53 -0
  78. package/frontend-vue/app/components/ActivityView.vue +34 -34
  79. package/frontend-vue/app/components/ChatPanel.vue +276 -0
  80. package/frontend-vue/app/components/SettingsView.vue +120 -112
  81. package/frontend-vue/app/components/SkillsView.vue +55 -53
  82. package/frontend-vue/app/plugins/ai.client.ts +145 -129
  83. package/frontend-vue/app/runtime/assistant-session.ts +21 -0
  84. package/frontend-vue/app/runtime/backoff.ts +11 -14
  85. package/frontend-vue/app/runtime/channel-client.ts +130 -141
  86. package/frontend-vue/app/runtime/chat-transport.ts +25 -55
  87. package/frontend-vue/app/runtime/constants.ts +69 -59
  88. package/frontend-vue/app/runtime/current-page.ts +49 -15
  89. package/frontend-vue/app/runtime/event-stream.ts +35 -35
  90. package/frontend-vue/app/runtime/frame-router.ts +30 -0
  91. package/frontend-vue/app/runtime/header-action.ts +18 -18
  92. package/frontend-vue/app/runtime/host-commands.ts +57 -56
  93. package/frontend-vue/app/runtime/listeners.ts +31 -0
  94. package/frontend-vue/app/runtime/navigation-complete.ts +7 -7
  95. package/frontend-vue/app/runtime/overlay-prefs.ts +36 -36
  96. package/frontend-vue/app/runtime/panel-state.ts +88 -0
  97. package/frontend-vue/app/runtime/session-gate.ts +8 -8
  98. package/frontend-vue/app/runtime/shortcuts.ts +12 -12
  99. package/frontend-vue/app/runtime/sidecar-status.ts +61 -61
  100. package/frontend-vue/app/runtime/typed-message.ts +11 -0
  101. package/frontend-vue/dms.frontend.ts +12 -12
  102. package/frontend-vue/eslint.config.mjs +13 -13
  103. package/frontend-vue/package.json +11 -4
  104. package/frontend-vue/pnpm-lock.yaml +298 -96
  105. package/frontend-vue/tests/backoff.test.ts +26 -30
  106. package/frontend-vue/tests/channel-client.test.ts +187 -188
  107. package/frontend-vue/tests/chat/markdown.test.ts +271 -0
  108. package/frontend-vue/tests/chat/useChatChannel.test.ts +124 -0
  109. package/frontend-vue/tests/chat/useConversation.test.ts +283 -0
  110. package/frontend-vue/tests/chat-panel.test.ts +354 -0
  111. package/frontend-vue/tests/chat-transport.test.ts +84 -57
  112. package/frontend-vue/tests/host-commands.test.ts +126 -87
  113. package/frontend-vue/tests/navigation.test.ts +65 -34
  114. package/frontend-vue/tests/overlay-prefs.test.ts +49 -49
  115. package/frontend-vue/tests/panel-state.test.ts +58 -0
  116. package/frontend-vue/tests/session-gate.test.ts +33 -33
  117. package/frontend-vue/tests/sidecar-guest.test.ts +46 -46
  118. package/frontend-vue/tests/sidecar-status.test.ts +29 -29
  119. package/frontend-vue/tsconfig.chat.json +16 -0
  120. package/frontend-vue/vitest.config.ts +6 -4
  121. package/package.json +5 -5
  122. package/sidecar/dist/agent/effective-mode.d.ts +2 -2
  123. package/sidecar/dist/agent/effective-mode.js +1 -1
  124. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  125. package/sidecar/dist/agent/host-context.js +7 -9
  126. package/sidecar/dist/agent/host-context.js.map +1 -1
  127. package/sidecar/dist/agent/permission-bus.d.ts +1 -1
  128. package/sidecar/dist/agent/permission-bus.js +2 -2
  129. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  130. package/sidecar/dist/agent/provider.d.ts +2 -2
  131. package/sidecar/dist/agent/question-bus.d.ts +1 -1
  132. package/sidecar/dist/agent/question-bus.js +2 -2
  133. package/sidecar/dist/agent/question-bus.js.map +1 -1
  134. package/sidecar/dist/agent/runner.d.ts +2 -2
  135. package/sidecar/dist/agent/runner.js +24 -8
  136. package/sidecar/dist/agent/runner.js.map +1 -1
  137. package/sidecar/dist/agent/session.d.ts +2 -2
  138. package/sidecar/dist/agent/session.js +49 -9
  139. package/sidecar/dist/agent/session.js.map +1 -1
  140. package/sidecar/dist/agent/switching-runner.js +10 -5
  141. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  142. package/sidecar/dist/agent/turn-end-reasons.d.ts +2 -0
  143. package/sidecar/dist/agent/turn-end-reasons.js +6 -1
  144. package/sidecar/dist/agent/turn-end-reasons.js.map +1 -1
  145. package/sidecar/dist/constants/agent.d.ts +4 -1
  146. package/sidecar/dist/constants/agent.js +4 -1
  147. package/sidecar/dist/constants/agent.js.map +1 -1
  148. package/sidecar/dist/constants/cli.d.ts +0 -1
  149. package/sidecar/dist/constants/cli.js +0 -1
  150. package/sidecar/dist/constants/cli.js.map +1 -1
  151. package/sidecar/dist/constants/codex.d.ts +5 -2
  152. package/sidecar/dist/constants/codex.js +5 -2
  153. package/sidecar/dist/constants/codex.js.map +1 -1
  154. package/sidecar/dist/constants/http.d.ts +0 -9
  155. package/sidecar/dist/constants/http.js +0 -19
  156. package/sidecar/dist/constants/http.js.map +1 -1
  157. package/sidecar/dist/constants/mcp.d.ts +1 -1
  158. package/sidecar/dist/constants/mcp.js +1 -1
  159. package/sidecar/dist/constants/mcp.js.map +1 -1
  160. package/sidecar/dist/constants/paths.d.ts +0 -1
  161. package/sidecar/dist/constants/paths.js +0 -1
  162. package/sidecar/dist/constants/paths.js.map +1 -1
  163. package/sidecar/dist/constants/ws.d.ts +3 -4
  164. package/sidecar/dist/constants/ws.js +3 -4
  165. package/sidecar/dist/constants/ws.js.map +1 -1
  166. package/sidecar/dist/index.d.ts +0 -1
  167. package/sidecar/dist/index.js +6 -10
  168. package/sidecar/dist/index.js.map +1 -1
  169. package/sidecar/dist/mcp/tools/find-pages-using.js +5 -10
  170. package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
  171. package/sidecar/dist/mcp/types.d.ts +1 -1
  172. package/sidecar/dist/pages/page-filepath.d.ts +4 -0
  173. package/sidecar/dist/pages/page-filepath.js +28 -0
  174. package/sidecar/dist/pages/page-filepath.js.map +1 -0
  175. package/sidecar/dist/prompts/agent.d.ts +1 -1
  176. package/sidecar/dist/prompts/agent.js +1 -1
  177. package/sidecar/dist/prompts/agent.js.map +1 -1
  178. package/sidecar/dist/protocol/events.js +2 -2
  179. package/sidecar/dist/protocol/events.js.map +1 -1
  180. package/sidecar/dist/protocol/messages.d.ts +4 -15
  181. package/sidecar/dist/protocol/messages.js +4 -11
  182. package/sidecar/dist/protocol/messages.js.map +1 -1
  183. package/sidecar/dist/providers/claude/config.d.ts +2 -2
  184. package/sidecar/dist/providers/claude/config.js +2 -2
  185. package/sidecar/dist/providers/claude/config.js.map +1 -1
  186. package/sidecar/dist/providers/claude/provider.js +1 -1
  187. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  188. package/sidecar/dist/providers/codex/config.d.ts +3 -2
  189. package/sidecar/dist/providers/codex/config.js +3 -3
  190. package/sidecar/dist/providers/codex/config.js.map +1 -1
  191. package/sidecar/dist/providers/codex/process.js +107 -74
  192. package/sidecar/dist/providers/codex/process.js.map +1 -1
  193. package/sidecar/dist/providers/codex/provider.js +68 -31
  194. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  195. package/sidecar/dist/providers/codex/skills.d.ts +1 -0
  196. package/sidecar/dist/providers/codex/skills.js +4 -1
  197. package/sidecar/dist/providers/codex/skills.js.map +1 -1
  198. package/sidecar/dist/providers/codex/stop.d.ts +4 -0
  199. package/sidecar/dist/providers/codex/stop.js +71 -0
  200. package/sidecar/dist/providers/codex/stop.js.map +1 -0
  201. package/sidecar/dist/server/{iframe-socket-registry.d.ts → chat-socket-registry.d.ts} +3 -2
  202. package/sidecar/dist/server/{iframe-socket-registry.js → chat-socket-registry.js} +4 -3
  203. package/sidecar/dist/server/chat-socket-registry.js.map +1 -0
  204. package/sidecar/dist/server/client-auth.js +2 -14
  205. package/sidecar/dist/server/client-auth.js.map +1 -1
  206. package/sidecar/dist/server/host-command-router.d.ts +5 -0
  207. package/sidecar/dist/server/host-command-router.js +4 -0
  208. package/sidecar/dist/server/host-command-router.js.map +1 -0
  209. package/sidecar/dist/server/host-socket-registry.d.ts +1 -1
  210. package/sidecar/dist/server/host-socket-registry.js +16 -13
  211. package/sidecar/dist/server/host-socket-registry.js.map +1 -1
  212. package/sidecar/dist/server/http.d.ts +0 -1
  213. package/sidecar/dist/server/http.js +1 -44
  214. package/sidecar/dist/server/http.js.map +1 -1
  215. package/sidecar/dist/server/routing.d.ts +5 -6
  216. package/sidecar/dist/server/routing.js +18 -28
  217. package/sidecar/dist/server/routing.js.map +1 -1
  218. package/sidecar/dist/server/ws.d.ts +2 -2
  219. package/sidecar/dist/server/ws.js +41 -39
  220. package/sidecar/dist/server/ws.js.map +1 -1
  221. package/sidecar/dist/state/host-state.d.ts +10 -2
  222. package/sidecar/dist/state/host-state.js +13 -3
  223. package/sidecar/dist/state/host-state.js.map +1 -1
  224. package/sidecar/dist/state/settings-store.js +2 -2
  225. package/sidecar/dist/state/settings-store.js.map +1 -1
  226. package/sidecar/dist/state/settings-types.d.ts +3 -3
  227. package/sidecar/dist/state/settings-types.js +1 -1
  228. package/sidecar/dist/state/settings-types.js.map +1 -1
  229. package/sidecar/package.json +3 -5
  230. package/dist/chatbox/passthrough.d.ts +0 -3
  231. package/dist/chatbox/passthrough.js +0 -53
  232. package/dist/chatbox/passthrough.js.map +0 -1
  233. package/dist/constants/chatbox.d.ts +0 -10
  234. package/dist/constants/chatbox.js +0 -35
  235. package/dist/constants/chatbox.js.map +0 -1
  236. package/dist/routes/chatbox.d.ts +0 -7
  237. package/dist/routes/chatbox.js +0 -47
  238. package/dist/routes/chatbox.js.map +0 -1
  239. package/frontend-vue/app/runtime/overlay.ts +0 -400
  240. package/frontend-vue/app/runtime/theme-bridge.ts +0 -189
  241. package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +0 -1
  242. package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +0 -52
  243. package/sidecar/chatbox/dist/index.html +0 -13
  244. package/sidecar/dist/server/iframe-socket-registry.js.map +0 -1
@@ -0,0 +1,582 @@
1
+ <script setup lang="ts">
2
+ import { computed, nextTick, onMounted, ref, watch } from "vue";
3
+ import type { ChatTransport } from "../../runtime/chat-transport";
4
+ import { useChatChannel } from "../composables/useChatChannel";
5
+ import { useConversation } from "../composables/useConversation";
6
+ import {
7
+ newConversationId,
8
+ persistConversationId,
9
+ resolveConversationId,
10
+ } from "../composables/useConversationId";
11
+ import { useConversationList } from "../composables/useConversationList";
12
+ import { usePermissionQueue } from "../composables/usePermissionQueue";
13
+ import { useQuestionQueue } from "../composables/useQuestionQueue";
14
+ import { useRunClock } from "../composables/useRunClock";
15
+ import { useSettings } from "../composables/useSettings";
16
+ import { TOGGLE_DRAWER_LABEL } from "../constants/conversation-drawer";
17
+ import {
18
+ type ConnectionStatus,
19
+ SERVER_EVENT_TYPES,
20
+ SETTINGS_PAGE_PATH,
21
+ } from "../constants/protocol";
22
+ import {
23
+ MODE_HINTS,
24
+ MODE_OPTIONS,
25
+ MODE_SECTION_LABEL,
26
+ OPEN_SETTINGS_LABEL,
27
+ PROVIDER_BUSY_HINT,
28
+ PROVIDER_OPTIONS,
29
+ PROVIDER_SECTION_LABEL,
30
+ PROVIDER_SWITCH_CONFIRM,
31
+ PROVIDER_SWITCH_WARNING,
32
+ PROVIDER_UNAVAILABLE_PREFIX,
33
+ SAFE_MODE_MODE_NOTE,
34
+ } from "../constants/settings";
35
+ import type { ChatMode, ProviderName } from "../types/settings";
36
+ import type { PendingAttachment } from "../utils/attachments";
37
+ import { isRunStalled } from "../utils/run-status";
38
+ import { latestTodos } from "../utils/todos";
39
+ import ComposerInput from "./ComposerInput.vue";
40
+ import ConnectionBanner from "./ConnectionBanner.vue";
41
+ import ConversationDrawer from "./ConversationDrawer.vue";
42
+ import MessageList from "./MessageList.vue";
43
+ import PermissionRequests from "./PermissionRequests.vue";
44
+ import QuestionPrompt from "./QuestionPrompt.vue";
45
+ import QueuedMessages from "./QueuedMessages.vue";
46
+ import TodoList from "./TodoList.vue";
47
+
48
+ interface Props {
49
+ /** The chat's side of the tab's stream, which the dashboard owns. */
50
+ transport: ChatTransport;
51
+ }
52
+
53
+ interface Emits {
54
+ close: [];
55
+ navigate: [path: string];
56
+ }
57
+
58
+ const props = defineProps<Props>();
59
+ const emit = defineEmits<Emits>();
60
+
61
+ const ASSISTANT_ICON = "i-ph-robot";
62
+ const TOGGLE_DRAWER_ICON = "i-ph-list-light";
63
+ const OPEN_SETTINGS_ICON = "i-ph-gear-six-light";
64
+ const CLOSE_PANEL_ICON = "i-ph-x-light";
65
+
66
+ const MODE_BAR_SELECT_UI = { content: "min-w-fit" };
67
+
68
+ function isObject(value: unknown): value is Record<string, unknown> {
69
+ return value !== null && typeof value === "object";
70
+ }
71
+
72
+ const STATUS_LABEL_BY_STATE: Record<ConnectionStatus, string> = {
73
+ connecting: "connecting…",
74
+ connected: "connected",
75
+ reconnecting: "reconnecting…",
76
+ disconnected: "disconnected",
77
+ };
78
+
79
+ const activeId = ref(resolveConversationId());
80
+ const channel = useChatChannel({
81
+ transport: props.transport,
82
+ getConversationId: () => activeId.value,
83
+ });
84
+ const permissionQueue = usePermissionQueue({ send: channel.send });
85
+ const questionQueue = useQuestionQueue({ send: channel.send });
86
+ const conversation = useConversation({
87
+ activeId,
88
+ send: channel.send,
89
+ onMessage: channel.onMessage,
90
+ onPermissionRequest: permissionQueue.enqueue,
91
+ onQuestionRequest: questionQueue.enqueue,
92
+ });
93
+ const conversationList = useConversationList({
94
+ send: channel.send,
95
+ onMessage: channel.onMessage,
96
+ });
97
+ const settings = useSettings({
98
+ send: channel.send,
99
+ onMessage: channel.onMessage,
100
+ });
101
+
102
+ const drawerOpen = ref(false);
103
+ const nowMs = ref(Date.now());
104
+ const runClockMs = useRunClock(conversation.isRunning);
105
+
106
+ const isRunStalledNow = computed<boolean>(
107
+ () =>
108
+ conversation.isTurnInFlight.value &&
109
+ channel.isConnected.value &&
110
+ isRunStalled(conversation.lastEventAtMs.value, runClockMs.value),
111
+ );
112
+
113
+ const stalledForMs = computed<number>(
114
+ () => runClockMs.value - conversation.lastEventAtMs.value,
115
+ );
116
+
117
+ function openSettingsPage(): void {
118
+ emit("navigate", SETTINGS_PAGE_PATH);
119
+ }
120
+
121
+ const modeHint = computed(() => {
122
+ const { mode, generationMode, builderAvailable } = settings.settings.value;
123
+ const isSafeModeActive = generationMode === "safe" && builderAvailable;
124
+ return isSafeModeActive
125
+ ? `${MODE_HINTS[mode]}. ${SAFE_MODE_MODE_NOTE}`
126
+ : MODE_HINTS[mode];
127
+ });
128
+
129
+ const modeModel = computed<ChatMode>({
130
+ get: () => settings.settings.value.mode,
131
+ set: (value) => {
132
+ if (value !== settings.settings.value.mode)
133
+ settings.update({ mode: value });
134
+ },
135
+ });
136
+
137
+ const providerItems = computed(() =>
138
+ PROVIDER_OPTIONS.map((option) => ({
139
+ ...option,
140
+ disabled:
141
+ !isProviderAvailable(option.value) || conversation.isRunning.value,
142
+ })),
143
+ );
144
+
145
+ function isProviderAvailable(name: ProviderName): boolean {
146
+ return settings.settings.value.providers[name]?.available !== false;
147
+ }
148
+
149
+ const providerHint = computed(() => {
150
+ const active = settings.settings.value.provider;
151
+ const reason = settings.settings.value.providers[active]?.reason;
152
+ if (reason !== undefined) return `${PROVIDER_UNAVAILABLE_PREFIX}${reason}`;
153
+ if (conversation.isRunning.value) return PROVIDER_BUSY_HINT;
154
+ return PROVIDER_SWITCH_WARNING;
155
+ });
156
+
157
+ function mayLeaveCurrentSession(): boolean {
158
+ if (conversation.messages.value.length === 0) return true;
159
+ return window.confirm(PROVIDER_SWITCH_CONFIRM);
160
+ }
161
+
162
+ const providerModel = computed<ProviderName>({
163
+ get: () => settings.settings.value.provider,
164
+ set: (value) => {
165
+ if (value === settings.settings.value.provider) return;
166
+ if (!isProviderAvailable(value)) return;
167
+ if (conversation.isRunning.value) return;
168
+ if (!mayLeaveCurrentSession()) return;
169
+ settings.update({ provider: value });
170
+ },
171
+ });
172
+
173
+ function dismissPanel(): void {
174
+ emit("close");
175
+ }
176
+
177
+ function refreshList(): void {
178
+ nowMs.value = Date.now();
179
+ conversationList.refresh();
180
+ }
181
+
182
+ function openDrawer(): void {
183
+ refreshList();
184
+ drawerOpen.value = true;
185
+ }
186
+
187
+ function closeDrawer(): void {
188
+ drawerOpen.value = false;
189
+ }
190
+
191
+ function switchConversation(id: string): void {
192
+ closeDrawer();
193
+ if (id === activeId.value) return;
194
+ activeId.value = id;
195
+ persistConversationId(id);
196
+ conversation.reset();
197
+ permissionQueue.clear();
198
+ questionQueue.clear();
199
+ channel.reidentify();
200
+ }
201
+
202
+ function newConversation(): void {
203
+ switchConversation(newConversationId());
204
+ }
205
+
206
+ function deleteConversation(id: string): void {
207
+ const wasActive = id === activeId.value;
208
+ conversationList.remove(id);
209
+ if (wasActive) newConversation();
210
+ }
211
+
212
+ function refreshListAfterTurn(msg: unknown): void {
213
+ if (!drawerOpen.value) return;
214
+ if (!isObject(msg) || msg.type !== SERVER_EVENT_TYPES.RUN_DONE) return;
215
+ refreshList();
216
+ }
217
+
218
+ channel.onMessage(refreshListAfterTurn);
219
+
220
+ const statusLabel = computed<string>(
221
+ () => STATUS_LABEL_BY_STATE[channel.connectionStatus.value],
222
+ );
223
+
224
+ const todos = computed(() => latestTodos(conversation.messages.value));
225
+
226
+ const SCROLL_STICKY_THRESHOLD_PX = 80;
227
+ const scrollContainer = ref<HTMLElement | null>(null);
228
+
229
+ function isNearBottom(el: HTMLElement): boolean {
230
+ const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
231
+ return distance <= SCROLL_STICKY_THRESHOLD_PX;
232
+ }
233
+
234
+ function scrollToBottom(): void {
235
+ const el = scrollContainer.value;
236
+ if (el === null) return;
237
+ el.scrollTop = el.scrollHeight;
238
+ }
239
+
240
+ watch(
241
+ () => [conversation.messages.value, conversation.isRunning.value],
242
+ () => {
243
+ const el = scrollContainer.value;
244
+ const sticky = el === null || isNearBottom(el);
245
+ if (!sticky) return;
246
+ void nextTick(scrollToBottom);
247
+ },
248
+ );
249
+
250
+ onMounted(() => {
251
+ void nextTick(scrollToBottom);
252
+ });
253
+
254
+ function onComposerSubmit(
255
+ content: string,
256
+ attachments: PendingAttachment[],
257
+ ): void {
258
+ conversation.sendUserMessage(content, attachments);
259
+ }
260
+
261
+ function onComposerStop(): void {
262
+ conversation.interrupt();
263
+ permissionQueue.clear();
264
+ questionQueue.clear();
265
+ }
266
+
267
+ function manualReconnect(): void {
268
+ channel.reconnect();
269
+ }
270
+
271
+ function retryLastMessage(): void {
272
+ conversation.retry();
273
+ }
274
+ </script>
275
+
276
+ <template>
277
+ <main class="chat-view">
278
+ <header class="chat-view-header">
279
+ <span class="chat-view-logo" aria-hidden="true">
280
+ <UIcon :name="ASSISTANT_ICON" class="size-[19px]" />
281
+ </span>
282
+ <div class="chat-view-titlewrap">
283
+ <b class="chat-view-title">AntelopeJS Assistant</b>
284
+ <span
285
+ class="chat-view-status"
286
+ :data-connected="channel.isConnected.value"
287
+ aria-live="polite"
288
+ >
289
+ <i class="chat-view-status-dot" />
290
+ {{ statusLabel }}
291
+ </span>
292
+ </div>
293
+ <button
294
+ type="button"
295
+ class="chat-view-ibtn"
296
+ :aria-label="TOGGLE_DRAWER_LABEL"
297
+ :title="TOGGLE_DRAWER_LABEL"
298
+ @click="openDrawer"
299
+ >
300
+ <UIcon :name="TOGGLE_DRAWER_ICON" class="size-[18px]" />
301
+ </button>
302
+ <button
303
+ type="button"
304
+ class="chat-view-ibtn"
305
+ :aria-label="OPEN_SETTINGS_LABEL"
306
+ :title="OPEN_SETTINGS_LABEL"
307
+ @click="openSettingsPage"
308
+ >
309
+ <UIcon :name="OPEN_SETTINGS_ICON" class="size-[18px]" />
310
+ </button>
311
+ <button
312
+ type="button"
313
+ class="chat-view-ibtn"
314
+ aria-label="Close"
315
+ title="Close"
316
+ @click="dismissPanel"
317
+ >
318
+ <UIcon :name="CLOSE_PANEL_ICON" class="size-[18px]" />
319
+ </button>
320
+ </header>
321
+
322
+ <div class="chat-view-modebar">
323
+ <span class="modebar-label">{{ MODE_SECTION_LABEL }}</span>
324
+ <USelect
325
+ v-model="modeModel"
326
+ :items="MODE_OPTIONS"
327
+ :portal="false"
328
+ :ui="MODE_BAR_SELECT_UI"
329
+ variant="ghost"
330
+ size="sm"
331
+ :title="modeHint"
332
+ class="text-primary font-semibold"
333
+ />
334
+ <span class="modebar-label">{{ PROVIDER_SECTION_LABEL }}</span>
335
+ <USelect
336
+ v-model="providerModel"
337
+ :items="providerItems"
338
+ :portal="false"
339
+ :ui="MODE_BAR_SELECT_UI"
340
+ variant="ghost"
341
+ size="sm"
342
+ :title="providerHint"
343
+ class="text-primary font-semibold"
344
+ />
345
+ </div>
346
+
347
+ <ConnectionBanner
348
+ :status="channel.connectionStatus.value"
349
+ @reconnect="manualReconnect"
350
+ />
351
+
352
+ <section ref="scrollContainer" class="chat-view-body">
353
+ <MessageList
354
+ :messages="conversation.messages.value"
355
+ :is-running="conversation.isRunning.value"
356
+ :progress="conversation.progress.value"
357
+ :now-ms="runClockMs"
358
+ :is-stalled="isRunStalledNow"
359
+ :stalled-for-ms="stalledForMs"
360
+ @retry="retryLastMessage"
361
+ @reconnect="manualReconnect"
362
+ @stop="onComposerStop"
363
+ />
364
+ </section>
365
+
366
+ <PermissionRequests
367
+ v-if="permissionQueue.queue.value.length > 0"
368
+ :requests="permissionQueue.queue.value"
369
+ @decide="permissionQueue.respond"
370
+ @decide-all="permissionQueue.respondAll"
371
+ />
372
+
373
+ <QuestionPrompt
374
+ v-if="questionQueue.queue.value.length > 0"
375
+ :requests="questionQueue.queue.value"
376
+ @answer="questionQueue.respond"
377
+ />
378
+
379
+ <TodoList
380
+ v-if="todos.length > 0"
381
+ :todos="todos"
382
+ :is-running="conversation.isRunning.value"
383
+ />
384
+
385
+ <QueuedMessages
386
+ v-if="conversation.queued.value.length > 0"
387
+ :queue="conversation.queued.value"
388
+ @cancel="conversation.cancelQueued"
389
+ />
390
+
391
+ <ComposerInput
392
+ :is-disabled="!channel.isConnected.value"
393
+ :is-running="conversation.isRunning.value"
394
+ :generation-mode="settings.settings.value.generationMode"
395
+ :builder-available="settings.settings.value.builderAvailable"
396
+ @submit="onComposerSubmit"
397
+ @stop="onComposerStop"
398
+ @update:generation-mode="(m) => settings.update({ generationMode: m })"
399
+ />
400
+
401
+ <ConversationDrawer
402
+ :open="drawerOpen"
403
+ :conversations="conversationList.conversations.value"
404
+ :active-id="activeId"
405
+ :now-ms="nowMs"
406
+ @select="switchConversation"
407
+ @delete="deleteConversation"
408
+ @new="newConversation"
409
+ @close="closeDrawer"
410
+ />
411
+ </main>
412
+ </template>
413
+
414
+ <style scoped>
415
+ /*
416
+ * The chat's design tokens, aliases of the dashboard's Nuxt UI tokens scoped to
417
+ * the chat: it follows the dashboard's theme and light/dark mode as they are.
418
+ */
419
+ .chat-view {
420
+ --accent: var(--ui-primary);
421
+ --accent-strong: var(--ui-color-primary-700);
422
+ --accent-fg: var(--ui-bg);
423
+ --accent-bg: color-mix(in oklab, var(--ui-primary) 10%, transparent);
424
+ --accent-bg-strong: color-mix(in oklab, var(--ui-primary) 18%, transparent);
425
+ --surface-side: var(--ui-bg);
426
+ --surface-card: var(--ui-bg-elevated);
427
+ --surface-card-2: var(--ui-bg-elevated);
428
+ --surface-inset: var(--ui-bg-muted);
429
+ --fg: var(--ui-text-highlighted);
430
+ --fg-secondary: var(--ui-text-toned);
431
+ --fg-tertiary: var(--ui-text-dimmed);
432
+ --hair: var(--ui-border);
433
+ --hair-strong: var(--ui-border-accented);
434
+ --success-400: var(--ui-color-success-400);
435
+ --success-500: var(--ui-color-success-500);
436
+ --success-bg: color-mix(
437
+ in oklab,
438
+ var(--ui-color-success-500) 10%,
439
+ transparent
440
+ );
441
+ --warning-400: var(--ui-color-warning-400);
442
+ --warning-bg: color-mix(
443
+ in oklab,
444
+ var(--ui-color-warning-500) 10%,
445
+ transparent
446
+ );
447
+ --danger-400: var(--ui-color-error-400);
448
+ --danger-bg: color-mix(in oklab, var(--ui-color-error-500) 12%, transparent);
449
+ --corner-sm: var(--ui-radius);
450
+ --corner-md: calc(var(--ui-radius) * 1.5);
451
+ --corner-lg: calc(var(--ui-radius) * 2);
452
+ --font-code: var(
453
+ --font-mono,
454
+ ui-monospace,
455
+ SFMono-Regular,
456
+ Menlo,
457
+ Monaco,
458
+ Consolas,
459
+ "Liberation Mono",
460
+ "Courier New",
461
+ monospace
462
+ );
463
+ --dur-fast: 150ms;
464
+
465
+ position: relative;
466
+ display: flex;
467
+ flex-direction: column;
468
+ height: 100%;
469
+ font-size: 13px;
470
+ background: var(--surface-side);
471
+ color: var(--fg);
472
+ }
473
+
474
+ .chat-view,
475
+ .chat-view :deep(*) {
476
+ scrollbar-width: thin;
477
+ scrollbar-color: var(--hair) transparent;
478
+ }
479
+
480
+ .chat-view-header {
481
+ display: flex;
482
+ align-items: center;
483
+ gap: 11px;
484
+ padding: 14px 14px 12px;
485
+ border-bottom: 1px solid var(--hair);
486
+ }
487
+
488
+ .chat-view-logo {
489
+ width: 34px;
490
+ height: 34px;
491
+ flex: 0 0 auto;
492
+ display: grid;
493
+ place-items: center;
494
+ border-radius: var(--corner-md);
495
+ background: var(--accent-bg);
496
+ border: 1px solid var(--accent-bg-strong);
497
+ color: var(--accent);
498
+ }
499
+
500
+ .chat-view-titlewrap {
501
+ flex: 1;
502
+ min-width: 0;
503
+ }
504
+
505
+ .chat-view-title {
506
+ display: block;
507
+ font-size: 15px;
508
+ font-weight: 600;
509
+ color: var(--fg);
510
+ }
511
+
512
+ .chat-view-status {
513
+ display: flex;
514
+ align-items: center;
515
+ gap: 6px;
516
+ font-size: 11.5px;
517
+ margin-top: 2px;
518
+ color: var(--fg-tertiary);
519
+ }
520
+
521
+ .chat-view-status-dot {
522
+ width: 6px;
523
+ height: 6px;
524
+ border-radius: 50%;
525
+ background: var(--fg-tertiary);
526
+ }
527
+
528
+ .chat-view-status[data-connected="true"] {
529
+ color: var(--success-400);
530
+ }
531
+
532
+ .chat-view-status[data-connected="true"] .chat-view-status-dot {
533
+ background: var(--success-500);
534
+ }
535
+
536
+ .chat-view-status[data-connected="false"] {
537
+ color: var(--danger-400);
538
+ }
539
+
540
+ .chat-view-status[data-connected="false"] .chat-view-status-dot {
541
+ background: var(--danger-400);
542
+ }
543
+
544
+ .chat-view-ibtn {
545
+ width: 30px;
546
+ height: 30px;
547
+ flex: 0 0 auto;
548
+ display: grid;
549
+ place-items: center;
550
+ border: none;
551
+ background: transparent;
552
+ border-radius: 7px;
553
+ color: var(--fg-tertiary);
554
+ line-height: 1;
555
+ cursor: pointer;
556
+ }
557
+
558
+ .chat-view-ibtn:hover {
559
+ background: var(--surface-inset);
560
+ color: var(--fg);
561
+ }
562
+
563
+ .chat-view-modebar {
564
+ display: flex;
565
+ align-items: center;
566
+ gap: 8px;
567
+ padding: 9px 14px;
568
+ background: var(--surface-inset);
569
+ border-bottom: 1px solid var(--hair);
570
+ }
571
+
572
+ .modebar-label {
573
+ font-size: 11.5px;
574
+ color: var(--fg-tertiary);
575
+ }
576
+
577
+ .chat-view-body {
578
+ flex: 1;
579
+ min-height: 0;
580
+ overflow-y: auto;
581
+ }
582
+ </style>