@antelopejs/dms-ai 0.1.3 → 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.
- package/README.md +48 -29
- package/dist/channels/bridge.d.ts +2 -8
- package/dist/channels/bridge.js +26 -42
- package/dist/channels/bridge.js.map +1 -1
- package/dist/channels/channel-service.d.ts +2 -2
- package/dist/channels/channel-service.js +12 -31
- package/dist/channels/channel-service.js.map +1 -1
- package/dist/channels/registry.d.ts +2 -2
- package/dist/channels/registry.js +7 -7
- package/dist/channels/registry.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/constants/channels.d.ts +5 -10
- package/dist/constants/channels.js +6 -10
- package/dist/constants/channels.js.map +1 -1
- package/dist/constants/sidecar.d.ts +2 -1
- package/dist/constants/sidecar.js +3 -2
- package/dist/constants/sidecar.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/build-id.d.ts +1 -0
- package/dist/lifecycle/build-id.js +32 -0
- package/dist/lifecycle/build-id.js.map +1 -0
- package/dist/lifecycle/sidecar-socket.d.ts +1 -1
- package/dist/lifecycle/sidecar-socket.js +2 -2
- package/dist/lifecycle/sidecar-socket.js.map +1 -1
- package/dist/lifecycle/spawn-sidecar.d.ts +0 -1
- package/dist/lifecycle/spawn-sidecar.js +5 -27
- package/dist/lifecycle/spawn-sidecar.js.map +1 -1
- package/dist/pages/settings/page.js.map +1 -1
- package/dist/routes/channels.d.ts +3 -3
- package/dist/routes/channels.js +11 -12
- package/dist/routes/channels.js.map +1 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/index.js.map +1 -1
- package/frontend-vue/.prettierignore +3 -0
- package/frontend-vue/.prettierrc +10 -0
- package/frontend-vue/app/chat/components/ChatView.vue +582 -0
- package/frontend-vue/app/chat/components/ComposerInput.vue +742 -0
- package/frontend-vue/app/chat/components/ConnectionBanner.vue +105 -0
- package/frontend-vue/app/chat/components/ConversationDrawer.vue +266 -0
- package/frontend-vue/app/chat/components/MarkdownContent.vue +196 -0
- package/frontend-vue/app/chat/components/MessageList.vue +474 -0
- package/frontend-vue/app/chat/components/PermissionRequests.vue +323 -0
- package/frontend-vue/app/chat/components/QuestionPrompt.vue +275 -0
- package/frontend-vue/app/chat/components/QueuedMessages.vue +109 -0
- package/frontend-vue/app/chat/components/TodoList.vue +291 -0
- package/frontend-vue/app/chat/components/ToolCallEntry.vue +218 -0
- package/frontend-vue/app/chat/components/ToolCluster.vue +282 -0
- package/frontend-vue/app/chat/composables/useChatChannel.ts +116 -0
- package/frontend-vue/app/chat/composables/useConversation.ts +838 -0
- package/frontend-vue/app/chat/composables/useConversationId.ts +34 -0
- package/frontend-vue/app/chat/composables/useConversationList.ts +56 -0
- package/frontend-vue/app/chat/composables/usePermissionQueue.ts +59 -0
- package/frontend-vue/app/chat/composables/useQuestionQueue.ts +50 -0
- package/frontend-vue/app/chat/composables/useRunClock.ts +28 -0
- package/frontend-vue/app/chat/composables/useSettings.ts +67 -0
- package/frontend-vue/app/chat/constants/attachments.ts +16 -0
- package/frontend-vue/app/chat/constants/connection-banner.ts +7 -0
- package/frontend-vue/app/chat/constants/conversation-drawer.ts +13 -0
- package/frontend-vue/app/chat/constants/conversation.ts +75 -0
- package/frontend-vue/app/chat/constants/permissions.ts +50 -0
- package/frontend-vue/app/chat/constants/protocol.ts +48 -0
- package/frontend-vue/app/chat/constants/run-status.ts +50 -0
- package/frontend-vue/app/chat/constants/settings.ts +60 -0
- package/frontend-vue/app/chat/types/conversation.ts +87 -0
- package/frontend-vue/app/chat/types/permission.ts +7 -0
- package/frontend-vue/app/chat/types/question.ts +17 -0
- package/frontend-vue/app/chat/types/settings.ts +27 -0
- package/frontend-vue/app/chat/utils/attachments.ts +62 -0
- package/frontend-vue/app/chat/utils/format.ts +14 -0
- package/frontend-vue/app/chat/utils/ids.ts +34 -0
- package/frontend-vue/app/chat/utils/markdown.ts +188 -0
- package/frontend-vue/app/chat/utils/run-status.ts +90 -0
- package/frontend-vue/app/chat/utils/todos.ts +64 -0
- package/frontend-vue/app/chat/utils/tool-summary.ts +53 -0
- package/frontend-vue/app/components/ActivityView.vue +34 -34
- package/frontend-vue/app/components/ChatPanel.vue +276 -0
- package/frontend-vue/app/components/SettingsView.vue +127 -130
- package/frontend-vue/app/components/SkillsView.vue +55 -53
- package/frontend-vue/app/plugins/ai.client.ts +145 -129
- package/frontend-vue/app/runtime/assistant-session.ts +21 -0
- package/frontend-vue/app/runtime/backoff.ts +11 -14
- package/frontend-vue/app/runtime/channel-client.ts +130 -141
- package/frontend-vue/app/runtime/chat-transport.ts +25 -55
- package/frontend-vue/app/runtime/constants.ts +70 -60
- package/frontend-vue/app/runtime/current-page.ts +49 -15
- package/frontend-vue/app/runtime/event-stream.ts +35 -35
- package/frontend-vue/app/runtime/frame-router.ts +30 -0
- package/frontend-vue/app/runtime/header-action.ts +18 -18
- package/frontend-vue/app/runtime/host-commands.ts +57 -56
- package/frontend-vue/app/runtime/listeners.ts +31 -0
- package/frontend-vue/app/runtime/navigation-complete.ts +7 -7
- package/frontend-vue/app/runtime/overlay-prefs.ts +36 -36
- package/frontend-vue/app/runtime/panel-state.ts +88 -0
- package/frontend-vue/app/runtime/session-gate.ts +8 -8
- package/frontend-vue/app/runtime/shortcuts.ts +12 -12
- package/frontend-vue/app/runtime/sidecar-status.ts +61 -61
- package/frontend-vue/app/runtime/typed-message.ts +11 -0
- package/frontend-vue/dms.frontend.ts +12 -12
- package/frontend-vue/eslint.config.mjs +13 -13
- package/frontend-vue/package.json +11 -4
- package/frontend-vue/pnpm-lock.yaml +298 -96
- package/frontend-vue/tests/backoff.test.ts +26 -30
- package/frontend-vue/tests/channel-client.test.ts +187 -188
- package/frontend-vue/tests/chat/markdown.test.ts +271 -0
- package/frontend-vue/tests/chat/useChatChannel.test.ts +124 -0
- package/frontend-vue/tests/chat/useConversation.test.ts +283 -0
- package/frontend-vue/tests/chat-panel.test.ts +354 -0
- package/frontend-vue/tests/chat-transport.test.ts +84 -57
- package/frontend-vue/tests/host-commands.test.ts +126 -87
- package/frontend-vue/tests/navigation.test.ts +65 -34
- package/frontend-vue/tests/overlay-prefs.test.ts +49 -49
- package/frontend-vue/tests/panel-state.test.ts +58 -0
- package/frontend-vue/tests/session-gate.test.ts +33 -33
- package/frontend-vue/tests/sidecar-guest.test.ts +46 -46
- package/frontend-vue/tests/sidecar-status.test.ts +29 -29
- package/frontend-vue/tsconfig.chat.json +16 -0
- package/frontend-vue/vitest.config.ts +6 -4
- package/package.json +5 -5
- package/sidecar/dist/agent/effective-mode.d.ts +2 -2
- package/sidecar/dist/agent/effective-mode.js +1 -1
- package/sidecar/dist/agent/effective-mode.js.map +1 -1
- package/sidecar/dist/agent/host-context.js +7 -9
- package/sidecar/dist/agent/host-context.js.map +1 -1
- package/sidecar/dist/agent/permission-bus.d.ts +1 -1
- package/sidecar/dist/agent/permission-bus.js +2 -2
- package/sidecar/dist/agent/permission-bus.js.map +1 -1
- package/sidecar/dist/agent/provider.d.ts +2 -2
- package/sidecar/dist/agent/question-bus.d.ts +1 -1
- package/sidecar/dist/agent/question-bus.js +2 -2
- package/sidecar/dist/agent/question-bus.js.map +1 -1
- package/sidecar/dist/agent/runner.d.ts +2 -2
- package/sidecar/dist/agent/runner.js +24 -8
- package/sidecar/dist/agent/runner.js.map +1 -1
- package/sidecar/dist/agent/session.d.ts +2 -2
- package/sidecar/dist/agent/session.js +49 -9
- package/sidecar/dist/agent/session.js.map +1 -1
- package/sidecar/dist/agent/switching-runner.js +10 -5
- package/sidecar/dist/agent/switching-runner.js.map +1 -1
- package/sidecar/dist/agent/turn-end-reasons.d.ts +2 -0
- package/sidecar/dist/agent/turn-end-reasons.js +6 -1
- package/sidecar/dist/agent/turn-end-reasons.js.map +1 -1
- package/sidecar/dist/constants/agent.d.ts +4 -1
- package/sidecar/dist/constants/agent.js +4 -1
- package/sidecar/dist/constants/agent.js.map +1 -1
- package/sidecar/dist/constants/cli.d.ts +0 -1
- package/sidecar/dist/constants/cli.js +0 -1
- package/sidecar/dist/constants/cli.js.map +1 -1
- package/sidecar/dist/constants/codex.d.ts +5 -2
- package/sidecar/dist/constants/codex.js +5 -2
- package/sidecar/dist/constants/codex.js.map +1 -1
- package/sidecar/dist/constants/http.d.ts +0 -9
- package/sidecar/dist/constants/http.js +0 -19
- package/sidecar/dist/constants/http.js.map +1 -1
- package/sidecar/dist/constants/mcp.d.ts +1 -1
- package/sidecar/dist/constants/mcp.js +1 -1
- package/sidecar/dist/constants/mcp.js.map +1 -1
- package/sidecar/dist/constants/paths.d.ts +0 -1
- package/sidecar/dist/constants/paths.js +0 -1
- package/sidecar/dist/constants/paths.js.map +1 -1
- package/sidecar/dist/constants/ws.d.ts +3 -4
- package/sidecar/dist/constants/ws.js +3 -4
- package/sidecar/dist/constants/ws.js.map +1 -1
- package/sidecar/dist/index.d.ts +0 -1
- package/sidecar/dist/index.js +6 -10
- package/sidecar/dist/index.js.map +1 -1
- package/sidecar/dist/mcp/tools/find-pages-using.js +5 -10
- package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
- package/sidecar/dist/mcp/types.d.ts +1 -1
- package/sidecar/dist/pages/page-filepath.d.ts +4 -0
- package/sidecar/dist/pages/page-filepath.js +28 -0
- package/sidecar/dist/pages/page-filepath.js.map +1 -0
- package/sidecar/dist/prompts/agent.d.ts +1 -1
- package/sidecar/dist/prompts/agent.js +1 -1
- package/sidecar/dist/prompts/agent.js.map +1 -1
- package/sidecar/dist/protocol/events.js +2 -2
- package/sidecar/dist/protocol/events.js.map +1 -1
- package/sidecar/dist/protocol/messages.d.ts +4 -15
- package/sidecar/dist/protocol/messages.js +4 -11
- package/sidecar/dist/protocol/messages.js.map +1 -1
- package/sidecar/dist/providers/claude/config.d.ts +2 -2
- package/sidecar/dist/providers/claude/config.js +2 -2
- package/sidecar/dist/providers/claude/config.js.map +1 -1
- package/sidecar/dist/providers/claude/provider.js +1 -1
- package/sidecar/dist/providers/claude/provider.js.map +1 -1
- package/sidecar/dist/providers/codex/config.d.ts +3 -2
- package/sidecar/dist/providers/codex/config.js +3 -3
- package/sidecar/dist/providers/codex/config.js.map +1 -1
- package/sidecar/dist/providers/codex/process.js +107 -74
- package/sidecar/dist/providers/codex/process.js.map +1 -1
- package/sidecar/dist/providers/codex/provider.js +68 -31
- package/sidecar/dist/providers/codex/provider.js.map +1 -1
- package/sidecar/dist/providers/codex/skills.d.ts +1 -0
- package/sidecar/dist/providers/codex/skills.js +4 -1
- package/sidecar/dist/providers/codex/skills.js.map +1 -1
- package/sidecar/dist/providers/codex/stop.d.ts +4 -0
- package/sidecar/dist/providers/codex/stop.js +71 -0
- package/sidecar/dist/providers/codex/stop.js.map +1 -0
- package/sidecar/dist/server/{iframe-socket-registry.d.ts → chat-socket-registry.d.ts} +3 -2
- package/sidecar/dist/server/{iframe-socket-registry.js → chat-socket-registry.js} +4 -3
- package/sidecar/dist/server/chat-socket-registry.js.map +1 -0
- package/sidecar/dist/server/client-auth.js +2 -14
- package/sidecar/dist/server/client-auth.js.map +1 -1
- package/sidecar/dist/server/host-command-router.d.ts +5 -0
- package/sidecar/dist/server/host-command-router.js +4 -0
- package/sidecar/dist/server/host-command-router.js.map +1 -0
- package/sidecar/dist/server/host-socket-registry.d.ts +1 -1
- package/sidecar/dist/server/host-socket-registry.js +16 -13
- package/sidecar/dist/server/host-socket-registry.js.map +1 -1
- package/sidecar/dist/server/http.d.ts +0 -1
- package/sidecar/dist/server/http.js +1 -44
- package/sidecar/dist/server/http.js.map +1 -1
- package/sidecar/dist/server/routing.d.ts +5 -6
- package/sidecar/dist/server/routing.js +18 -28
- package/sidecar/dist/server/routing.js.map +1 -1
- package/sidecar/dist/server/ws.d.ts +2 -2
- package/sidecar/dist/server/ws.js +41 -39
- package/sidecar/dist/server/ws.js.map +1 -1
- package/sidecar/dist/state/host-state.d.ts +10 -2
- package/sidecar/dist/state/host-state.js +13 -3
- package/sidecar/dist/state/host-state.js.map +1 -1
- package/sidecar/dist/state/settings-store.js +2 -2
- package/sidecar/dist/state/settings-store.js.map +1 -1
- package/sidecar/dist/state/settings-types.d.ts +3 -3
- package/sidecar/dist/state/settings-types.js +1 -1
- package/sidecar/dist/state/settings-types.js.map +1 -1
- package/sidecar/package.json +3 -5
- package/dist/chatbox/passthrough.d.ts +0 -3
- package/dist/chatbox/passthrough.js +0 -53
- package/dist/chatbox/passthrough.js.map +0 -1
- package/dist/constants/chatbox.d.ts +0 -10
- package/dist/constants/chatbox.js +0 -35
- package/dist/constants/chatbox.js.map +0 -1
- package/dist/routes/chatbox.d.ts +0 -7
- package/dist/routes/chatbox.js +0 -47
- package/dist/routes/chatbox.js.map +0 -1
- package/frontend-vue/app/runtime/overlay.ts +0 -400
- package/frontend-vue/app/runtime/theme-bridge.ts +0 -189
- package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +0 -1
- package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +0 -52
- package/sidecar/chatbox/dist/index.html +0 -13
- package/sidecar/dist/server/iframe-socket-registry.js.map +0 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type Ref, readonly, ref } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
OVERLAY_DEFAULT_WIDTH_PX,
|
|
4
|
+
OVERLAY_MIN_WIDTH_PX,
|
|
5
|
+
OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS,
|
|
6
|
+
} from "./constants";
|
|
7
|
+
import {
|
|
8
|
+
type OverlayPrefs,
|
|
9
|
+
readPrefs,
|
|
10
|
+
writePrefsDebounced,
|
|
11
|
+
} from "./overlay-prefs";
|
|
12
|
+
|
|
13
|
+
const MAX_WIDTH_VW_RATIO = 0.94;
|
|
14
|
+
|
|
15
|
+
/** Whether the chat panel is open and how wide it is, remembered per browser. */
|
|
16
|
+
export interface ChatPanelState {
|
|
17
|
+
isOpen: Readonly<Ref<boolean>>;
|
|
18
|
+
width: Readonly<Ref<number>>;
|
|
19
|
+
/** The keyboard shortcut's toggle: never suppressed. */
|
|
20
|
+
toggle: () => void;
|
|
21
|
+
/**
|
|
22
|
+
* The header launcher's toggle. It ignores an open request right after an
|
|
23
|
+
* outside click closed the panel: the launcher's `click` follows the same
|
|
24
|
+
* `pointerdown` that closed it, and would open it again at once.
|
|
25
|
+
*/
|
|
26
|
+
toggleFromLauncher: () => void;
|
|
27
|
+
close: () => void;
|
|
28
|
+
/** An outside click closed the panel; `timeStamp` is the event's. */
|
|
29
|
+
closeFromOutside: (timeStamp: number) => void;
|
|
30
|
+
/** Resizes live, between the minimum width and most of the viewport. */
|
|
31
|
+
resize: (width: number) => void;
|
|
32
|
+
/** Remembers the width once a resize is over. */
|
|
33
|
+
commitWidth: () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function maxWidth(): number {
|
|
37
|
+
const viewportWidth = globalThis.innerWidth ?? OVERLAY_DEFAULT_WIDTH_PX * 3;
|
|
38
|
+
return Math.max(
|
|
39
|
+
OVERLAY_MIN_WIDTH_PX,
|
|
40
|
+
Math.round(viewportWidth * MAX_WIDTH_VW_RATIO),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function clampWidth(width: number): number {
|
|
45
|
+
return Math.min(Math.max(width, OVERLAY_MIN_WIDTH_PX), maxWidth());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveInitialPrefs(): OverlayPrefs {
|
|
49
|
+
const stored = readPrefs();
|
|
50
|
+
if (stored === null)
|
|
51
|
+
return { width: OVERLAY_DEFAULT_WIDTH_PX, isOpen: false };
|
|
52
|
+
return { width: clampWidth(stored.width), isOpen: stored.isOpen };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isRightAfter(timeStamp: number): boolean {
|
|
56
|
+
return performance.now() - timeStamp < OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createChatPanelState(): ChatPanelState {
|
|
60
|
+
const initial = resolveInitialPrefs();
|
|
61
|
+
const isOpen = ref(initial.isOpen);
|
|
62
|
+
const width = ref(initial.width);
|
|
63
|
+
let outsideCloseAt = Number.NEGATIVE_INFINITY;
|
|
64
|
+
const persist = (): void =>
|
|
65
|
+
writePrefsDebounced({ width: width.value, isOpen: isOpen.value });
|
|
66
|
+
const setOpen = (next: boolean): void => {
|
|
67
|
+
isOpen.value = next;
|
|
68
|
+
persist();
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
isOpen: readonly(isOpen),
|
|
72
|
+
width: readonly(width),
|
|
73
|
+
toggle: () => setOpen(!isOpen.value),
|
|
74
|
+
toggleFromLauncher: () => {
|
|
75
|
+
if (!isOpen.value && isRightAfter(outsideCloseAt)) return;
|
|
76
|
+
setOpen(!isOpen.value);
|
|
77
|
+
},
|
|
78
|
+
close: () => setOpen(false),
|
|
79
|
+
closeFromOutside: (timeStamp) => {
|
|
80
|
+
outsideCloseAt = timeStamp;
|
|
81
|
+
setOpen(false);
|
|
82
|
+
},
|
|
83
|
+
resize: (next) => {
|
|
84
|
+
width.value = clampWidth(next);
|
|
85
|
+
},
|
|
86
|
+
commitWidth: persist,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { watch } from
|
|
1
|
+
import { watch } from "vue";
|
|
2
2
|
|
|
3
3
|
// Plugins run once, when the Vue app boots, on whatever page the browser landed
|
|
4
4
|
// on. Landing on the sign-in screen means there is no session yet, and signing
|
|
@@ -10,13 +10,13 @@ export function runWhenLoggedIn(
|
|
|
10
10
|
start: () => void,
|
|
11
11
|
): () => void {
|
|
12
12
|
if (isLoggedIn()) {
|
|
13
|
-
start()
|
|
14
|
-
return () => undefined
|
|
13
|
+
start();
|
|
14
|
+
return () => undefined;
|
|
15
15
|
}
|
|
16
16
|
const stop = watch(isLoggedIn, (loggedIn) => {
|
|
17
|
-
if (!loggedIn) return
|
|
18
|
-
stop()
|
|
19
|
-
start()
|
|
20
|
-
})
|
|
21
|
-
return stop
|
|
17
|
+
if (!loggedIn) return;
|
|
18
|
+
stop();
|
|
19
|
+
start();
|
|
20
|
+
});
|
|
21
|
+
return stop;
|
|
22
22
|
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { MODAL_OPEN_SELECTOR, TOGGLE_SHORTCUT_KEY } from
|
|
1
|
+
import { MODAL_OPEN_SELECTOR, TOGGLE_SHORTCUT_KEY } from "./constants";
|
|
2
2
|
|
|
3
3
|
export interface InstallToggleShortcutOptions {
|
|
4
|
-
onToggle: () => void
|
|
4
|
+
onToggle: () => void;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
function isModalOpen(): boolean {
|
|
8
|
-
return document.querySelector(MODAL_OPEN_SELECTOR) !== null
|
|
8
|
+
return document.querySelector(MODAL_OPEN_SELECTOR) !== null;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function isToggleCombo(event: KeyboardEvent): boolean {
|
|
12
|
-
const isModifierPressed = event.metaKey || event.ctrlKey
|
|
13
|
-
const isTargetKey = event.key.toLowerCase() === TOGGLE_SHORTCUT_KEY
|
|
12
|
+
const isModifierPressed = event.metaKey || event.ctrlKey;
|
|
13
|
+
const isTargetKey = event.key.toLowerCase() === TOGGLE_SHORTCUT_KEY;
|
|
14
14
|
// Require shift too — plain ctrl/cmd + k is the DMS search palette.
|
|
15
|
-
return isModifierPressed && event.shiftKey && isTargetKey
|
|
15
|
+
return isModifierPressed && event.shiftKey && isTargetKey;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function installToggleShortcut(
|
|
19
19
|
options: InstallToggleShortcutOptions,
|
|
20
20
|
): void {
|
|
21
21
|
const handler = (event: KeyboardEvent): void => {
|
|
22
|
-
if (!isToggleCombo(event)) return
|
|
23
|
-
if (isModalOpen()) return
|
|
24
|
-
event.preventDefault()
|
|
25
|
-
options.onToggle()
|
|
26
|
-
}
|
|
27
|
-
document.addEventListener(
|
|
22
|
+
if (!isToggleCombo(event)) return;
|
|
23
|
+
if (isModalOpen()) return;
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
options.onToggle();
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener("keydown", handler);
|
|
28
28
|
}
|
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
import { createBackoff, nextDelay, resetBackoff } from
|
|
1
|
+
import { createBackoff, nextDelay, resetBackoff } from "./backoff";
|
|
2
2
|
import {
|
|
3
3
|
SIDECAR_POLL_DELAYS_MS,
|
|
4
4
|
SIDECAR_STATUS_CONNECTED,
|
|
5
5
|
SIDECAR_STATUS_CONNECTING,
|
|
6
6
|
SIDECAR_STATUS_REVIVING,
|
|
7
7
|
SIDECAR_STATUS_UNAVAILABLE,
|
|
8
|
-
} from
|
|
8
|
+
} from "./constants";
|
|
9
9
|
|
|
10
10
|
export type SidecarStatus =
|
|
11
11
|
| typeof SIDECAR_STATUS_CONNECTING
|
|
12
12
|
| typeof SIDECAR_STATUS_CONNECTED
|
|
13
13
|
| typeof SIDECAR_STATUS_REVIVING
|
|
14
|
-
| typeof SIDECAR_STATUS_UNAVAILABLE
|
|
14
|
+
| typeof SIDECAR_STATUS_UNAVAILABLE;
|
|
15
15
|
|
|
16
16
|
interface SidecarInfo {
|
|
17
|
-
isRunning: boolean
|
|
18
|
-
hasGivenUp: boolean
|
|
19
|
-
disabled: boolean
|
|
17
|
+
isRunning: boolean;
|
|
18
|
+
hasGivenUp: boolean;
|
|
19
|
+
disabled: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface SidecarStatusController {
|
|
23
23
|
// Runs the first probe. Returns false when the assistant should not be shown
|
|
24
24
|
// at all — the dms-ai backend route is absent (probe unreachable) or spawning
|
|
25
25
|
// is disabled for this process — so the caller skips injection entirely.
|
|
26
|
-
init: () => Promise<boolean
|
|
27
|
-
getStatus: () => SidecarStatus
|
|
26
|
+
init: () => Promise<boolean>;
|
|
27
|
+
getStatus: () => SidecarStatus;
|
|
28
28
|
// Subscribe to status changes; fires immediately with the current status so a
|
|
29
29
|
// late subscriber (overlay/icon injected after init) reflects it right away.
|
|
30
|
-
subscribe: (cb: (status: SidecarStatus) => void) => () => void
|
|
30
|
+
subscribe: (cb: (status: SidecarStatus) => void) => () => void;
|
|
31
31
|
/**
|
|
32
32
|
* The host channel dropped: re-probe now, which also revives an idle-exited
|
|
33
33
|
* sidecar, and report `connected` again once it answers.
|
|
34
34
|
*/
|
|
35
|
-
reportChannelDown: () => void
|
|
36
|
-
dispose: () => void
|
|
35
|
+
reportChannelDown: () => void;
|
|
36
|
+
dispose: () => void;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export type FetchSidecarInfo = () => Promise<Partial<SidecarInfo
|
|
39
|
+
export type FetchSidecarInfo = () => Promise<Partial<SidecarInfo>>;
|
|
40
40
|
|
|
41
41
|
async function fetchInfo(
|
|
42
42
|
fetchDetails: FetchSidecarInfo,
|
|
43
43
|
): Promise<SidecarInfo | null> {
|
|
44
44
|
try {
|
|
45
|
-
const body = await fetchDetails()
|
|
45
|
+
const body = await fetchDetails();
|
|
46
46
|
return {
|
|
47
47
|
isRunning: body.isRunning === true,
|
|
48
48
|
hasGivenUp: body.hasGivenUp === true,
|
|
49
49
|
disabled: body.disabled === true,
|
|
50
|
-
}
|
|
50
|
+
};
|
|
51
51
|
} catch {
|
|
52
|
-
return null
|
|
52
|
+
return null;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export function createSidecarStatusController(
|
|
57
57
|
fetchDetails: FetchSidecarInfo,
|
|
58
58
|
): SidecarStatusController {
|
|
59
|
-
const backoff = createBackoff()
|
|
60
|
-
const subscribers = new Set<(status: SidecarStatus) => void>()
|
|
61
|
-
let status: SidecarStatus = SIDECAR_STATUS_CONNECTING
|
|
62
|
-
let pollTimer: ReturnType<typeof setTimeout> | null = null
|
|
63
|
-
let disposed = false
|
|
59
|
+
const backoff = createBackoff();
|
|
60
|
+
const subscribers = new Set<(status: SidecarStatus) => void>();
|
|
61
|
+
let status: SidecarStatus = SIDECAR_STATUS_CONNECTING;
|
|
62
|
+
let pollTimer: ReturnType<typeof setTimeout> | null = null;
|
|
63
|
+
let disposed = false;
|
|
64
64
|
|
|
65
65
|
function notify(): void {
|
|
66
|
-
for (const cb of subscribers) cb(status)
|
|
66
|
+
for (const cb of subscribers) cb(status);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function setStatus(next: SidecarStatus): void {
|
|
70
|
-
if (status === next) return
|
|
71
|
-
status = next
|
|
72
|
-
notify()
|
|
70
|
+
if (status === next) return;
|
|
71
|
+
status = next;
|
|
72
|
+
notify();
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function schedulePoll(): void {
|
|
76
|
-
if (disposed || pollTimer !== null) return
|
|
77
|
-
const delay = nextDelay(backoff, SIDECAR_POLL_DELAYS_MS)
|
|
76
|
+
if (disposed || pollTimer !== null) return;
|
|
77
|
+
const delay = nextDelay(backoff, SIDECAR_POLL_DELAYS_MS);
|
|
78
78
|
pollTimer = setTimeout(() => {
|
|
79
|
-
pollTimer = null
|
|
80
|
-
void poll()
|
|
81
|
-
}, delay)
|
|
79
|
+
pollTimer = null;
|
|
80
|
+
void poll();
|
|
81
|
+
}, delay);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
async function poll(): Promise<void> {
|
|
85
|
-
if (disposed) return
|
|
86
|
-
handleInfo(await fetchInfo(fetchDetails))
|
|
85
|
+
if (disposed) return;
|
|
86
|
+
handleInfo(await fetchInfo(fetchDetails));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function handleInfo(info: SidecarInfo | null): void {
|
|
90
|
-
if (disposed) return
|
|
90
|
+
if (disposed) return;
|
|
91
91
|
// Probe itself failed (backend momentarily unreachable): keep waiting.
|
|
92
92
|
if (info === null) {
|
|
93
|
-
setStatus(SIDECAR_STATUS_REVIVING)
|
|
94
|
-
schedulePoll()
|
|
95
|
-
return
|
|
93
|
+
setStatus(SIDECAR_STATUS_REVIVING);
|
|
94
|
+
schedulePoll();
|
|
95
|
+
return;
|
|
96
96
|
}
|
|
97
97
|
if (info.hasGivenUp) {
|
|
98
|
-
setStatus(SIDECAR_STATUS_UNAVAILABLE)
|
|
99
|
-
return
|
|
98
|
+
setStatus(SIDECAR_STATUS_UNAVAILABLE);
|
|
99
|
+
return;
|
|
100
100
|
}
|
|
101
101
|
if (info.isRunning) {
|
|
102
|
-
resetBackoff(backoff)
|
|
103
|
-
setStatus(SIDECAR_STATUS_CONNECTED)
|
|
104
|
-
return
|
|
102
|
+
resetBackoff(backoff);
|
|
103
|
+
setStatus(SIDECAR_STATUS_CONNECTED);
|
|
104
|
+
return;
|
|
105
105
|
}
|
|
106
106
|
// Down but not terminal — the probe already asked the backend to respawn;
|
|
107
107
|
// keep polling until it reports the sidecar running.
|
|
108
|
-
setStatus(SIDECAR_STATUS_REVIVING)
|
|
109
|
-
schedulePoll()
|
|
108
|
+
setStatus(SIDECAR_STATUS_REVIVING);
|
|
109
|
+
schedulePoll();
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return {
|
|
113
113
|
init: async (): Promise<boolean> => {
|
|
114
|
-
const info = await fetchInfo(fetchDetails)
|
|
115
|
-
if (info === null) return false
|
|
116
|
-
if (info.disabled) return false
|
|
117
|
-
handleInfo(info)
|
|
118
|
-
return true
|
|
114
|
+
const info = await fetchInfo(fetchDetails);
|
|
115
|
+
if (info === null) return false;
|
|
116
|
+
if (info.disabled) return false;
|
|
117
|
+
handleInfo(info);
|
|
118
|
+
return true;
|
|
119
119
|
},
|
|
120
120
|
getStatus: () => status,
|
|
121
121
|
subscribe: (cb) => {
|
|
122
|
-
subscribers.add(cb)
|
|
123
|
-
cb(status)
|
|
122
|
+
subscribers.add(cb);
|
|
123
|
+
cb(status);
|
|
124
124
|
return () => {
|
|
125
|
-
subscribers.delete(cb)
|
|
126
|
-
}
|
|
125
|
+
subscribers.delete(cb);
|
|
126
|
+
};
|
|
127
127
|
},
|
|
128
128
|
reportChannelDown: () => {
|
|
129
|
-
if (disposed) return
|
|
130
|
-
if (status !== SIDECAR_STATUS_CONNECTED) return
|
|
129
|
+
if (disposed) return;
|
|
130
|
+
if (status !== SIDECAR_STATUS_CONNECTED) return;
|
|
131
131
|
// Surface the drop right away (amber dot + spinner) instead of waiting on
|
|
132
132
|
// the re-probe: that probe awaits a respawn and can take a second or two,
|
|
133
133
|
// during which the icon would otherwise look healthy. poll() flips back
|
|
134
134
|
// to connected once the sidecar answers again.
|
|
135
|
-
resetBackoff(backoff)
|
|
136
|
-
setStatus(SIDECAR_STATUS_REVIVING)
|
|
137
|
-
void poll()
|
|
135
|
+
resetBackoff(backoff);
|
|
136
|
+
setStatus(SIDECAR_STATUS_REVIVING);
|
|
137
|
+
void poll();
|
|
138
138
|
},
|
|
139
139
|
dispose: () => {
|
|
140
|
-
disposed = true
|
|
140
|
+
disposed = true;
|
|
141
141
|
if (pollTimer !== null) {
|
|
142
|
-
clearTimeout(pollTimer)
|
|
143
|
-
pollTimer = null
|
|
142
|
+
clearTimeout(pollTimer);
|
|
143
|
+
pollTimer = null;
|
|
144
144
|
}
|
|
145
|
-
subscribers.clear()
|
|
145
|
+
subscribers.clear();
|
|
146
146
|
},
|
|
147
|
-
}
|
|
147
|
+
};
|
|
148
148
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TypedMessage {
|
|
2
|
+
type: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/** Whether `value` is an object carrying a string `type`, as every protocol message does. */
|
|
6
|
+
export function isTypedMessage<T extends TypedMessage = TypedMessage>(
|
|
7
|
+
value: unknown,
|
|
8
|
+
): value is T {
|
|
9
|
+
if (value === null || typeof value !== "object") return false;
|
|
10
|
+
return typeof Reflect.get(value, "type") === "string";
|
|
11
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { defineAsyncComponent, type Component } from
|
|
2
|
-
import type { DmsFrontendModule } from
|
|
3
|
-
import aiPlugin from
|
|
1
|
+
import { defineAsyncComponent, type Component } from "vue";
|
|
2
|
+
import type { DmsFrontendModule } from "#dms/frontend-module";
|
|
3
|
+
import aiPlugin from "./app/plugins/ai.client";
|
|
4
4
|
|
|
5
5
|
interface VueModule {
|
|
6
|
-
default: Component
|
|
6
|
+
default: Component;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const components = import.meta.glob<VueModule>(
|
|
9
|
+
const components = import.meta.glob<VueModule>("./app/components/**/*.vue");
|
|
10
10
|
|
|
11
11
|
const frontendModule: DmsFrontendModule = {
|
|
12
12
|
setup(sdk) {
|
|
@@ -14,13 +14,13 @@ const frontendModule: DmsFrontendModule = {
|
|
|
14
14
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
15
15
|
.forEach(([path, loader]) => {
|
|
16
16
|
const name = `DmsAi${path
|
|
17
|
-
.split(
|
|
17
|
+
.split("/")
|
|
18
18
|
.at(-1)!
|
|
19
|
-
.replace(/\.vue$/,
|
|
20
|
-
sdk.registerComponent(name, defineAsyncComponent(loader))
|
|
21
|
-
})
|
|
22
|
-
sdk.registerPlugin(aiPlugin, { clientOnly: true })
|
|
19
|
+
.replace(/\.vue$/, "")}`;
|
|
20
|
+
sdk.registerComponent(name, defineAsyncComponent(loader));
|
|
21
|
+
});
|
|
22
|
+
sdk.registerPlugin(aiPlugin, { clientOnly: true });
|
|
23
23
|
},
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
25
|
|
|
26
|
-
export default frontendModule
|
|
26
|
+
export default frontendModule;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import eslint from
|
|
2
|
-
import ts from
|
|
3
|
-
import vue from
|
|
4
|
-
import globals from
|
|
1
|
+
import eslint from "@eslint/js";
|
|
2
|
+
import ts from "typescript-eslint";
|
|
3
|
+
import vue from "eslint-plugin-vue";
|
|
4
|
+
import globals from "globals";
|
|
5
5
|
|
|
6
6
|
export default ts.config(
|
|
7
|
-
{ ignores: [
|
|
7
|
+
{ ignores: ["dist/**", "node_modules/**"] },
|
|
8
8
|
eslint.configs.recommended,
|
|
9
|
-
...vue.configs[
|
|
9
|
+
...vue.configs["flat/essential"],
|
|
10
10
|
{
|
|
11
|
-
files: [
|
|
11
|
+
files: ["**/*.{ts,vue}"],
|
|
12
12
|
languageOptions: {
|
|
13
13
|
parserOptions: { parser: ts.parser },
|
|
14
14
|
globals: { ...globals.browser, ...globals.node },
|
|
15
15
|
},
|
|
16
16
|
rules: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
"no-undef": "off",
|
|
18
|
+
"no-unused-vars": "off",
|
|
19
|
+
"no-redeclare": "off",
|
|
20
|
+
"vue/multi-word-component-names": "off",
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
|
-
{ files: [
|
|
24
|
-
)
|
|
23
|
+
{ files: ["**/*.ts"], languageOptions: { parser: ts.parser } },
|
|
24
|
+
);
|
|
@@ -11,25 +11,32 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "pnpm --dir \"${DMS_FRONTEND_WORKSPACE:?Set DMS_FRONTEND_WORKSPACE to the generated Inertia workspace}\" build",
|
|
13
13
|
"typecheck": "pnpm --dir \"${DMS_FRONTEND_WORKSPACE:?Set DMS_FRONTEND_WORKSPACE to the generated Inertia workspace}\" typecheck",
|
|
14
|
+
"typecheck:chat": "vue-tsc --noEmit -p tsconfig.chat.json",
|
|
14
15
|
"format": "prettier --write .",
|
|
15
|
-
"lint": "eslint .",
|
|
16
|
-
"lint:fix": "eslint . --fix",
|
|
16
|
+
"lint": "eslint . && prettier --check .",
|
|
17
|
+
"lint:fix": "eslint . --fix && prettier --write .",
|
|
17
18
|
"test": "vitest run --passWithNoTests"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@inertiajs/vue3": "^3.7.0",
|
|
22
|
+
"@types/markdown-it": "^14.1.2",
|
|
23
|
+
"dompurify": "^3.2.4",
|
|
24
|
+
"markdown-it": "^14.1.0",
|
|
21
25
|
"vue": "^3.5.18"
|
|
22
26
|
},
|
|
23
27
|
"devDependencies": {
|
|
24
28
|
"@eslint/js": "^9.0.0",
|
|
25
|
-
"
|
|
29
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
30
|
+
"@vitest/runner": "^4.1.5",
|
|
31
|
+
"@vue/test-utils": "^2.4.6",
|
|
26
32
|
"eslint": "^9.0.0",
|
|
27
33
|
"eslint-plugin-vue": "^10.0.0",
|
|
28
34
|
"globals": "^16.0.0",
|
|
29
|
-
"@vitest/runner": "^4.1.5",
|
|
30
35
|
"jsdom": "^29.1.1",
|
|
31
36
|
"prettier": "^3.5.3",
|
|
37
|
+
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
32
38
|
"typescript": "^5.8.2",
|
|
39
|
+
"typescript-eslint": "^8.0.0",
|
|
33
40
|
"vitest": "^4.1.5",
|
|
34
41
|
"vue-tsc": "^3.1.1"
|
|
35
42
|
}
|