@antelopejs/dms-ai 0.1.4 → 0.1.6
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 +55 -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 +5 -1
- package/dist/constants/sidecar.js +6 -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-owner.d.ts +13 -0
- package/dist/lifecycle/sidecar-owner.js +89 -0
- package/dist/lifecycle/sidecar-owner.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 +51 -159
- package/dist/lifecycle/spawn-sidecar.js.map +1 -1
- package/dist/lifecycle/terminate-process.d.ts +1 -0
- package/dist/lifecycle/terminate-process.js +34 -0
- package/dist/lifecycle/terminate-process.js.map +1 -0
- 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 +120 -112
- 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 +69 -59
- 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 +6 -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 +4 -1
- package/sidecar/dist/index.js +13 -13
- 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
package/sidecar/package.json
CHANGED
|
@@ -9,12 +9,10 @@
|
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "rimraf dist && tsc",
|
|
12
|
-
"build:chatbox": "cd chatbox && pnpm install && pnpm build",
|
|
13
|
-
"build:all": "pnpm build && pnpm build:chatbox",
|
|
14
12
|
"dev": "tsc -w",
|
|
15
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json
|
|
16
|
-
"lint": "oxlint && oxfmt --check .
|
|
17
|
-
"lint:fix": "oxlint --fix --max-warnings=1000000 && oxfmt .
|
|
13
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
14
|
+
"lint": "oxlint && oxfmt --check .",
|
|
15
|
+
"lint:fix": "oxlint --fix --max-warnings=1000000 && oxfmt .",
|
|
18
16
|
"test": "vitest run --passWithNoTests",
|
|
19
17
|
"generate:codex-types": "node scripts/generate-codex-types.mjs",
|
|
20
18
|
"test:e2e": "echo 'no e2e yet' && exit 0",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { HTTPResult, type RequestContext } from "@antelopejs/interface-api";
|
|
2
|
-
export type ChatboxLoader = (path: string) => Promise<Response>;
|
|
3
|
-
export declare function relayChatboxFile(ctx: RequestContext, path: string, load?: ChatboxLoader): Promise<HTTPResult | undefined>;
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.relayChatboxFile = relayChatboxFile;
|
|
4
|
-
const node_stream_1 = require("node:stream");
|
|
5
|
-
const promises_1 = require("node:stream/promises");
|
|
6
|
-
const interface_api_1 = require("@antelopejs/interface-api");
|
|
7
|
-
const chatbox_1 = require("../constants/chatbox");
|
|
8
|
-
const sidecar_1 = require("../constants/sidecar");
|
|
9
|
-
const sidecar_unavailable_error_1 = require("../lifecycle/sidecar-unavailable-error");
|
|
10
|
-
const spawn_sidecar_1 = require("../lifecycle/spawn-sidecar");
|
|
11
|
-
async function loadFromSidecar(path) {
|
|
12
|
-
await (0, spawn_sidecar_1.ensureSidecarRunning)();
|
|
13
|
-
const port = (0, spawn_sidecar_1.getSidecarPort)();
|
|
14
|
-
if (port === null)
|
|
15
|
-
throw new sidecar_unavailable_error_1.SidecarUnavailableError();
|
|
16
|
-
return fetch(`http://${sidecar_1.SIDECAR_LOOPBACK_HOST}:${port}${path}`);
|
|
17
|
-
}
|
|
18
|
-
async function tryLoad(load, path) {
|
|
19
|
-
try {
|
|
20
|
-
return await load(path);
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function applyHeaders(ctx, upstream) {
|
|
27
|
-
for (const [name, value] of Object.entries(chatbox_1.CHATBOX_RESPONSE_HEADERS)) {
|
|
28
|
-
ctx.response.addHeader(name, value);
|
|
29
|
-
}
|
|
30
|
-
for (const name of chatbox_1.CHATBOX_RELAYED_HEADERS) {
|
|
31
|
-
const value = upstream.headers.get(name);
|
|
32
|
-
if (value !== null)
|
|
33
|
-
ctx.response.addHeader(name, value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async function relayChatboxFile(ctx, path, load = loadFromSidecar) {
|
|
37
|
-
const upstream = await tryLoad(load, path);
|
|
38
|
-
if (upstream === null) {
|
|
39
|
-
return new interface_api_1.HTTPResult(sidecar_1.SIDECAR_UNAVAILABLE_STATUS, sidecar_1.SIDECAR_UNAVAILABLE_BODY);
|
|
40
|
-
}
|
|
41
|
-
applyHeaders(ctx, upstream);
|
|
42
|
-
const contentType = upstream.headers.get(chatbox_1.CHATBOX_CONTENT_TYPE_HEADER) ??
|
|
43
|
-
chatbox_1.CHATBOX_DEFAULT_CONTENT_TYPE;
|
|
44
|
-
const sink = ctx.response.getWriteStream(contentType, upstream.status);
|
|
45
|
-
if (upstream.body === null) {
|
|
46
|
-
sink.end();
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
const body = node_stream_1.Readable.fromWeb(upstream.body);
|
|
50
|
-
void (0, promises_1.pipeline)(body, sink).catch(() => sink.destroy());
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=passthrough.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"passthrough.js","sourceRoot":"","sources":["../../src/chatbox/passthrough.ts"],"names":[],"mappings":";;AAyDA,4CAqBC;AA9ED,6CAAuC;AACvC,mDAAgD;AAEhD,6DAA4E;AAC5E,kDAK8B;AAC9B,kDAI8B;AAC9B,sFAAiF;AACjF,8DAGoC;AAKpC,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,MAAM,IAAA,oCAAoB,GAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAA,8BAAc,GAAE,CAAC;IAC9B,IAAI,IAAI,KAAK,IAAI;QAAE,MAAM,IAAI,mDAAuB,EAAE,CAAC;IACvD,OAAO,KAAK,CAAC,UAAU,+BAAqB,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,IAAmB,EACnB,IAAY;IAEZ,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,GAAmB,EAAE,QAAkB;IAC3D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kCAAwB,CAAC,EAAE,CAAC;QACrE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,iCAAuB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAOM,KAAK,UAAU,gBAAgB,CACpC,GAAmB,EACnB,IAAY,EACZ,OAAsB,eAAe;IAErC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,IAAI,0BAAU,CAAC,oCAA0B,EAAE,kCAAwB,CAAC,CAAC;IAC9E,CAAC;IACD,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC5B,MAAM,WAAW,GACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qCAA2B,CAAC;QACjD,sCAA4B,CAAC;IAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvE,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,sBAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAqC,CAAC,CAAC;IAC9E,KAAK,IAAA,mBAAQ,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare const CHATBOX_ROUTE_PREFIX = "/api/ai/chatbox";
|
|
2
|
-
export declare const CHATBOX_INDEX_ROUTE = "/";
|
|
3
|
-
export declare const CHATBOX_ASSET_ROUTE = "::path";
|
|
4
|
-
export declare const CHATBOX_PATH_PARAM = "path";
|
|
5
|
-
export declare const CHATBOX_INDEX_PATH = "/";
|
|
6
|
-
export declare const CHATBOX_DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
7
|
-
export declare const CHATBOX_CONTENT_TYPE_HEADER = "content-type";
|
|
8
|
-
export declare const CHATBOX_RELAYED_HEADERS: readonly ["content-length"];
|
|
9
|
-
export declare const CHATBOX_CONTENT_SECURITY_POLICY: string;
|
|
10
|
-
export declare const CHATBOX_RESPONSE_HEADERS: Readonly<Record<string, string>>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CHATBOX_RESPONSE_HEADERS = exports.CHATBOX_CONTENT_SECURITY_POLICY = exports.CHATBOX_RELAYED_HEADERS = exports.CHATBOX_CONTENT_TYPE_HEADER = exports.CHATBOX_DEFAULT_CONTENT_TYPE = exports.CHATBOX_INDEX_PATH = exports.CHATBOX_PATH_PARAM = exports.CHATBOX_ASSET_ROUTE = exports.CHATBOX_INDEX_ROUTE = exports.CHATBOX_ROUTE_PREFIX = void 0;
|
|
4
|
-
exports.CHATBOX_ROUTE_PREFIX = "/api/ai/chatbox";
|
|
5
|
-
exports.CHATBOX_INDEX_ROUTE = "/";
|
|
6
|
-
exports.CHATBOX_ASSET_ROUTE = "::path";
|
|
7
|
-
exports.CHATBOX_PATH_PARAM = "path";
|
|
8
|
-
exports.CHATBOX_INDEX_PATH = "/";
|
|
9
|
-
exports.CHATBOX_DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
10
|
-
exports.CHATBOX_CONTENT_TYPE_HEADER = "content-type";
|
|
11
|
-
exports.CHATBOX_RELAYED_HEADERS = ["content-length"];
|
|
12
|
-
const ICONIFY_API_ORIGINS = [
|
|
13
|
-
"https://api.iconify.design",
|
|
14
|
-
"https://api.simplesvg.com",
|
|
15
|
-
"https://api.unisvg.com",
|
|
16
|
-
];
|
|
17
|
-
exports.CHATBOX_CONTENT_SECURITY_POLICY = [
|
|
18
|
-
"default-src 'self'",
|
|
19
|
-
"script-src 'self'",
|
|
20
|
-
"style-src 'self' 'unsafe-inline'",
|
|
21
|
-
"img-src 'self' data: blob:",
|
|
22
|
-
"font-src 'self' data:",
|
|
23
|
-
`connect-src 'self' ${ICONIFY_API_ORIGINS.join(" ")}`,
|
|
24
|
-
"frame-ancestors 'self'",
|
|
25
|
-
"base-uri 'self'",
|
|
26
|
-
"form-action 'none'",
|
|
27
|
-
"object-src 'none'",
|
|
28
|
-
].join("; ");
|
|
29
|
-
exports.CHATBOX_RESPONSE_HEADERS = {
|
|
30
|
-
"Content-Security-Policy": exports.CHATBOX_CONTENT_SECURITY_POLICY,
|
|
31
|
-
"X-Content-Type-Options": "nosniff",
|
|
32
|
-
"Referrer-Policy": "same-origin",
|
|
33
|
-
"Cache-Control": "no-cache",
|
|
34
|
-
};
|
|
35
|
-
//# sourceMappingURL=chatbox.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chatbox.js","sourceRoot":"","sources":["../../src/constants/chatbox.ts"],"names":[],"mappings":";;;AAIa,QAAA,oBAAoB,GAAG,iBAAiB,CAAC;AACzC,QAAA,mBAAmB,GAAG,GAAG,CAAC;AAC1B,QAAA,mBAAmB,GAAG,QAAQ,CAAC;AAC/B,QAAA,kBAAkB,GAAG,MAAM,CAAC;AAC5B,QAAA,kBAAkB,GAAG,GAAG,CAAC;AACzB,QAAA,4BAA4B,GAAG,0BAA0B,CAAC;AAC1D,QAAA,2BAA2B,GAAG,cAAc,CAAC;AAG7C,QAAA,uBAAuB,GAAG,CAAC,gBAAgB,CAAU,CAAC;AAEnE,MAAM,mBAAmB,GAAG;IAC1B,4BAA4B;IAC5B,2BAA2B;IAC3B,wBAAwB;CACzB,CAAC;AAUW,QAAA,+BAA+B,GAAG;IAC7C,oBAAoB;IACpB,mBAAmB;IACnB,kCAAkC;IAClC,4BAA4B;IAC5B,uBAAuB;IACvB,sBAAsB,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;IACrD,wBAAwB;IACxB,iBAAiB;IACjB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEA,QAAA,wBAAwB,GAAqC;IACxE,yBAAyB,EAAE,uCAA+B;IAC1D,wBAAwB,EAAE,SAAS;IACnC,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,UAAU;CAC5B,CAAC"}
|
package/dist/routes/chatbox.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type HTTPResult, type RequestContext } from "@antelopejs/interface-api";
|
|
2
|
-
declare const AIChatboxController_base: import("@antelopejs/interface-api").ControllerClass<object>;
|
|
3
|
-
export declare class AIChatboxController extends AIChatboxController_base {
|
|
4
|
-
document(ctx: RequestContext): Promise<HTTPResult | undefined>;
|
|
5
|
-
asset(ctx: RequestContext, path: string): Promise<HTTPResult | undefined>;
|
|
6
|
-
}
|
|
7
|
-
export {};
|
package/dist/routes/chatbox.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.AIChatboxController = void 0;
|
|
16
|
-
const interface_api_1 = require("@antelopejs/interface-api");
|
|
17
|
-
const auth_1 = require("@antelopejs/interface-dms/auth");
|
|
18
|
-
const passthrough_1 = require("../chatbox/passthrough");
|
|
19
|
-
const chatbox_1 = require("../constants/chatbox");
|
|
20
|
-
let AIChatboxController = class AIChatboxController extends (0, interface_api_1.Controller)(chatbox_1.CHATBOX_ROUTE_PREFIX) {
|
|
21
|
-
document(ctx) {
|
|
22
|
-
return (0, passthrough_1.relayChatboxFile)(ctx, chatbox_1.CHATBOX_INDEX_PATH);
|
|
23
|
-
}
|
|
24
|
-
asset(ctx, path) {
|
|
25
|
-
return (0, passthrough_1.relayChatboxFile)(ctx, `/${path}`);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
exports.AIChatboxController = AIChatboxController;
|
|
29
|
-
__decorate([
|
|
30
|
-
(0, interface_api_1.Get)(chatbox_1.CHATBOX_INDEX_ROUTE),
|
|
31
|
-
__param(0, (0, interface_api_1.Context)()),
|
|
32
|
-
__metadata("design:type", Function),
|
|
33
|
-
__metadata("design:paramtypes", [Object]),
|
|
34
|
-
__metadata("design:returntype", Promise)
|
|
35
|
-
], AIChatboxController.prototype, "document", null);
|
|
36
|
-
__decorate([
|
|
37
|
-
(0, interface_api_1.Get)(chatbox_1.CHATBOX_ASSET_ROUTE),
|
|
38
|
-
__param(0, (0, interface_api_1.Context)()),
|
|
39
|
-
__param(1, (0, interface_api_1.Parameter)(chatbox_1.CHATBOX_PATH_PARAM, "param")),
|
|
40
|
-
__metadata("design:type", Function),
|
|
41
|
-
__metadata("design:paramtypes", [Object, String]),
|
|
42
|
-
__metadata("design:returntype", Promise)
|
|
43
|
-
], AIChatboxController.prototype, "asset", null);
|
|
44
|
-
exports.AIChatboxController = AIChatboxController = __decorate([
|
|
45
|
-
(0, auth_1.AuthOwnerOnly)()
|
|
46
|
-
], AIChatboxController);
|
|
47
|
-
//# sourceMappingURL=chatbox.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chatbox.js","sourceRoot":"","sources":["../../src/routes/chatbox.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,6DAOmC;AACnC,yDAA+D;AAC/D,wDAA0D;AAC1D,kDAM8B;AAOvB,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,IAAA,0BAAU,EAAC,8BAAoB,CAAC;IAEvE,QAAQ,CAAY,GAAmB;QACrC,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,4BAAkB,CAAC,CAAC;IACnD,CAAC;IAGD,KAAK,CACQ,GAAmB,EACU,IAAY;QAEpD,OAAO,IAAA,8BAAgB,EAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AAbY,kDAAmB;AAE9B;IADC,IAAA,mBAAG,EAAC,6BAAmB,CAAC;IACf,WAAA,IAAA,uBAAO,GAAE,CAAA;;;;mDAElB;AAGD;IADC,IAAA,mBAAG,EAAC,6BAAmB,CAAC;IAEtB,WAAA,IAAA,uBAAO,GAAE,CAAA;IACT,WAAA,IAAA,yBAAS,EAAC,4BAAkB,EAAE,OAAO,CAAC,CAAA;;;;gDAGxC;8BAZU,mBAAmB;IAD/B,IAAA,oBAAa,GAAE;GACH,mBAAmB,CAa/B"}
|
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
OVERLAY_BOX_SHADOW,
|
|
3
|
-
OVERLAY_DEFAULT_WIDTH_PX,
|
|
4
|
-
OVERLAY_DOM_ID,
|
|
5
|
-
OVERLAY_IFRAME_ID,
|
|
6
|
-
OVERLAY_MIN_WIDTH_PX,
|
|
7
|
-
OVERLAY_OPEN_CHANGE_EVENT,
|
|
8
|
-
OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS,
|
|
9
|
-
OVERLAY_PLACEHOLDER_ID,
|
|
10
|
-
OVERLAY_RESIZE_HANDLE_SIZE_PX,
|
|
11
|
-
OVERLAY_Z_INDEX,
|
|
12
|
-
PLACEHOLDER_CONNECTING_TEXT,
|
|
13
|
-
PLACEHOLDER_REVIVING_TEXT,
|
|
14
|
-
PLACEHOLDER_UNAVAILABLE_TEXT,
|
|
15
|
-
PLACEHOLDER_UNAVAILABLE_TITLE,
|
|
16
|
-
} from './constants'
|
|
17
|
-
import {
|
|
18
|
-
type OverlayPrefs,
|
|
19
|
-
readPrefs,
|
|
20
|
-
writePrefsDebounced,
|
|
21
|
-
} from './overlay-prefs'
|
|
22
|
-
import {
|
|
23
|
-
type HostTheme,
|
|
24
|
-
installThemeBridge,
|
|
25
|
-
isFromFrame,
|
|
26
|
-
readHostMode,
|
|
27
|
-
} from './theme-bridge'
|
|
28
|
-
|
|
29
|
-
// What the host-origin placeholder shows while the sidecar frame can't be shown.
|
|
30
|
-
// `connecting`/`reviving` are transient (spinner); `unavailable` is terminal.
|
|
31
|
-
export type PlaceholderKind = 'connecting' | 'reviving' | 'unavailable'
|
|
32
|
-
|
|
33
|
-
export interface OverlayHandle {
|
|
34
|
-
// Toggle the docked panel's slide-in open/closed state.
|
|
35
|
-
toggleOpen: () => void
|
|
36
|
-
// Launcher-specific toggle: ignores an open request that immediately follows
|
|
37
|
-
// an outside-click close, since the launcher's `click` fires after the same
|
|
38
|
-
// `pointerdown` that already closed the panel. The keyboard shortcut must
|
|
39
|
-
// use `toggleOpen` so it is never suppressed.
|
|
40
|
-
toggleFromLauncher: () => void
|
|
41
|
-
isOpen: () => boolean
|
|
42
|
-
/** Called on every open or close, however the panel was toggled. */
|
|
43
|
-
onOpenChange: (listener: (isOpen: boolean) => void) => () => void
|
|
44
|
-
// Point the frame at the chat document. A no-op when the URL is
|
|
45
|
-
// unchanged and already loaded, so a redundant `connected` notify won't reload
|
|
46
|
-
// the chatbox; otherwise it reloads and shows a connecting placeholder until
|
|
47
|
-
// the frame paints.
|
|
48
|
-
repoint: (url: string) => void
|
|
49
|
-
// Cover the frame with the host-origin status panel (never the browser's raw
|
|
50
|
-
// error page) while the sidecar is unreachable.
|
|
51
|
-
showPlaceholder: (kind: PlaceholderKind) => void
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface OverlayElements {
|
|
55
|
-
container: HTMLDivElement
|
|
56
|
-
iframe: HTMLIFrameElement
|
|
57
|
-
resizeHandle: HTMLDivElement
|
|
58
|
-
placeholder: HTMLDivElement
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
interface OverlayPrefsRef {
|
|
62
|
-
value: OverlayPrefs
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
interface MessageData {
|
|
66
|
-
type?: unknown
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const MAX_WIDTH_VW_RATIO = 0.94
|
|
70
|
-
// Message types accepted from the chatbox iframe to close the panel.
|
|
71
|
-
const CLOSE_MESSAGE_TYPES = new Set(['dms-ai:close'])
|
|
72
|
-
|
|
73
|
-
// Fallback backdrop shown behind the iframe while it slides in, before the
|
|
74
|
-
// bridge reports the host's real `--ui-bg`. Only briefly visible.
|
|
75
|
-
const BACKDROP_DARK = '#0a0b12'
|
|
76
|
-
const BACKDROP_LIGHT = '#ffffff'
|
|
77
|
-
|
|
78
|
-
function backdropFor(mode: 'dark' | 'light'): string {
|
|
79
|
-
return mode === 'dark' ? BACKDROP_DARK : BACKDROP_LIGHT
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function maxWidth(): number {
|
|
83
|
-
const vw = globalThis.innerWidth ?? OVERLAY_DEFAULT_WIDTH_PX * 3
|
|
84
|
-
return Math.max(OVERLAY_MIN_WIDTH_PX, Math.round(vw * MAX_WIDTH_VW_RATIO))
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function clampWidth(width: number): number {
|
|
88
|
-
return Math.min(Math.max(width, OVERLAY_MIN_WIDTH_PX), maxWidth())
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildContainer(): HTMLDivElement {
|
|
92
|
-
const container = document.createElement('div')
|
|
93
|
-
container.id = OVERLAY_DOM_ID
|
|
94
|
-
const style = container.style
|
|
95
|
-
style.position = 'fixed'
|
|
96
|
-
style.top = '0'
|
|
97
|
-
style.right = '0'
|
|
98
|
-
style.height = '100vh'
|
|
99
|
-
style.zIndex = String(OVERLAY_Z_INDEX)
|
|
100
|
-
style.boxShadow = OVERLAY_BOX_SHADOW
|
|
101
|
-
// Backdrop behind the iframe while it slides in — follow the host mode so a
|
|
102
|
-
// light-mode host doesn't flash a dark sheet. The iframe fills the container
|
|
103
|
-
// once painted, so this is only ever briefly visible.
|
|
104
|
-
style.background = backdropFor(readHostMode())
|
|
105
|
-
style.borderLeft = '1px solid rgba(140,160,220,0.20)'
|
|
106
|
-
style.overflow = 'hidden'
|
|
107
|
-
style.display = 'flex'
|
|
108
|
-
style.transition = 'transform 220ms cubic-bezier(0.16, 1, 0.3, 1)'
|
|
109
|
-
style.willChange = 'transform'
|
|
110
|
-
return container
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Built with no `src`: the frame is only pointed at the chat via `repoint` once
|
|
114
|
-
// the status controller confirms the sidecar runs, so it never renders the
|
|
115
|
-
// error page of a sidecar that is down.
|
|
116
|
-
function buildIframe(): HTMLIFrameElement {
|
|
117
|
-
const iframe = document.createElement('iframe')
|
|
118
|
-
iframe.id = OVERLAY_IFRAME_ID
|
|
119
|
-
iframe.style.flex = '1 1 auto'
|
|
120
|
-
iframe.style.width = '100%'
|
|
121
|
-
iframe.style.height = '100%'
|
|
122
|
-
iframe.style.border = '0'
|
|
123
|
-
iframe.style.colorScheme = readHostMode()
|
|
124
|
-
return iframe
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const SPINNER_STYLE_ID = 'dms-ai-overlay-spinner-style'
|
|
128
|
-
|
|
129
|
-
function ensureSpinnerKeyframes(): void {
|
|
130
|
-
if (document.getElementById(SPINNER_STYLE_ID) !== null) return
|
|
131
|
-
const style = document.createElement('style')
|
|
132
|
-
style.id = SPINNER_STYLE_ID
|
|
133
|
-
style.textContent =
|
|
134
|
-
'@keyframes dms-ai-spin{to{transform:rotate(360deg)}}'
|
|
135
|
-
document.head.appendChild(style)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function buildPlaceholder(): HTMLDivElement {
|
|
139
|
-
const placeholder = document.createElement('div')
|
|
140
|
-
placeholder.id = OVERLAY_PLACEHOLDER_ID
|
|
141
|
-
const style = placeholder.style
|
|
142
|
-
style.position = 'absolute'
|
|
143
|
-
style.inset = '0'
|
|
144
|
-
// Above the iframe, but transparent to pointers so the resize handle and the
|
|
145
|
-
// (harmless) dead frame beneath stay reachable.
|
|
146
|
-
style.zIndex = '2'
|
|
147
|
-
style.pointerEvents = 'none'
|
|
148
|
-
style.display = 'none'
|
|
149
|
-
style.flexDirection = 'column'
|
|
150
|
-
style.alignItems = 'center'
|
|
151
|
-
style.justifyContent = 'center'
|
|
152
|
-
style.gap = '14px'
|
|
153
|
-
style.padding = '24px'
|
|
154
|
-
style.textAlign = 'center'
|
|
155
|
-
style.font =
|
|
156
|
-
'500 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif'
|
|
157
|
-
style.background = backdropFor(readHostMode())
|
|
158
|
-
return placeholder
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function spinnerHtml(color: string): string {
|
|
162
|
-
return `<span style="width:22px;height:22px;border:2.5px solid ${color}33;border-top-color:${color};border-radius:50%;animation:dms-ai-spin 0.7s linear infinite;"></span>`
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function placeholderContent(kind: PlaceholderKind): string {
|
|
166
|
-
if (kind === 'unavailable') {
|
|
167
|
-
return `<div style="font-size:15px;font-weight:600;">${PLACEHOLDER_UNAVAILABLE_TITLE}</div><div style="opacity:0.7;">${PLACEHOLDER_UNAVAILABLE_TEXT}</div>`
|
|
168
|
-
}
|
|
169
|
-
const text =
|
|
170
|
-
kind === 'reviving'
|
|
171
|
-
? PLACEHOLDER_REVIVING_TEXT
|
|
172
|
-
: PLACEHOLDER_CONNECTING_TEXT
|
|
173
|
-
return `${spinnerHtml('currentColor')}<div style="opacity:0.75;">${text}</div>`
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function buildResizeHandle(): HTMLDivElement {
|
|
177
|
-
const handle = document.createElement('div')
|
|
178
|
-
const style = handle.style
|
|
179
|
-
style.position = 'absolute'
|
|
180
|
-
style.left = '0'
|
|
181
|
-
style.top = '0'
|
|
182
|
-
style.width = `${OVERLAY_RESIZE_HANDLE_SIZE_PX}px`
|
|
183
|
-
style.height = '100%'
|
|
184
|
-
style.cursor = 'ew-resize'
|
|
185
|
-
style.touchAction = 'none'
|
|
186
|
-
style.zIndex = '1'
|
|
187
|
-
return handle
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function applyPrefs(elements: OverlayElements, prefs: OverlayPrefs): void {
|
|
191
|
-
const width = clampWidth(prefs.width)
|
|
192
|
-
elements.container.style.width = `${width}px`
|
|
193
|
-
elements.container.style.transform = prefs.isOpen
|
|
194
|
-
? 'translateX(0)'
|
|
195
|
-
: 'translateX(100%)'
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function resolveInitialPrefs(): OverlayPrefs {
|
|
199
|
-
const stored = readPrefs()
|
|
200
|
-
if (stored !== null)
|
|
201
|
-
return { width: clampWidth(stored.width), isOpen: stored.isOpen }
|
|
202
|
-
return { width: OVERLAY_DEFAULT_WIDTH_PX, isOpen: false }
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
interface InteractionContext {
|
|
206
|
-
elements: OverlayElements
|
|
207
|
-
stateRef: OverlayPrefsRef
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function installWidthResize(ctx: InteractionContext): void {
|
|
211
|
-
const { resizeHandle, container, iframe } = ctx.elements
|
|
212
|
-
let startX = 0
|
|
213
|
-
let startWidth = 0
|
|
214
|
-
|
|
215
|
-
const onMove = (event: PointerEvent): void => {
|
|
216
|
-
// Panel is pinned right, so dragging left (smaller clientX) grows it.
|
|
217
|
-
const next = clampWidth(startWidth + (startX - event.clientX))
|
|
218
|
-
ctx.stateRef.value = { ...ctx.stateRef.value, width: next }
|
|
219
|
-
container.style.width = `${next}px`
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const onUp = (): void => {
|
|
223
|
-
globalThis.removeEventListener('pointermove', onMove)
|
|
224
|
-
globalThis.removeEventListener('pointerup', onUp)
|
|
225
|
-
// Restore the iframe's pointer capture now the drag is over.
|
|
226
|
-
iframe.style.pointerEvents = ''
|
|
227
|
-
writePrefsDebounced(ctx.stateRef.value)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
resizeHandle.addEventListener('pointerdown', (event: PointerEvent) => {
|
|
231
|
-
event.preventDefault()
|
|
232
|
-
startX = event.clientX
|
|
233
|
-
startWidth = ctx.stateRef.value.width
|
|
234
|
-
// While dragging, dragging over the iframe would let it swallow the
|
|
235
|
-
// pointer stream, so the parent never sees pointermove/up and the drag
|
|
236
|
-
// sticks. Make the iframe transparent to pointers for the drag's duration.
|
|
237
|
-
iframe.style.pointerEvents = 'none'
|
|
238
|
-
globalThis.addEventListener('pointermove', onMove)
|
|
239
|
-
globalThis.addEventListener('pointerup', onUp)
|
|
240
|
-
})
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Close the panel when the user clicks anywhere in the host page outside it.
|
|
244
|
-
// Clicks inside the iframe never reach the host document, so they can't close
|
|
245
|
-
// the panel. Returns a predicate telling whether an outside click just closed
|
|
246
|
-
// the panel: the header launcher toggles on `click`, which follows the same
|
|
247
|
-
// `pointerdown` that closed the panel — without this guard, clicking the
|
|
248
|
-
// launcher while open would close then immediately re-open it.
|
|
249
|
-
function installOutsideClickClose(ctx: InteractionContext): () => boolean {
|
|
250
|
-
let lastCloseAt = Number.NEGATIVE_INFINITY
|
|
251
|
-
document.addEventListener(
|
|
252
|
-
'pointerdown',
|
|
253
|
-
(event: PointerEvent) => {
|
|
254
|
-
if (!ctx.stateRef.value.isOpen) return
|
|
255
|
-
if (event.composedPath().includes(ctx.elements.container)) return
|
|
256
|
-
lastCloseAt = event.timeStamp
|
|
257
|
-
setOpen(ctx.elements, ctx.stateRef, false)
|
|
258
|
-
},
|
|
259
|
-
{ capture: true },
|
|
260
|
-
)
|
|
261
|
-
return (): boolean =>
|
|
262
|
-
performance.now() - lastCloseAt < OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function appendElements(elements: OverlayElements): void {
|
|
266
|
-
elements.container.appendChild(elements.iframe)
|
|
267
|
-
elements.container.appendChild(elements.placeholder)
|
|
268
|
-
elements.container.appendChild(elements.resizeHandle)
|
|
269
|
-
document.body.appendChild(elements.container)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
interface PlaceholderController {
|
|
273
|
-
show: (kind: PlaceholderKind) => void
|
|
274
|
-
hide: () => void
|
|
275
|
-
recolor: (theme: HostTheme) => void
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function createPlaceholderController(
|
|
279
|
-
placeholder: HTMLDivElement,
|
|
280
|
-
): PlaceholderController {
|
|
281
|
-
return {
|
|
282
|
-
show: (kind) => {
|
|
283
|
-
placeholder.innerHTML = placeholderContent(kind)
|
|
284
|
-
placeholder.style.display = 'flex'
|
|
285
|
-
},
|
|
286
|
-
hide: () => {
|
|
287
|
-
placeholder.style.display = 'none'
|
|
288
|
-
},
|
|
289
|
-
recolor: (theme) => {
|
|
290
|
-
placeholder.style.background =
|
|
291
|
-
theme.tokens['--ui-bg'] || backdropFor(theme.mode)
|
|
292
|
-
placeholder.style.color = theme.tokens['--ui-text'] || ''
|
|
293
|
-
},
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Repoint the frame at `url`, deferring the placeholder-hide until it paints so a
|
|
298
|
-
// reload of the chatbox never flashes the previous frame. A no-op reload when
|
|
299
|
-
// the URL is already loaded.
|
|
300
|
-
function createRepointer(
|
|
301
|
-
iframe: HTMLIFrameElement,
|
|
302
|
-
placeholder: PlaceholderController,
|
|
303
|
-
): (url: string) => void {
|
|
304
|
-
let currentUrl = ''
|
|
305
|
-
let loaded = false
|
|
306
|
-
iframe.addEventListener('load', () => {
|
|
307
|
-
if (iframe.src === '') return
|
|
308
|
-
loaded = true
|
|
309
|
-
placeholder.hide()
|
|
310
|
-
})
|
|
311
|
-
return (url: string): void => {
|
|
312
|
-
if (url === currentUrl && loaded) {
|
|
313
|
-
placeholder.hide()
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
currentUrl = url
|
|
317
|
-
loaded = false
|
|
318
|
-
placeholder.show('connecting')
|
|
319
|
-
iframe.src = url
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function setOpen(
|
|
324
|
-
elements: OverlayElements,
|
|
325
|
-
stateRef: OverlayPrefsRef,
|
|
326
|
-
isOpen: boolean,
|
|
327
|
-
): void {
|
|
328
|
-
const hasChanged = stateRef.value.isOpen !== isOpen
|
|
329
|
-
stateRef.value = { ...stateRef.value, isOpen }
|
|
330
|
-
applyPrefs(elements, stateRef.value)
|
|
331
|
-
writePrefsDebounced(stateRef.value)
|
|
332
|
-
if (!hasChanged) return
|
|
333
|
-
elements.container.dispatchEvent(
|
|
334
|
-
new CustomEvent(OVERLAY_OPEN_CHANGE_EVENT, { detail: isOpen }),
|
|
335
|
-
)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function injectOverlay(): OverlayHandle | null {
|
|
339
|
-
const isAlreadyInjected = document.getElementById(OVERLAY_DOM_ID) !== null
|
|
340
|
-
if (isAlreadyInjected) return null
|
|
341
|
-
ensureSpinnerKeyframes()
|
|
342
|
-
const elements: OverlayElements = {
|
|
343
|
-
container: buildContainer(),
|
|
344
|
-
iframe: buildIframe(),
|
|
345
|
-
resizeHandle: buildResizeHandle(),
|
|
346
|
-
placeholder: buildPlaceholder(),
|
|
347
|
-
}
|
|
348
|
-
appendElements(elements)
|
|
349
|
-
const placeholder = createPlaceholderController(elements.placeholder)
|
|
350
|
-
const repoint = createRepointer(elements.iframe, placeholder)
|
|
351
|
-
const stateRef = { value: resolveInitialPrefs() }
|
|
352
|
-
applyPrefs(elements, stateRef.value)
|
|
353
|
-
installWidthResize({ elements, stateRef })
|
|
354
|
-
const wasJustClosedByOutsideClick = installOutsideClickClose({
|
|
355
|
-
elements,
|
|
356
|
-
stateRef,
|
|
357
|
-
})
|
|
358
|
-
// Mirror the host's primary palette + light/dark mode into the iframe, and
|
|
359
|
-
// keep the panel backdrop / iframe color-scheme (and the status placeholder)
|
|
360
|
-
// following live mode changes (e.g. an OS-driven dark switch) so no light edge
|
|
361
|
-
// lingers until a reload.
|
|
362
|
-
installThemeBridge(elements.iframe, (theme) => {
|
|
363
|
-
elements.container.style.background =
|
|
364
|
-
theme.tokens['--ui-bg'] || backdropFor(theme.mode)
|
|
365
|
-
elements.iframe.style.colorScheme = theme.mode
|
|
366
|
-
placeholder.recolor(theme)
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
// The chatbox header's close button lives inside the iframe; it asks the
|
|
370
|
-
// host to slide the panel away via postMessage.
|
|
371
|
-
globalThis.addEventListener('message', (event: MessageEvent) => {
|
|
372
|
-
if (!isFromFrame(event, elements.iframe)) return
|
|
373
|
-
const type = (event.data as MessageData)?.type
|
|
374
|
-
if (typeof type === 'string' && CLOSE_MESSAGE_TYPES.has(type)) {
|
|
375
|
-
setOpen(elements, stateRef, false)
|
|
376
|
-
}
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
const toggle = (): void =>
|
|
380
|
-
setOpen(elements, stateRef, !stateRef.value.isOpen)
|
|
381
|
-
|
|
382
|
-
return {
|
|
383
|
-
toggleOpen: toggle,
|
|
384
|
-
toggleFromLauncher: (): void => {
|
|
385
|
-
const wouldOpen = !stateRef.value.isOpen
|
|
386
|
-
if (wouldOpen && wasJustClosedByOutsideClick()) return
|
|
387
|
-
toggle()
|
|
388
|
-
},
|
|
389
|
-
isOpen: (): boolean => stateRef.value.isOpen,
|
|
390
|
-
onOpenChange: (listener) => {
|
|
391
|
-
const onChange = (event: Event): void =>
|
|
392
|
-
listener((event as CustomEvent<boolean>).detail)
|
|
393
|
-
elements.container.addEventListener(OVERLAY_OPEN_CHANGE_EVENT, onChange)
|
|
394
|
-
return () =>
|
|
395
|
-
elements.container.removeEventListener(OVERLAY_OPEN_CHANGE_EVENT, onChange)
|
|
396
|
-
},
|
|
397
|
-
repoint,
|
|
398
|
-
showPlaceholder: (kind) => placeholder.show(kind),
|
|
399
|
-
}
|
|
400
|
-
}
|