@duckmind/dm-darwin-x64 0.32.9 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +14 -65
  3. package/extensions/dm-9router-ext/README.md +142 -0
  4. package/extensions/dm-9router-ext/package.json +45 -0
  5. package/extensions/dm-9router-ext/src/index.ts +541 -0
  6. package/extensions/dm-9router-ext/tsconfig.json +17 -0
  7. package/extensions/dm-subagents/package-lock.json +3 -3
  8. package/extensions/dm-tasks/node_modules/.package-lock.json +3 -3
  9. package/extensions/dm-tasks/node_modules/typebox/build/type/script/mapping.d.mts +2 -2
  10. package/extensions/dm-tasks/node_modules/typebox/build/type/script/parser.d.mts +2 -2
  11. package/extensions/dm-tasks/node_modules/typebox/build/type/script/parser.mjs +2 -2
  12. package/extensions/dm-tasks/node_modules/typebox/build/type/types/number.d.mts +1 -1
  13. package/extensions/dm-tasks/node_modules/typebox/build/type/types/number.mjs +1 -1
  14. package/extensions/dm-tasks/node_modules/typebox/build/type/types/record.d.mts +1 -1
  15. package/extensions/dm-tasks/node_modules/typebox/package.json +1 -1
  16. package/extensions/dm-tasks/package-lock.json +3 -3
  17. package/package.json +1 -1
  18. package/theme/theme-alps.json +93 -0
  19. package/extensions/dm-chime/README.md +0 -11
  20. package/extensions/dm-chime/docs/protocols.md +0 -107
  21. package/extensions/dm-chime/index.ts +0 -205
  22. package/extensions/dm-chime/package.json +0 -33
  23. package/extensions/dm-phone/README.md +0 -24
  24. package/extensions/dm-phone/index.ts +0 -12
  25. package/extensions/dm-phone/node_modules/.package-lock.json +0 -29
  26. package/extensions/dm-phone/node_modules/ws/LICENSE +0 -20
  27. package/extensions/dm-phone/node_modules/ws/README.md +0 -548
  28. package/extensions/dm-phone/node_modules/ws/browser.js +0 -8
  29. package/extensions/dm-phone/node_modules/ws/index.js +0 -22
  30. package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +0 -131
  31. package/extensions/dm-phone/node_modules/ws/lib/constants.js +0 -19
  32. package/extensions/dm-phone/node_modules/ws/lib/event-target.js +0 -292
  33. package/extensions/dm-phone/node_modules/ws/lib/extension.js +0 -203
  34. package/extensions/dm-phone/node_modules/ws/lib/limiter.js +0 -55
  35. package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +0 -528
  36. package/extensions/dm-phone/node_modules/ws/lib/receiver.js +0 -760
  37. package/extensions/dm-phone/node_modules/ws/lib/sender.js +0 -607
  38. package/extensions/dm-phone/node_modules/ws/lib/stream.js +0 -161
  39. package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +0 -62
  40. package/extensions/dm-phone/node_modules/ws/lib/validation.js +0 -152
  41. package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +0 -562
  42. package/extensions/dm-phone/node_modules/ws/lib/websocket.js +0 -1407
  43. package/extensions/dm-phone/node_modules/ws/package.json +0 -70
  44. package/extensions/dm-phone/node_modules/ws/wrapper.mjs +0 -21
  45. package/extensions/dm-phone/package-lock.json +0 -66
  46. package/extensions/dm-phone/package.json +0 -35
  47. package/extensions/dm-phone/phone-session-pool.ts +0 -8
  48. package/extensions/dm-phone/public/app/attachments.js +0 -233
  49. package/extensions/dm-phone/public/app/autocomplete-controller.js +0 -81
  50. package/extensions/dm-phone/public/app/autocomplete.js +0 -135
  51. package/extensions/dm-phone/public/app/bindings.js +0 -178
  52. package/extensions/dm-phone/public/app/command-catalog.js +0 -76
  53. package/extensions/dm-phone/public/app/commands.js +0 -376
  54. package/extensions/dm-phone/public/app/constants.js +0 -60
  55. package/extensions/dm-phone/public/app/formatters.js +0 -131
  56. package/extensions/dm-phone/public/app/handlers.js +0 -442
  57. package/extensions/dm-phone/public/app/main.js +0 -6
  58. package/extensions/dm-phone/public/app/markdown.js +0 -105
  59. package/extensions/dm-phone/public/app/messages.js +0 -418
  60. package/extensions/dm-phone/public/app/sheet-actions.js +0 -113
  61. package/extensions/dm-phone/public/app/sheet-navigation.js +0 -19
  62. package/extensions/dm-phone/public/app/sheets-view.js +0 -287
  63. package/extensions/dm-phone/public/app/state.js +0 -95
  64. package/extensions/dm-phone/public/app/tool-rendering.js +0 -562
  65. package/extensions/dm-phone/public/app/transport.js +0 -176
  66. package/extensions/dm-phone/public/app/ui.js +0 -417
  67. package/extensions/dm-phone/public/app.js +0 -1
  68. package/extensions/dm-phone/public/icon.svg +0 -15
  69. package/extensions/dm-phone/public/index.html +0 -146
  70. package/extensions/dm-phone/public/manifest.webmanifest +0 -17
  71. package/extensions/dm-phone/public/styles.css +0 -1139
  72. package/extensions/dm-phone/public/sw.js +0 -78
  73. package/extensions/dm-phone/src/extension/duckmind-models.js +0 -264
  74. package/extensions/dm-phone/src/extension/phone-args.ts +0 -121
  75. package/extensions/dm-phone/src/extension/phone-paths.ts +0 -250
  76. package/extensions/dm-phone/src/extension/phone-quota.ts +0 -188
  77. package/extensions/dm-phone/src/extension/phone-runtime.ts +0 -154
  78. package/extensions/dm-phone/src/extension/phone-server-runtime.ts +0 -1217
  79. package/extensions/dm-phone/src/extension/phone-sessions.ts +0 -139
  80. package/extensions/dm-phone/src/extension/phone-static.ts +0 -30
  81. package/extensions/dm-phone/src/extension/phone-tailscale.ts +0 -148
  82. package/extensions/dm-phone/src/extension/phone-theme.ts +0 -85
  83. package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +0 -112
  84. package/extensions/dm-phone/src/extension/register-phone-extension.ts +0 -106
  85. package/extensions/dm-phone/src/extension/types.ts +0 -73
  86. package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +0 -882
  87. package/extensions/dm-phone/src/session-pool/session-pool.ts +0 -470
  88. package/extensions/dm-phone/src/session-pool/session-worker.ts +0 -739
  89. package/extensions/dm-phone/src/session-pool/types.ts +0 -111
  90. package/extensions/dm-phone/src/session-pool/utils.ts +0 -23
  91. package/extensions/dm-phone/test/duckmind-models.test.js +0 -147
  92. package/extensions/dm-thinking-timer/LICENSE +0 -21
  93. package/extensions/dm-thinking-timer/README.md +0 -7
  94. package/extensions/dm-thinking-timer/package.json +0 -20
  95. package/extensions/dm-thinking-timer/thinking-timer.ts +0 -250
