@duckmind/dm-darwin-arm64 0.33.0 → 0.33.2

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 (81) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +1 -76
  3. package/package.json +1 -1
  4. package/theme/theme-alps.json +84 -0
  5. package/extensions/dm-chime/README.md +0 -11
  6. package/extensions/dm-chime/docs/protocols.md +0 -107
  7. package/extensions/dm-chime/index.ts +0 -205
  8. package/extensions/dm-chime/package.json +0 -33
  9. package/extensions/dm-phone/README.md +0 -24
  10. package/extensions/dm-phone/index.ts +0 -12
  11. package/extensions/dm-phone/node_modules/.package-lock.json +0 -29
  12. package/extensions/dm-phone/node_modules/ws/LICENSE +0 -20
  13. package/extensions/dm-phone/node_modules/ws/README.md +0 -548
  14. package/extensions/dm-phone/node_modules/ws/browser.js +0 -8
  15. package/extensions/dm-phone/node_modules/ws/index.js +0 -22
  16. package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +0 -131
  17. package/extensions/dm-phone/node_modules/ws/lib/constants.js +0 -19
  18. package/extensions/dm-phone/node_modules/ws/lib/event-target.js +0 -292
  19. package/extensions/dm-phone/node_modules/ws/lib/extension.js +0 -203
  20. package/extensions/dm-phone/node_modules/ws/lib/limiter.js +0 -55
  21. package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +0 -528
  22. package/extensions/dm-phone/node_modules/ws/lib/receiver.js +0 -760
  23. package/extensions/dm-phone/node_modules/ws/lib/sender.js +0 -607
  24. package/extensions/dm-phone/node_modules/ws/lib/stream.js +0 -161
  25. package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +0 -62
  26. package/extensions/dm-phone/node_modules/ws/lib/validation.js +0 -152
  27. package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +0 -562
  28. package/extensions/dm-phone/node_modules/ws/lib/websocket.js +0 -1407
  29. package/extensions/dm-phone/node_modules/ws/package.json +0 -70
  30. package/extensions/dm-phone/node_modules/ws/wrapper.mjs +0 -21
  31. package/extensions/dm-phone/package-lock.json +0 -66
  32. package/extensions/dm-phone/package.json +0 -35
  33. package/extensions/dm-phone/phone-session-pool.ts +0 -8
  34. package/extensions/dm-phone/public/app/attachments.js +0 -233
  35. package/extensions/dm-phone/public/app/autocomplete-controller.js +0 -81
  36. package/extensions/dm-phone/public/app/autocomplete.js +0 -135
  37. package/extensions/dm-phone/public/app/bindings.js +0 -178
  38. package/extensions/dm-phone/public/app/command-catalog.js +0 -76
  39. package/extensions/dm-phone/public/app/commands.js +0 -376
  40. package/extensions/dm-phone/public/app/constants.js +0 -60
  41. package/extensions/dm-phone/public/app/formatters.js +0 -131
  42. package/extensions/dm-phone/public/app/handlers.js +0 -442
  43. package/extensions/dm-phone/public/app/main.js +0 -6
  44. package/extensions/dm-phone/public/app/markdown.js +0 -105
  45. package/extensions/dm-phone/public/app/messages.js +0 -418
  46. package/extensions/dm-phone/public/app/sheet-actions.js +0 -113
  47. package/extensions/dm-phone/public/app/sheet-navigation.js +0 -19
  48. package/extensions/dm-phone/public/app/sheets-view.js +0 -287
  49. package/extensions/dm-phone/public/app/state.js +0 -95
  50. package/extensions/dm-phone/public/app/tool-rendering.js +0 -562
  51. package/extensions/dm-phone/public/app/transport.js +0 -176
  52. package/extensions/dm-phone/public/app/ui.js +0 -417
  53. package/extensions/dm-phone/public/app.js +0 -1
  54. package/extensions/dm-phone/public/icon.svg +0 -15
  55. package/extensions/dm-phone/public/index.html +0 -146
  56. package/extensions/dm-phone/public/manifest.webmanifest +0 -17
  57. package/extensions/dm-phone/public/styles.css +0 -1139
  58. package/extensions/dm-phone/public/sw.js +0 -78
  59. package/extensions/dm-phone/src/extension/duckmind-models.js +0 -264
  60. package/extensions/dm-phone/src/extension/phone-args.ts +0 -121
  61. package/extensions/dm-phone/src/extension/phone-paths.ts +0 -250
  62. package/extensions/dm-phone/src/extension/phone-quota.ts +0 -188
  63. package/extensions/dm-phone/src/extension/phone-runtime.ts +0 -154
  64. package/extensions/dm-phone/src/extension/phone-server-runtime.ts +0 -1217
  65. package/extensions/dm-phone/src/extension/phone-sessions.ts +0 -139
  66. package/extensions/dm-phone/src/extension/phone-static.ts +0 -30
  67. package/extensions/dm-phone/src/extension/phone-tailscale.ts +0 -148
  68. package/extensions/dm-phone/src/extension/phone-theme.ts +0 -85
  69. package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +0 -112
  70. package/extensions/dm-phone/src/extension/register-phone-extension.ts +0 -106
  71. package/extensions/dm-phone/src/extension/types.ts +0 -73
  72. package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +0 -882
  73. package/extensions/dm-phone/src/session-pool/session-pool.ts +0 -470
  74. package/extensions/dm-phone/src/session-pool/session-worker.ts +0 -739
  75. package/extensions/dm-phone/src/session-pool/types.ts +0 -111
  76. package/extensions/dm-phone/src/session-pool/utils.ts +0 -23
  77. package/extensions/dm-phone/test/duckmind-models.test.js +0 -147
  78. package/extensions/dm-thinking-timer/LICENSE +0 -21
  79. package/extensions/dm-thinking-timer/README.md +0 -7
  80. package/extensions/dm-thinking-timer/package.json +0 -20
  81. 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
- };