@duckmind/dm-darwin-x64 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,70 +0,0 @@
1
- {
2
- "name": "ws",
3
- "version": "8.21.0",
4
- "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
5
- "keywords": [
6
- "HyBi",
7
- "Push",
8
- "RFC-6455",
9
- "WebSocket",
10
- "WebSockets",
11
- "real-time"
12
- ],
13
- "homepage": "https://github.com/websockets/ws",
14
- "bugs": "https://github.com/websockets/ws/issues",
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/websockets/ws.git"
18
- },
19
- "author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
20
- "license": "MIT",
21
- "main": "index.js",
22
- "exports": {
23
- ".": {
24
- "browser": "./browser.js",
25
- "import": "./wrapper.mjs",
26
- "require": "./index.js"
27
- },
28
- "./package.json": "./package.json"
29
- },
30
- "browser": "browser.js",
31
- "engines": {
32
- "node": ">=10.0.0"
33
- },
34
- "files": [
35
- "browser.js",
36
- "index.js",
37
- "lib/*.js",
38
- "wrapper.mjs"
39
- ],
40
- "scripts": {
41
- "test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js",
42
- "integration": "mocha --throw-deprecation test/*.integration.js",
43
- "lint": "eslint . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\""
44
- },
45
- "peerDependencies": {
46
- "bufferutil": "^4.0.1",
47
- "utf-8-validate": ">=5.0.2"
48
- },
49
- "peerDependenciesMeta": {
50
- "bufferutil": {
51
- "optional": true
52
- },
53
- "utf-8-validate": {
54
- "optional": true
55
- }
56
- },
57
- "devDependencies": {
58
- "@eslint/js": "^10.0.1",
59
- "benchmark": "^2.1.4",
60
- "bufferutil": "^4.0.1",
61
- "eslint": "^10.0.1",
62
- "eslint-config-prettier": "^10.0.1",
63
- "eslint-plugin-prettier": "^5.0.0",
64
- "globals": "^17.0.0",
65
- "mocha": "^8.4.0",
66
- "nyc": "^15.0.0",
67
- "prettier": "^3.0.0",
68
- "utf-8-validate": "^6.0.0"
69
- }
70
- }
@@ -1,21 +0,0 @@
1
- import createWebSocketStream from './lib/stream.js';
2
- import extension from './lib/extension.js';
3
- import PerMessageDeflate from './lib/permessage-deflate.js';
4
- import Receiver from './lib/receiver.js';
5
- import Sender from './lib/sender.js';
6
- import subprotocol from './lib/subprotocol.js';
7
- import WebSocket from './lib/websocket.js';
8
- import WebSocketServer from './lib/websocket-server.js';
9
-
10
- export {
11
- createWebSocketStream,
12
- extension,
13
- PerMessageDeflate,
14
- Receiver,
15
- Sender,
16
- subprotocol,
17
- WebSocket,
18
- WebSocketServer
19
- };
20
-
21
- export default WebSocket;
@@ -1,66 +0,0 @@
1
- {
2
- "name": "dm-phone",
3
- "version": "0.0.13",
4
- "lockfileVersion": 3,
5
- "requires": true,
6
- "packages": {
7
- "": {
8
- "name": "dm-phone",
9
- "version": "0.0.13",
10
- "dependencies": {
11
- "ws": "^8.18.3"
12
- },
13
- "devDependencies": {
14
- "@types/ws": "^8.18.1"
15
- }
16
- },
17
- "node_modules/@types/node": {
18
- "version": "25.9.1",
19
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
20
- "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
21
- "dev": true,
22
- "license": "MIT",
23
- "dependencies": {
24
- "undici-types": ">=7.24.0 <7.24.7"
25
- }
26
- },
27
- "node_modules/@types/ws": {
28
- "version": "8.18.1",
29
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
30
- "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
31
- "dev": true,
32
- "license": "MIT",
33
- "dependencies": {
34
- "@types/node": "*"
35
- }
36
- },
37
- "node_modules/undici-types": {
38
- "version": "7.24.6",
39
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
40
- "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
41
- "dev": true,
42
- "license": "MIT"
43
- },
44
- "node_modules/ws": {
45
- "version": "8.21.0",
46
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
47
- "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
48
- "license": "MIT",
49
- "engines": {
50
- "node": ">=10.0.0"
51
- },
52
- "peerDependencies": {
53
- "bufferutil": "^4.0.1",
54
- "utf-8-validate": ">=5.0.2"
55
- },
56
- "peerDependenciesMeta": {
57
- "bufferutil": {
58
- "optional": true
59
- },
60
- "utf-8-validate": {
61
- "optional": true
62
- }
63
- }
64
- }
65
- }
66
- }
@@ -1,35 +0,0 @@
1
- {
2
- "name": "dm-phone",
3
- "version": "0.0.13",
4
- "description": "Phone-first remote UI extension for DM with Tailscale-friendly access",
5
- "type": "module",
6
- "keywords": [
7
- "duckmind",
8
- "dm",
9
- "extension",
10
- "phone",
11
- "mobile",
12
- "tailscale"
13
- ],
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "files": [
18
- "index.ts",
19
- "phone-session-pool.ts",
20
- "src",
21
- "public",
22
- "README.md"
23
- ],
24
- "pi": {
25
- "extensions": [
26
- "./index.ts"
27
- ]
28
- },
29
- "dependencies": {
30
- "ws": "^8.18.3"
31
- },
32
- "devDependencies": {
33
- "@types/ws": "^8.18.1"
34
- }
35
- }
@@ -1,8 +0,0 @@
1
- export { PhoneSessionPool } from "./src/session-pool/session-pool";
2
- export { PhoneSessionWorker } from "./src/session-pool/session-worker";
3
- export type {
4
- PhoneSessionPoolOptions,
5
- SessionSnapshot,
6
- SessionSummary,
7
- SessionWorkerOptions,
8
- } from "./src/session-pool/types";
@@ -1,233 +0,0 @@
1
- import { insertTextAtCursor } from "./autocomplete.js";
2
- import { escapeHtml, formatBytes } from "./formatters.js";
3
- import { el, state } from "./state.js";
4
- import { autoResizeTextarea, scheduleComposerLayoutSync } from "./ui.js";
5
-
6
- function currentPromptText() {
7
- return String(el.promptInput?.value || "");
8
- }
9
-
10
- function createAttachmentRecord(file) {
11
- const tokenOrder = state.nextAttachmentTokenId || 1;
12
- state.nextAttachmentTokenId = tokenOrder + 1;
13
-
14
- return {
15
- id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
16
- file,
17
- name: file.name,
18
- size: file.size,
19
- type: file.type,
20
- url: URL.createObjectURL(file),
21
- token: `⟦img${tokenOrder}⟧`,
22
- tokenOrder,
23
- };
24
- }
25
-
26
- function orderedAttachments(promptText = currentPromptText()) {
27
- return [...state.attachments].sort((left, right) => {
28
- const leftIndex = promptText.indexOf(left.token);
29
- const rightIndex = promptText.indexOf(right.token);
30
- const leftMissing = leftIndex === -1;
31
- const rightMissing = rightIndex === -1;
32
-
33
- if (leftMissing !== rightMissing) return leftMissing ? 1 : -1;
34
- if (!leftMissing && leftIndex !== rightIndex) return leftIndex - rightIndex;
35
- return (left.tokenOrder || 0) - (right.tokenOrder || 0);
36
- });
37
- }
38
-
39
- function disposeAttachment(attachment) {
40
- if (attachment?.url) {
41
- URL.revokeObjectURL(attachment.url);
42
- }
43
- }
44
-
45
- function disposeAttachments(attachments = []) {
46
- for (const attachment of attachments) {
47
- disposeAttachment(attachment);
48
- }
49
- }
50
-
51
- function buildTokenInsertion(tokens) {
52
- if (!tokens.length) return "";
53
-
54
- const value = currentPromptText();
55
- const start = el.promptInput.selectionStart ?? value.length;
56
- const end = el.promptInput.selectionEnd ?? start;
57
- const beforeChar = start > 0 ? value[start - 1] : "";
58
- const afterChar = end < value.length ? value[end] : "";
59
- const needsLeadingSpace = beforeChar && !/\s/.test(beforeChar);
60
- const needsTrailingSpace = !afterChar || !/\s/.test(afterChar);
61
-
62
- return `${needsLeadingSpace ? " " : ""}${tokens.join(" ")}${needsTrailingSpace ? " " : ""}`;
63
- }
64
-
65
- function stripTokenFromPrompt(token) {
66
- const value = currentPromptText();
67
- if (!token || !value.includes(token)) return false;
68
-
69
- const selectionStart = el.promptInput.selectionStart ?? value.length;
70
- const selectionEnd = el.promptInput.selectionEnd ?? selectionStart;
71
- let nextText = "";
72
- let nextSelectionStart = selectionStart;
73
- let nextSelectionEnd = selectionEnd;
74
- let offset = 0;
75
-
76
- while (offset < value.length) {
77
- const index = value.indexOf(token, offset);
78
- if (index === -1) break;
79
-
80
- nextText += value.slice(offset, index);
81
-
82
- if (index < nextSelectionStart) {
83
- nextSelectionStart -= Math.min(token.length, nextSelectionStart - index);
84
- }
85
- if (index < nextSelectionEnd) {
86
- nextSelectionEnd -= Math.min(token.length, nextSelectionEnd - index);
87
- }
88
-
89
- offset = index + token.length;
90
- }
91
-
92
- nextText += value.slice(offset);
93
- if (nextText === value) return false;
94
-
95
- el.promptInput.value = nextText;
96
- const clampedStart = Math.max(0, Math.min(nextText.length, nextSelectionStart));
97
- const clampedEnd = Math.max(clampedStart, Math.min(nextText.length, nextSelectionEnd));
98
- el.promptInput.focus();
99
- el.promptInput.setSelectionRange(clampedStart, clampedEnd);
100
- autoResizeTextarea();
101
- return true;
102
- }
103
-
104
- export function renderAttachmentStrip() {
105
- const attachments = orderedAttachments();
106
- if (!attachments.length) {
107
- el.attachmentStrip.classList.add("hidden");
108
- el.attachmentStrip.innerHTML = "";
109
- scheduleComposerLayoutSync();
110
- return;
111
- }
112
-
113
- el.attachmentStrip.innerHTML = attachments.map((attachment) => `
114
- <article class="attachment-chip">
115
- <img src="${attachment.url}" alt="${escapeHtml(attachment.name)}" />
116
- <div class="attachment-chip-header">
117
- <div class="attachment-chip-token mono">${escapeHtml(attachment.token)}</div>
118
- <button class="attachment-chip-remove" data-remove-attachment="${attachment.id}" aria-label="Remove image ${escapeHtml(attachment.token)}">✕</button>
119
- </div>
120
- <div class="attachment-chip-name">${escapeHtml(attachment.name)}</div>
121
- <div class="attachment-chip-meta">${escapeHtml(formatBytes(attachment.size))}</div>
122
- </article>
123
- `).join("");
124
- el.attachmentStrip.classList.remove("hidden");
125
- scheduleComposerLayoutSync();
126
- }
127
-
128
- export function addAttachments(files) {
129
- const incoming = Array.from(files || []).filter((file) => file.type.startsWith("image/"));
130
- if (!incoming.length) return;
131
-
132
- const added = incoming.map((file) => createAttachmentRecord(file));
133
- state.attachments.push(...added);
134
-
135
- const insertion = buildTokenInsertion(added.map((attachment) => attachment.token));
136
- if (insertion) {
137
- insertTextAtCursor(insertion);
138
- }
139
-
140
- syncAttachmentsWithPrompt();
141
- renderAttachmentStrip();
142
- }
143
-
144
- export function removeAttachment(id) {
145
- const index = state.attachments.findIndex((attachment) => attachment.id === id);
146
- if (index === -1) return;
147
-
148
- const [removed] = state.attachments.splice(index, 1);
149
- stripTokenFromPrompt(removed.token);
150
- disposeAttachment(removed);
151
- renderAttachmentStrip();
152
- }
153
-
154
- export function clearAttachments(options = {}) {
155
- if (options.removeTokensFromPrompt) {
156
- for (const attachment of state.attachments) {
157
- stripTokenFromPrompt(attachment.token);
158
- }
159
- }
160
-
161
- disposeAttachments(state.attachments);
162
- state.attachments = [];
163
- renderAttachmentStrip();
164
- }
165
-
166
- function fileToBase64(file) {
167
- return new Promise((resolve, reject) => {
168
- const reader = new FileReader();
169
- reader.onload = () => {
170
- const result = String(reader.result || "");
171
- const base64 = result.includes(",") ? result.split(",")[1] : result;
172
- resolve(base64);
173
- };
174
- reader.onerror = () => reject(reader.error || new Error(`Failed to read ${file.name}`));
175
- reader.readAsDataURL(file);
176
- });
177
- }
178
-
179
- export function syncAttachmentsWithPrompt() {
180
- if (!state.attachments.length) return [];
181
-
182
- const promptText = currentPromptText();
183
- const kept = [];
184
- const removed = [];
185
-
186
- for (const attachment of state.attachments) {
187
- if (promptText.includes(attachment.token)) {
188
- kept.push(attachment);
189
- } else {
190
- removed.push(attachment);
191
- }
192
- }
193
-
194
- if (removed.length) {
195
- disposeAttachments(removed);
196
- state.attachments = kept;
197
- }
198
-
199
- renderAttachmentStrip();
200
- return removed;
201
- }
202
-
203
- function attachmentOccurrences(promptText = currentPromptText()) {
204
- const matches = [];
205
-
206
- for (const attachment of state.attachments) {
207
- let offset = 0;
208
- while (offset <= promptText.length) {
209
- const index = promptText.indexOf(attachment.token, offset);
210
- if (index === -1) break;
211
- matches.push({ index, attachment });
212
- offset = index + attachment.token.length;
213
- }
214
- }
215
-
216
- matches.sort((left, right) => left.index - right.index || (left.attachment.tokenOrder || 0) - (right.attachment.tokenOrder || 0));
217
- return matches.map((match) => match.attachment);
218
- }
219
-
220
- export async function buildPromptPayload(promptText = currentPromptText()) {
221
- const rawPrompt = String(promptText || "");
222
- const attachments = attachmentOccurrences(rawPrompt);
223
-
224
- const images = await Promise.all(
225
- attachments.map(async (attachment) => ({
226
- type: "image",
227
- data: await fileToBase64(attachment.file),
228
- mimeType: attachment.type || "image/png",
229
- })),
230
- );
231
-
232
- return { message: rawPrompt, images };
233
- }
@@ -1,81 +0,0 @@
1
- import {
2
- clearAutocompleteSuggestions,
3
- detectCdAutocompleteContext,
4
- detectMentionAutocompleteContext,
5
- detectSlashCommandAutocompleteContext,
6
- renderAutocompleteItems,
7
- } from "./autocomplete.js";
8
- import { visibleCommandCatalog } from "./command-catalog.js";
9
- import { el, state } from "./state.js";
10
- import { sendLocalCommand } from "./transport.js";
11
-
12
- function activeAutocompleteContext() {
13
- const value = el.promptInput.value || "";
14
- const cursor = el.promptInput.selectionStart ?? value.length;
15
-
16
- const mention = detectMentionAutocompleteContext(value, cursor);
17
- if (mention) return mention;
18
-
19
- const cd = detectCdAutocompleteContext(value, cursor);
20
- if (cd) return cd;
21
-
22
- const slash = detectSlashCommandAutocompleteContext(value, cursor);
23
- if (slash) return slash;
24
-
25
- return null;
26
- }
27
-
28
- function requestPathSuggestions(context) {
29
- if (state.socket?.readyState !== WebSocket.OPEN) {
30
- renderAutocompleteItems([]);
31
- return;
32
- }
33
-
34
- const requestId = ++state.autocompleteRemoteRequestId;
35
- sendLocalCommand({
36
- type: "path-suggestions",
37
- mode: context.mode,
38
- query: context.query,
39
- requestId,
40
- });
41
- }
42
-
43
- function queuePathSuggestions(context) {
44
- if (state.autocompleteRemoteTimer) {
45
- clearTimeout(state.autocompleteRemoteTimer);
46
- }
47
-
48
- state.autocompleteRemoteTimer = setTimeout(() => {
49
- state.autocompleteRemoteTimer = null;
50
- requestPathSuggestions(context);
51
- }, 90);
52
- }
53
-
54
- export function renderCommandSuggestions() {
55
- const context = activeAutocompleteContext();
56
- state.autocompleteContext = context;
57
-
58
- if (!context) {
59
- clearAutocompleteSuggestions();
60
- return;
61
- }
62
-
63
- if (context.type === "slash-command") {
64
- const matches = visibleCommandCatalog()
65
- .filter((command) => command.name.toLowerCase().startsWith(context.query.toLowerCase()))
66
- .slice(0, 10)
67
- .map((command) => ({
68
- kind: command.source === "local" ? (command.insertOnly ? "local-command-insert" : "local-command-run") : "remote-command-insert",
69
- label: `/${command.name}`,
70
- badge: command.source || "command",
71
- description: command.description || "",
72
- name: command.name,
73
- }));
74
-
75
- renderAutocompleteItems(matches);
76
- return;
77
- }
78
-
79
- renderAutocompleteItems([]);
80
- queuePathSuggestions(context);
81
- }
@@ -1,135 +0,0 @@
1
- import { AUTOCOMPLETE_DELIMITERS } from "./constants.js";
2
- import { el, state } from "./state.js";
3
- import { escapeAttribute, escapeHtml } from "./formatters.js";
4
- import { autoResizeTextarea, scheduleComposerLayoutSync } from "./ui.js";
5
-
6
- export function clearAutocompleteSuggestions() {
7
- if (state.autocompleteRemoteTimer) {
8
- clearTimeout(state.autocompleteRemoteTimer);
9
- state.autocompleteRemoteTimer = null;
10
- }
11
- state.autocompleteContext = null;
12
- state.autocompleteItems = [];
13
- el.commandStrip.classList.add("hidden");
14
- el.commandStrip.innerHTML = "";
15
- scheduleComposerLayoutSync();
16
- }
17
-
18
- export function renderAutocompleteItems(items = []) {
19
- state.autocompleteItems = items;
20
-
21
- if (!items.length) {
22
- el.commandStrip.classList.add("hidden");
23
- el.commandStrip.innerHTML = "";
24
- scheduleComposerLayoutSync();
25
- return;
26
- }
27
-
28
- el.commandStrip.innerHTML = items.map((item, index) => `
29
- <button class="command-chip secondary" data-autocomplete-index="${index}" title="${escapeAttribute(item.title || item.description || item.label || "")}">
30
- <span>${escapeHtml(item.label || "")}</span>
31
- <span class="source">${escapeHtml(item.badge || "")}</span>
32
- </button>
33
- `).join("");
34
- el.commandStrip.classList.remove("hidden");
35
- scheduleComposerLayoutSync();
36
- }
37
-
38
- function delimiterBeforeIndex(text, index) {
39
- return index <= 0 || AUTOCOMPLETE_DELIMITERS.has(text[index - 1]);
40
- }
41
-
42
- function findTokenBounds(text, start, end) {
43
- let tokenStart = start;
44
- let tokenEnd = end;
45
-
46
- while (tokenStart > 0 && !AUTOCOMPLETE_DELIMITERS.has(text[tokenStart - 1])) {
47
- tokenStart -= 1;
48
- }
49
- while (tokenEnd < text.length && !AUTOCOMPLETE_DELIMITERS.has(text[tokenEnd])) {
50
- tokenEnd += 1;
51
- }
52
-
53
- return { start: tokenStart, end: tokenEnd };
54
- }
55
-
56
- export function detectMentionAutocompleteContext(text, cursor) {
57
- const scanLimit = Math.min(cursor, text.length);
58
- let tokenStart = scanLimit;
59
- while (tokenStart > 0 && !AUTOCOMPLETE_DELIMITERS.has(text[tokenStart - 1])) {
60
- tokenStart -= 1;
61
- }
62
-
63
- if (text[tokenStart] !== "@") return null;
64
- if (!delimiterBeforeIndex(text, tokenStart)) return null;
65
-
66
- const bounds = findTokenBounds(text, tokenStart, cursor);
67
- return {
68
- type: "path",
69
- mode: "mention",
70
- query: text.slice(tokenStart + 1, cursor),
71
- replaceStart: bounds.start,
72
- replaceEnd: bounds.end,
73
- };
74
- }
75
-
76
- export function detectCdAutocompleteContext(text, cursor) {
77
- const leadingWhitespace = text.match(/^\s*/)?.[0] || "";
78
- const trimmed = text.slice(leadingWhitespace.length);
79
- if (!trimmed.startsWith("/cd")) return null;
80
-
81
- const afterCommand = trimmed.slice(3);
82
- if (afterCommand && !/^\s/.test(afterCommand)) return null;
83
-
84
- const commandStart = leadingWhitespace.length;
85
- const argsStart = commandStart + 3 + (afterCommand.match(/^\s*/) || [""])[0].length;
86
- if (cursor < argsStart) return null;
87
-
88
- return {
89
- type: "path",
90
- mode: "cd",
91
- query: text.slice(argsStart, cursor),
92
- replaceStart: argsStart,
93
- replaceEnd: text.length,
94
- };
95
- }
96
-
97
- export function detectSlashCommandAutocompleteContext(text, cursor) {
98
- const leadingWhitespace = text.match(/^\s*/)?.[0] || "";
99
- const trimmedBeforeCursor = text.slice(leadingWhitespace.length, cursor);
100
- if (!trimmedBeforeCursor.startsWith("/")) return null;
101
- if (/\s/.test(trimmedBeforeCursor.slice(1))) return null;
102
-
103
- return {
104
- type: "slash-command",
105
- query: trimmedBeforeCursor.slice(1),
106
- };
107
- }
108
-
109
- export function replacePromptRange(start, end, nextText) {
110
- const value = el.promptInput.value;
111
- el.promptInput.value = `${value.slice(0, start)}${nextText}${value.slice(end)}`;
112
- const nextCursor = start + nextText.length;
113
- el.promptInput.focus();
114
- el.promptInput.setSelectionRange(nextCursor, nextCursor);
115
- autoResizeTextarea();
116
- }
117
-
118
- export function insertTextAtCursor(text) {
119
- const start = el.promptInput.selectionStart ?? el.promptInput.value.length;
120
- const end = el.promptInput.selectionEnd ?? start;
121
- replacePromptRange(start, end, text);
122
- }
123
-
124
- export function insertCdCommand() {
125
- const value = el.promptInput.value;
126
- if (!value.trim()) {
127
- el.promptInput.value = "/cd ";
128
- el.promptInput.focus();
129
- el.promptInput.setSelectionRange(4, 4);
130
- autoResizeTextarea();
131
- return;
132
- }
133
-
134
- insertTextAtCursor("/cd ");
135
- }