@@ -1,178 +0,0 @@
1
- import { addAttachments, clearAttachments, removeAttachment, renderAttachmentStrip, syncAttachmentsWithPrompt } from "./attachments.js";
2
- import { renderCommandSuggestions } from "./autocomplete-controller.js";
3
- import { applyAutocompleteItem, submitPrompt } from "./commands.js";
4
- import { el, state } from "./state.js";
5
- import { handleSheetButtonAction, sheetButtonActionKey } from "./sheet-actions.js";
6
- import { closeSheet, openSheet } from "./sheet-navigation.js";
7
- import { renderSheet } from "./sheets-view.js";
8
- import { connectSocket, refreshAll, sendRpc } from "./transport.js";
9
- import {
10
- autoResizeTextarea,
11
- closeTokenModal,
12
- isNearBottom,
13
- scheduleComposerLayoutSync,
14
- scrollMessagesToBottom,
15
- setFollowLatest,
16
- showToast,
17
- storeToken,
18
- } from "./ui.js";
19
- import { insertCdCommand } from "./autocomplete.js";
20
-
21
- export function initializeBindings({ handleEnvelope, handleAuthFailure }) {
22
- el.promptInput.addEventListener("input", () => {
23
- syncAttachmentsWithPrompt();
24
- autoResizeTextarea();
25
- renderCommandSuggestions();
26
- });
27
-
28
- el.promptInput.addEventListener("click", () => {
29
- renderCommandSuggestions();
30
- });
31
-
32
- el.promptInput.addEventListener("keyup", (event) => {
33
- if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
34
- renderCommandSuggestions();
35
- }
36
- });
37
-
38
- autoResizeTextarea();
39
- renderCommandSuggestions();
40
- renderAttachmentStrip();
41
- scheduleComposerLayoutSync();
42
-
43
- if ("ResizeObserver" in window && el.composerWrap) {
44
- const composerResizeObserver = new ResizeObserver(() => scheduleComposerLayoutSync());
45
- composerResizeObserver.observe(el.composerWrap);
46
- }
47
-
48
- window.addEventListener("resize", scheduleComposerLayoutSync, { passive: true });
49
- window.visualViewport?.addEventListener("resize", scheduleComposerLayoutSync, { passive: true });
50
-
51
- const noteUserScrollIntent = () => {
52
- state.lastUserScrollIntentAt = Date.now();
53
- };
54
-
55
- const syncFollowLatestOnScroll = () => {
56
- const now = Date.now();
57
- const fromUser = now - state.lastUserScrollIntentAt < 400;
58
- if (!fromUser && now < state.ignoreScrollTrackingUntil) return;
59
- setFollowLatest(isNearBottom());
60
- };
61
-
62
- window.addEventListener("wheel", noteUserScrollIntent, { passive: true });
63
- window.addEventListener("touchmove", noteUserScrollIntent, { passive: true });
64
- window.addEventListener("scroll", syncFollowLatestOnScroll, { passive: true });
65
-
66
- el.refreshButton.addEventListener("click", refreshAll);
67
- el.abortButton.addEventListener("click", () => sendRpc({ type: "abort" }));
68
- el.stopButton?.addEventListener("click", () => sendRpc({ type: "abort" }));
69
- el.jumpToLatestButton?.addEventListener("click", () => {
70
- setFollowLatest(true);
71
- scrollMessagesToBottom({ force: true, behavior: "smooth" });
72
- });
73
- el.actionsButton.addEventListener("click", () => openSheet("actions"));
74
- el.insertCommandButton.addEventListener("click", () => openSheet("commands"));
75
- el.cdCommandButton?.addEventListener("click", () => {
76
- insertCdCommand();
77
- renderCommandSuggestions();
78
- });
79
- el.sessionSidebarButton.addEventListener("click", () => openSheet("active-sessions"));
80
- el.treeBrowserButton.addEventListener("click", () => openSheet("tree"));
81
- el.steerButton.addEventListener("click", () => submitPrompt({ steer: true }));
82
- el.sendButton.addEventListener("click", () => submitPrompt());
83
- el.sheetCloseButton.addEventListener("click", closeSheet);
84
- el.sheetSavedSessionsButton?.addEventListener("click", () => openSheet("sessions"));
85
- el.attachImageButton.addEventListener("click", () => el.imageInput.click());
86
- el.imageInput.addEventListener("change", (event) => {
87
- addAttachments(event.target.files);
88
- renderCommandSuggestions();
89
- el.imageInput.value = "";
90
- });
91
-
92
- el.attachmentStrip.addEventListener("click", (event) => {
93
- const button = event.target.closest("[data-remove-attachment]");
94
- if (!button) return;
95
- removeAttachment(button.getAttribute("data-remove-attachment"));
96
- renderCommandSuggestions();
97
- });
98
-
99
- el.commandStrip.addEventListener("click", (event) => {
100
- const button = event.target.closest("[data-autocomplete-index]");
101
- if (!button) return;
102
-
103
- const index = Number(button.getAttribute("data-autocomplete-index"));
104
- if (!Number.isFinite(index) || index < 0) return;
105
- applyAutocompleteItem(state.autocompleteItems[index]);
106
- });
107
-
108
- el.sheetContent.addEventListener("change", (event) => {
109
- if (!(event.target instanceof HTMLSelectElement)) return;
110
- if (!event.target.hasAttribute("data-command-category-select")) return;
111
- state.commandSheetCategory = event.target.value;
112
- renderSheet();
113
- });
114
-
115
- el.sheetContent.addEventListener("pointerdown", (event) => {
116
- const button = event.target.closest("button");
117
- if (!button) return;
118
-
119
- const sheetAction = button.getAttribute("data-sheet-action") || "";
120
- const shouldHandleEarly = button.hasAttribute("data-active-session-id") || ["new-parent-session", "new-parallel-session"].includes(sheetAction);
121
- if (!shouldHandleEarly) return;
122
-
123
- event.preventDefault();
124
- const actionKey = sheetButtonActionKey(button);
125
- state.lastSheetPointerAction = actionKey;
126
- state.lastSheetPointerActionAt = Date.now();
127
- handleSheetButtonAction(button);
128
- });
129
-
130
- el.sheetContent.addEventListener("click", (event) => {
131
- const button = event.target.closest("button");
132
- if (!button) return;
133
-
134
- const actionKey = sheetButtonActionKey(button);
135
- if (state.lastSheetPointerAction === actionKey && Date.now() - state.lastSheetPointerActionAt < 800) {
136
- state.lastSheetPointerAction = "";
137
- state.lastSheetPointerActionAt = 0;
138
- return;
139
- }
140
-
141
- handleSheetButtonAction(button);
142
- });
143
-
144
- el.tokenSaveButton.addEventListener("click", () => {
145
- const nextToken = el.tokenInput.value.trim();
146
- if (!nextToken) {
147
- showToast("Enter the current /phone-start token.", "error");
148
- el.tokenInput.focus();
149
- return;
150
- }
151
-
152
- state.token = nextToken;
153
- storeToken(state.token);
154
- closeTokenModal();
155
- connectSocket({ handleEnvelope, handleAuthFailure });
156
- });
157
-
158
- el.tokenInput.addEventListener("keydown", (event) => {
159
- if (event.key === "Enter") {
160
- event.preventDefault();
161
- el.tokenSaveButton.click();
162
- }
163
- });
164
-
165
- document.addEventListener("toggle", (event) => {
166
- const details = event.target;
167
- if (!(details instanceof HTMLDetailsElement)) return;
168
- const itemId = details.getAttribute("data-tool-panel");
169
- if (!itemId) return;
170
- state.toolPanelOpen.set(itemId, details.open);
171
- }, true);
172
-
173
- window.addEventListener("beforeunload", () => {
174
- state.manuallyClosed = true;
175
- if (state.socket) state.socket.close();
176
- clearAttachments();
177
- });
178
- }
@@ -1,76 +0,0 @@
1
- import { COMMAND_CATEGORY_ORDER, LOCAL_COMMAND_DEFINITIONS } from "./constants.js";
2
- import { state } from "./state.js";
3
-
4
- function compareCommandNames(left, right) {
5
- return String(left?.name || "").localeCompare(String(right?.name || ""));
6
- }
7
-
8
- export function sortCommandCategories(categories = []) {
9
- return [...categories].sort((left, right) => {
10
- const leftIndex = COMMAND_CATEGORY_ORDER.indexOf(left);
11
- const rightIndex = COMMAND_CATEGORY_ORDER.indexOf(right);
12
- const normalizedLeftIndex = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
13
- const normalizedRightIndex = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
14
-
15
- if (normalizedLeftIndex !== normalizedRightIndex) return normalizedLeftIndex - normalizedRightIndex;
16
- return String(left || "").localeCompare(String(right || ""));
17
- });
18
- }
19
-
20
- export function localCommandCatalog() {
21
- return LOCAL_COMMAND_DEFINITIONS.map((command) => ({
22
- name: command.name,
23
- description: command.description,
24
- source: "local",
25
- insertOnly: Boolean(command.insertOnly),
26
- }));
27
- }
28
-
29
- export function visibleCommandCatalog() {
30
- const localCommands = localCommandCatalog();
31
- const localNames = new Set(localCommands.map((command) => command.name));
32
- return [
33
- ...localCommands,
34
- ...state.commands.filter((command) => !localNames.has(command.name)),
35
- ];
36
- }
37
-
38
- export function findLocalCommandDefinition(name) {
39
- return LOCAL_COMMAND_DEFINITIONS.find((command) => command.name === name) || null;
40
- }
41
-
42
- export function groupedCommands() {
43
- const groups = new Map();
44
- const merged = visibleCommandCatalog();
45
-
46
- for (const command of merged) {
47
- const key = command.source || "command";
48
- if (!groups.has(key)) groups.set(key, []);
49
- groups.get(key).push(command);
50
- }
51
-
52
- for (const commands of groups.values()) {
53
- commands.sort(compareCommandNames);
54
- }
55
-
56
- return new Map(sortCommandCategories([...groups.keys()]).map((category) => [category, groups.get(category) || []]));
57
- }
58
-
59
- export function commandCategoryLabel(category = "") {
60
- if (!category) return "Commands";
61
- return category.charAt(0).toUpperCase() + category.slice(1);
62
- }
63
-
64
- export function selectedCommandCategory(categories = []) {
65
- if (!categories.length) {
66
- state.commandSheetCategory = "";
67
- return "";
68
- }
69
-
70
- if (categories.includes(state.commandSheetCategory)) {
71
- return state.commandSheetCategory;
72
- }
73
-
74
- state.commandSheetCategory = categories[0];
75
- return state.commandSheetCategory;
76
- }
@@ -1,376 +0,0 @@
1
- import { buildPromptPayload, clearAttachments, syncAttachmentsWithPrompt } from "./attachments.js";
2
- import { insertCdCommand, replacePromptRange } from "./autocomplete.js";
3
- import { renderCommandSuggestions } from "./autocomplete-controller.js";
4
- import { LOCAL_COMMAND_NAMES, THINKING_LEVELS } from "./constants.js";
5
- import { findLocalCommandDefinition } from "./command-catalog.js";
6
- import { el, state } from "./state.js";
7
- import { openSheet } from "./sheet-navigation.js";
8
- import { refreshAll, requestReload, sendLocalCommand, sendRpc } from "./transport.js";
9
- import { autoResizeTextarea, renderHeader, setFollowLatest, showToast } from "./ui.js";
10
- import { clearSnapshotView, renderMessages } from "./messages.js";
11
-
12
- function parseLocalCommandInput(text) {
13
- const match = String(text || "").match(/^\/(\S+)(?:\s+([\s\S]*))?$/);
14
- if (!match) return null;
15
- return {
16
- name: match[1] || "",
17
- args: match[2] || "",
18
- };
19
- }
20
-
21
- function parseSlashCommandText(text) {
22
- const value = String(text || "").trim();
23
- if (!value.startsWith("/")) return null;
24
-
25
- const body = value.slice(1).trim();
26
- if (!body) return null;
27
-
28
- const spaceIndex = body.indexOf(" ");
29
- const name = spaceIndex === -1 ? body : body.slice(0, spaceIndex);
30
-
31
- return {
32
- text: `/${body}`,
33
- name,
34
- };
35
- }
36
-
37
- function findRemoteSlashCommand(text) {
38
- const parsed = parseSlashCommandText(text);
39
- if (!parsed) return null;
40
-
41
- const match = state.commands.find((command) => command.name === parsed.name);
42
- if (!match) return null;
43
-
44
- return {
45
- ...parsed,
46
- source: match.source || "extension",
47
- };
48
- }
49
-
50
- function sendRemoteSlashCommand(command, { images = [], steer = false } = {}) {
51
- if (command.source === "extension" && images.length > 0) {
52
- showToast("Extension slash commands do not support image attachments.", "error");
53
- return "blocked";
54
- }
55
-
56
- const streaming = Boolean(state.status?.isStreaming || state.snapshotState?.isStreaming);
57
- const sent = sendLocalCommand({
58
- type: "slash-command",
59
- text: command.text,
60
- ...(images.length ? { images } : {}),
61
- ...(command.source !== "extension"
62
- ? steer
63
- ? { streamingBehavior: "steer" }
64
- : streaming
65
- ? { streamingBehavior: "followUp" }
66
- : {}
67
- : {}),
68
- });
69
-
70
- return sent ? "handled" : false;
71
- }
72
-
73
- const INLINE_IMAGE_TOKEN_PATTERN = /⟦img\d+⟧|\{img\d*\}/g;
74
-
75
- function buildInlineDisplayContent(text, images = []) {
76
- const value = String(text || "");
77
- if (!images.length) return value;
78
-
79
- INLINE_IMAGE_TOKEN_PATTERN.lastIndex = 0;
80
- const matches = [...value.matchAll(INLINE_IMAGE_TOKEN_PATTERN)];
81
- if (!matches.length) return value;
82
-
83
- const content = [];
84
- let lastIndex = 0;
85
- let imageIndex = 0;
86
-
87
- for (const match of matches) {
88
- const token = match[0] || "";
89
- const index = match.index ?? -1;
90
- if (index < 0) continue;
91
-
92
- const before = value.slice(lastIndex, index);
93
- if (before) {
94
- content.push({ type: "text", text: before });
95
- }
96
-
97
- const image = images[imageIndex];
98
- if (image?.type === "image" && image.data && image.mimeType) {
99
- content.push({
100
- type: "image",
101
- data: image.data,
102
- mimeType: image.mimeType,
103
- });
104
- imageIndex += 1;
105
- } else {
106
- content.push({ type: "text", text: token });
107
- }
108
-
109
- lastIndex = index + token.length;
110
- }
111
-
112
- const after = value.slice(lastIndex);
113
- if (after) {
114
- content.push({ type: "text", text: after });
115
- }
116
-
117
- while (imageIndex < images.length) {
118
- const image = images[imageIndex];
119
- if (image?.type === "image" && image.data && image.mimeType) {
120
- content.push({
121
- type: "image",
122
- data: image.data,
123
- mimeType: image.mimeType,
124
- });
125
- }
126
- imageIndex += 1;
127
- }
128
-
129
- return content;
130
- }
131
-
132
- export function insertSlashCommand(commandName) {
133
- el.promptInput.value = `/${commandName} `;
134
- autoResizeTextarea();
135
- renderCommandSuggestions();
136
- el.promptInput.focus();
137
- }
138
-
139
- export function tryHandleLocalCommand(text, { hasAttachments = false } = {}) {
140
- if (!text.startsWith("/")) return false;
141
- const parsed = parseLocalCommandInput(text);
142
- if (!parsed?.name) return false;
143
- const { name, args } = parsed;
144
-
145
- if (hasAttachments && LOCAL_COMMAND_NAMES.has(name)) {
146
- showToast("Local phone commands do not support image attachments.", "error");
147
- return "blocked";
148
- }
149
-
150
- if (name === "new") {
151
- sendRpc({ type: "new_session" });
152
- return "handled";
153
- }
154
- if (name === "compact") {
155
- sendRpc({ type: "compact" });
156
- return "handled";
157
- }
158
- if (name === "reload") {
159
- return requestReload() ? "handled" : "blocked";
160
- }
161
- if (name === "refresh") {
162
- refreshAll();
163
- return "handled";
164
- }
165
- if (name === "stats" || name === "cost") {
166
- openSheet("actions");
167
- sendRpc({ type: "get_session_stats" });
168
- return "handled";
169
- }
170
- if (name === "commands") {
171
- openSheet("commands");
172
- return "handled";
173
- }
174
- if (name === "sessions") {
175
- openSheet("sessions");
176
- return "handled";
177
- }
178
- if (name === "tree") {
179
- openSheet("tree");
180
- return "handled";
181
- }
182
- if (name === "cd") {
183
- return sendLocalCommand({ type: "cd", args }) ? "handled" : "blocked";
184
- }
185
- if (name === "thinking") {
186
- if (args && THINKING_LEVELS.includes(args)) {
187
- sendRpc({ type: "set_thinking_level", level: args });
188
- } else {
189
- openSheet("thinking");
190
- }
191
- return "handled";
192
- }
193
- if (name === "model") {
194
- if (args) {
195
- const normalizedArgs = args.trim().toLowerCase();
196
- const [provider, modelId] = normalizedArgs.includes("/") ? normalizedArgs.split("/", 2) : [null, normalizedArgs];
197
- const match = state.models.find((model) => {
198
- const optionProvider = String(model.provider || "").toLowerCase();
199
- const optionId = String(model.id || "").toLowerCase();
200
- const optionName = String(model.name || "").toLowerCase();
201
- return provider ? optionProvider === provider && optionId === modelId : optionId === modelId || optionName === normalizedArgs;
202
- });
203
- if (match) {
204
- sendRpc({ type: "set_model", provider: match.provider, modelId: match.id });
205
- } else {
206
- openSheet("models");
207
- sendRpc({ type: "get_available_models" });
208
- showToast("Model not found locally. Pick one from the sheet.", "error");
209
- }
210
- } else {
211
- openSheet("models");
212
- sendRpc({ type: "get_available_models" });
213
- }
214
- return "handled";
215
- }
216
- return false;
217
- }
218
-
219
- export function applyAutocompleteItem(item) {
220
- const context = state.autocompleteContext;
221
- if (!item) return;
222
-
223
- if (item.kind === "local-command-run") {
224
- const result = tryHandleLocalCommand(`/${item.name}`, { hasAttachments: state.attachments.length > 0 });
225
- if (result === "handled") {
226
- el.promptInput.value = "";
227
- autoResizeTextarea();
228
- renderCommandSuggestions();
229
- }
230
- return;
231
- }
232
-
233
- if (item.kind === "local-command-insert" || item.kind === "remote-command-insert") {
234
- insertSlashCommand(item.name);
235
- return;
236
- }
237
-
238
- if (!context || context.type !== "path") return;
239
-
240
- if (context.mode === "mention") {
241
- const suffix = item.isDirectory ? "" : " ";
242
- replacePromptRange(context.replaceStart, context.replaceEnd, `@${item.value}${suffix}`);
243
- renderCommandSuggestions();
244
- return;
245
- }
246
-
247
- const suffix = item.isDirectory ? "" : " ";
248
- replacePromptRange(context.replaceStart, context.replaceEnd, `${item.value}${suffix}`);
249
- renderCommandSuggestions();
250
- }
251
-
252
- export async function submitPrompt({ steer = false } = {}) {
253
- syncAttachmentsWithPrompt();
254
-
255
- const rawPrompt = el.promptInput.value;
256
- const commandText = rawPrompt.trim();
257
- if (!commandText && state.attachments.length === 0) return;
258
-
259
- const localCommandResult = !steer && commandText
260
- ? tryHandleLocalCommand(commandText, { hasAttachments: state.attachments.length > 0 })
261
- : false;
262
-
263
- if (localCommandResult) {
264
- if (localCommandResult === "handled") {
265
- el.promptInput.value = "";
266
- autoResizeTextarea();
267
- renderCommandSuggestions();
268
- }
269
- return;
270
- }
271
-
272
- let promptPayload = { message: rawPrompt, images: [] };
273
- try {
274
- promptPayload = await buildPromptPayload(rawPrompt);
275
- } catch (error) {
276
- showToast(error instanceof Error ? error.message : "Failed to read images", "error");
277
- return;
278
- }
279
-
280
- const message = promptPayload.message.trim();
281
- const images = promptPayload.images;
282
- const remoteSlashCommand = message ? findRemoteSlashCommand(message) : null;
283
- if (remoteSlashCommand) {
284
- const remoteCommandResult = sendRemoteSlashCommand(remoteSlashCommand, { images, steer });
285
- if (remoteCommandResult) {
286
- if (remoteCommandResult === "handled") {
287
- el.promptInput.value = "";
288
- autoResizeTextarea();
289
- renderCommandSuggestions();
290
- clearAttachments();
291
- }
292
- return;
293
- }
294
- }
295
-
296
- const streaming = Boolean(state.status?.isStreaming || state.snapshotState?.isStreaming);
297
- sendRpc({
298
- type: "prompt",
299
- message,
300
- ...(steer ? { streamingBehavior: "steer" } : streaming ? { streamingBehavior: "followUp" } : {}),
301
- ...(images.length ? { images } : {}),
302
- });
303
-
304
- state.messages.push({
305
- id: `local-user-${Date.now()}`,
306
- kind: "user",
307
- meta: "just now",
308
- text: message || "(image prompt)",
309
- rawContent: buildInlineDisplayContent(rawPrompt, images),
310
- imageCount: images.length,
311
- });
312
- setFollowLatest(true);
313
- renderMessages({ forceScroll: true });
314
- el.promptInput.value = "";
315
- autoResizeTextarea();
316
- renderCommandSuggestions();
317
- clearAttachments();
318
- }
319
-
320
- export function handleInsertOnlyLocalCommand(commandName) {
321
- const definition = findLocalCommandDefinition(commandName);
322
- if (!definition?.insertOnly) return false;
323
-
324
- if (commandName === "cd") {
325
- insertCdCommand();
326
- renderCommandSuggestions();
327
- } else {
328
- insertSlashCommand(commandName);
329
- }
330
-
331
- return true;
332
- }
333
-
334
- export function prepareSessionSelection(sessionId) {
335
- if (state.socket?.readyState !== WebSocket.OPEN) {
336
- showToast("Not connected to DM.", "error");
337
- return false;
338
- }
339
-
340
- clearSnapshotView();
341
- setFollowLatest(true);
342
- renderHeader();
343
- renderMessages({ forceScroll: true });
344
- state.socket.send(JSON.stringify({ kind: "session-select", sessionId }));
345
- return true;
346
- }
347
-
348
- export function prepareParentSessionNew() {
349
- if (state.socket?.readyState !== WebSocket.OPEN) {
350
- showToast("Not connected to DM.", "error");
351
- return false;
352
- }
353
-
354
- clearSnapshotView();
355
- setFollowLatest(true);
356
- renderHeader();
357
- renderMessages({ forceScroll: true });
358
- showToast("Starting new parent session…");
359
- state.socket.send(JSON.stringify({ kind: "session-parent-new" }));
360
- return true;
361
- }
362
-
363
- export function prepareSessionSpawn() {
364
- if (state.socket?.readyState !== WebSocket.OPEN) {
365
- showToast("Not connected to DM.", "error");
366
- return false;
367
- }
368
-
369
- clearSnapshotView();
370
- setFollowLatest(true);
371
- renderHeader();
372
- renderMessages({ forceScroll: true });
373
- showToast("Opening new parallel session…");
374
- state.socket.send(JSON.stringify({ kind: "session-spawn" }));
375
- return true;
376
- }
@@ -1,60 +0,0 @@
1
- export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
2
-
3
- export const LOCAL_COMMAND_DEFINITIONS = [
4
- { name: "new", description: "Start a new session" },
5
- { name: "compact", description: "Compact the current session" },
6
- { name: "reload", description: "Reload extensions, skills, prompts, and themes" },
7
- { name: "stats", description: "Show session stats" },
8
- { name: "cost", description: "Show session cost stats" },
9
- { name: "model", description: "Open model picker" },
10
- { name: "thinking", description: "Open thinking level picker" },
11
- { name: "commands", description: "Browse commands, skills, and prompts" },
12
- { name: "sessions", description: "Browse saved sessions" },
13
- { name: "tree", description: "Browse the current session tree" },
14
- { name: "cd", description: "Change DM working directory", insertOnly: true },
15
- { name: "refresh", description: "Refresh snapshot" },
16
- ];
17
-
18
- export const LOCAL_COMMAND_NAMES = new Set(LOCAL_COMMAND_DEFINITIONS.map((command) => command.name));
19
- export const COMMAND_CATEGORY_ORDER = ["local", "extension", "prompt", "skill"];
20
- export const AUTOCOMPLETE_DELIMITERS = new Set([" ", "\t", "\n", '"', "'", "="]);
21
- export const TOKEN_STORAGE_KEY = "dm-phone-token";
22
-
23
- export const THEME_CSS_VARIABLES = {
24
- mdCode: "--md-code",
25
- mdCodeBlock: "--md-code-block",
26
- mdCodeBlockBorder: "--md-code-block-border",
27
- };
28
-
29
- export const TOOL_LANGUAGE_LABELS = {
30
- c: "C",
31
- cc: "C++",
32
- cpp: "C++",
33
- css: "CSS",
34
- go: "Go",
35
- h: "Header",
36
- hpp: "C++",
37
- html: "HTML",
38
- java: "Java",
39
- js: "JS",
40
- jsx: "JSX",
41
- json: "JSON",
42
- kt: "Kotlin",
43
- md: "Markdown",
44
- mjs: "JS",
45
- php: "PHP",
46
- py: "Python",
47
- rb: "Ruby",
48
- rs: "Rust",
49
- scss: "SCSS",
50
- sh: "Shell",
51
- sql: "SQL",
52
- swift: "Swift",
53
- toml: "TOML",
54
- ts: "TypeScript",
55
- tsx: "TSX",
56
- txt: "Text",
57
- yaml: "YAML",
58
- yml: "YAML",
59
- zsh: "Shell",
60
- };