ikanban-web 0.2.2 → 0.2.4
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 +0 -17
- package/bin/cli.js +32 -2
- package/dist/assets/{ghostty-web-DC9h7yp6.js → ghostty-web-jGvmQkEo.js} +1 -1
- package/dist/assets/{home-B2bEEt-Q.js → home-CvPou5Jd.js} +1 -1
- package/dist/assets/{index-CLdyKsqF.css → index-CitLv2ln.css} +1 -1
- package/dist/assets/index-HcsA-1S9.js +2413 -0
- package/dist/assets/session-DXwXMH7X.js +24 -0
- package/dist/index.html +2 -2
- package/package.json +1 -6
- package/src/components/dialog-select-model.tsx +43 -6
- package/src/components/prompt-input.tsx +3 -9
- package/src/context/language.tsx +77 -199
- package/src/i18n/en.ts +1 -0
- package/src/pages/session/use-session-commands.tsx +226 -180
- package/dist/assets/index-BM0fG0_S.js +0 -2443
- package/dist/assets/session-CFBQK6ra.js +0 -24
|
@@ -1,102 +1,119 @@
|
|
|
1
|
-
import { createMemo } from "solid-js"
|
|
2
|
-
import { useNavigate, useParams } from "@solidjs/router"
|
|
3
|
-
import { useCommand, type CommandOption } from "@/context/command"
|
|
4
|
-
import { useDialog } from "ikanban-ui/context/dialog"
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
1
|
+
import { createMemo } from "solid-js";
|
|
2
|
+
import { useNavigate, useParams } from "@solidjs/router";
|
|
3
|
+
import { useCommand, type CommandOption } from "@/context/command";
|
|
4
|
+
import { useDialog } from "ikanban-ui/context/dialog";
|
|
5
|
+
import {
|
|
6
|
+
useFile,
|
|
7
|
+
selectionFromLines,
|
|
8
|
+
type FileSelection,
|
|
9
|
+
type SelectedLineRange,
|
|
10
|
+
} from "@/context/file";
|
|
11
|
+
import { useLanguage } from "@/context/language";
|
|
12
|
+
import { useLayout } from "@/context/layout";
|
|
13
|
+
import { useLocal } from "@/context/local";
|
|
14
|
+
import { usePermission } from "@/context/permission";
|
|
15
|
+
import { usePrompt } from "@/context/prompt";
|
|
16
|
+
import { useSDK } from "@/context/sdk";
|
|
17
|
+
import { useSync } from "@/context/sync";
|
|
18
|
+
import { useTerminal } from "@/context/terminal";
|
|
19
|
+
import { DialogSelectFile } from "@/components/dialog-select-file";
|
|
20
|
+
import { DialogSelectModel } from "@/components/dialog-select-model";
|
|
21
|
+
import { DialogSelectMcp } from "@/components/dialog-select-mcp";
|
|
22
|
+
import { DialogFork } from "@/components/dialog-fork";
|
|
23
|
+
import { showToast } from "ikanban-ui/toast";
|
|
24
|
+
import { findLast } from "ikanban-utils/array";
|
|
25
|
+
import { extractPromptFromParts } from "@/utils/prompt";
|
|
26
|
+
import { UserMessage } from "@opencode-ai/sdk/v2";
|
|
27
|
+
import { canAddSelectionContext } from "@/pages/session/session-command-helpers";
|
|
23
28
|
|
|
24
29
|
export type SessionCommandContext = {
|
|
25
|
-
navigateMessageByOffset: (offset: number) => void
|
|
26
|
-
setActiveMessage: (message: UserMessage | undefined) => void
|
|
27
|
-
focusInput: () => void
|
|
28
|
-
}
|
|
30
|
+
navigateMessageByOffset: (offset: number) => void;
|
|
31
|
+
setActiveMessage: (message: UserMessage | undefined) => void;
|
|
32
|
+
focusInput: () => void;
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
const withCategory = (category: string) => {
|
|
31
36
|
return (option: Omit<CommandOption, "category">): CommandOption => ({
|
|
32
37
|
...option,
|
|
33
38
|
category,
|
|
34
|
-
})
|
|
35
|
-
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
36
41
|
|
|
37
42
|
export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
38
|
-
const command = useCommand()
|
|
39
|
-
const dialog = useDialog()
|
|
40
|
-
const file = useFile()
|
|
41
|
-
const language = useLanguage()
|
|
42
|
-
const local = useLocal()
|
|
43
|
-
const permission = usePermission()
|
|
44
|
-
const prompt = usePrompt()
|
|
45
|
-
const sdk = useSDK()
|
|
46
|
-
const sync = useSync()
|
|
47
|
-
const terminal = useTerminal()
|
|
48
|
-
const layout = useLayout()
|
|
49
|
-
const params = useParams()
|
|
50
|
-
const navigate = useNavigate()
|
|
43
|
+
const command = useCommand();
|
|
44
|
+
const dialog = useDialog();
|
|
45
|
+
const file = useFile();
|
|
46
|
+
const language = useLanguage();
|
|
47
|
+
const local = useLocal();
|
|
48
|
+
const permission = usePermission();
|
|
49
|
+
const prompt = usePrompt();
|
|
50
|
+
const sdk = useSDK();
|
|
51
|
+
const sync = useSync();
|
|
52
|
+
const terminal = useTerminal();
|
|
53
|
+
const layout = useLayout();
|
|
54
|
+
const params = useParams();
|
|
55
|
+
const navigate = useNavigate();
|
|
51
56
|
|
|
52
|
-
const sessionKey = createMemo(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
57
|
+
const sessionKey = createMemo(
|
|
58
|
+
() => `${params.dir}${params.id ? "/" + params.id : ""}`,
|
|
59
|
+
);
|
|
60
|
+
const tabs = createMemo(() => layout.tabs(sessionKey));
|
|
61
|
+
const view = createMemo(() => layout.view(sessionKey));
|
|
62
|
+
const info = createMemo(() =>
|
|
63
|
+
params.id ? sync.session.get(params.id) : undefined,
|
|
64
|
+
);
|
|
56
65
|
|
|
57
|
-
const idle = { type: "idle" as const }
|
|
58
|
-
const status = createMemo(
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
const idle = { type: "idle" as const };
|
|
67
|
+
const status = createMemo(
|
|
68
|
+
() => sync.data.session_status[params.id ?? ""] ?? idle,
|
|
69
|
+
);
|
|
70
|
+
const messages = createMemo(() =>
|
|
71
|
+
params.id ? (sync.data.message[params.id] ?? []) : [],
|
|
72
|
+
);
|
|
73
|
+
const userMessages = createMemo(
|
|
74
|
+
() => messages().filter((m) => m.role === "user") as UserMessage[],
|
|
75
|
+
);
|
|
61
76
|
const visibleUserMessages = createMemo(() => {
|
|
62
|
-
const revert = info()?.revert?.messageID
|
|
63
|
-
if (!revert) return userMessages()
|
|
64
|
-
return userMessages().filter((m) => m.id < revert)
|
|
65
|
-
})
|
|
77
|
+
const revert = info()?.revert?.messageID;
|
|
78
|
+
if (!revert) return userMessages();
|
|
79
|
+
return userMessages().filter((m) => m.id < revert);
|
|
80
|
+
});
|
|
66
81
|
|
|
67
82
|
const showAllFiles = () => {
|
|
68
|
-
if (layout.fileTree.tab() !== "changes") return
|
|
69
|
-
layout.fileTree.setTab("all")
|
|
70
|
-
}
|
|
83
|
+
if (layout.fileTree.tab() !== "changes") return;
|
|
84
|
+
layout.fileTree.setTab("all");
|
|
85
|
+
};
|
|
71
86
|
|
|
72
87
|
const selectionPreview = (path: string, selection: FileSelection) => {
|
|
73
|
-
const content = file.get(path)?.content?.content
|
|
74
|
-
if (!content) return undefined
|
|
75
|
-
const start = Math.max(1, Math.min(selection.startLine, selection.endLine))
|
|
76
|
-
const end = Math.max(selection.startLine, selection.endLine)
|
|
77
|
-
const lines = content.split("\n").slice(start - 1, end)
|
|
78
|
-
if (lines.length === 0) return undefined
|
|
79
|
-
return lines.slice(0, 2).join("\n")
|
|
80
|
-
}
|
|
88
|
+
const content = file.get(path)?.content?.content;
|
|
89
|
+
if (!content) return undefined;
|
|
90
|
+
const start = Math.max(1, Math.min(selection.startLine, selection.endLine));
|
|
91
|
+
const end = Math.max(selection.startLine, selection.endLine);
|
|
92
|
+
const lines = content.split("\n").slice(start - 1, end);
|
|
93
|
+
if (lines.length === 0) return undefined;
|
|
94
|
+
return lines.slice(0, 2).join("\n");
|
|
95
|
+
};
|
|
81
96
|
|
|
82
97
|
const addSelectionToContext = (path: string, selection: FileSelection) => {
|
|
83
|
-
const preview = selectionPreview(path, selection)
|
|
84
|
-
prompt.context.add({ type: "file", path, selection, preview })
|
|
85
|
-
}
|
|
98
|
+
const preview = selectionPreview(path, selection);
|
|
99
|
+
prompt.context.add({ type: "file", path, selection, preview });
|
|
100
|
+
};
|
|
86
101
|
|
|
87
|
-
const navigateMessageByOffset = actions.navigateMessageByOffset
|
|
88
|
-
const setActiveMessage = actions.setActiveMessage
|
|
89
|
-
const focusInput = actions.focusInput
|
|
102
|
+
const navigateMessageByOffset = actions.navigateMessageByOffset;
|
|
103
|
+
const setActiveMessage = actions.setActiveMessage;
|
|
104
|
+
const focusInput = actions.focusInput;
|
|
90
105
|
|
|
91
|
-
const sessionCommand = withCategory(language.t("command.category.session"))
|
|
92
|
-
const fileCommand = withCategory(language.t("command.category.file"))
|
|
93
|
-
const contextCommand = withCategory(language.t("command.category.context"))
|
|
94
|
-
const viewCommand = withCategory(language.t("command.category.view"))
|
|
95
|
-
const terminalCommand = withCategory(language.t("command.category.terminal"))
|
|
96
|
-
const modelCommand = withCategory(language.t("command.category.model"))
|
|
97
|
-
const mcpCommand = withCategory(language.t("command.category.mcp"))
|
|
98
|
-
const agentCommand = withCategory(language.t("command.category.agent"))
|
|
99
|
-
const permissionsCommand = withCategory(
|
|
106
|
+
const sessionCommand = withCategory(language.t("command.category.session"));
|
|
107
|
+
const fileCommand = withCategory(language.t("command.category.file"));
|
|
108
|
+
const contextCommand = withCategory(language.t("command.category.context"));
|
|
109
|
+
const viewCommand = withCategory(language.t("command.category.view"));
|
|
110
|
+
const terminalCommand = withCategory(language.t("command.category.terminal"));
|
|
111
|
+
const modelCommand = withCategory(language.t("command.category.model"));
|
|
112
|
+
const mcpCommand = withCategory(language.t("command.category.mcp"));
|
|
113
|
+
const agentCommand = withCategory(language.t("command.category.agent"));
|
|
114
|
+
const permissionsCommand = withCategory(
|
|
115
|
+
language.t("command.category.permissions"),
|
|
116
|
+
);
|
|
100
117
|
|
|
101
118
|
const sessionCommands = createMemo(() => [
|
|
102
119
|
sessionCommand({
|
|
@@ -106,7 +123,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
106
123
|
slash: "new",
|
|
107
124
|
onSelect: () => navigate(`/${params.dir}/session`),
|
|
108
125
|
}),
|
|
109
|
-
])
|
|
126
|
+
]);
|
|
110
127
|
|
|
111
128
|
const fileCommands = createMemo(() => [
|
|
112
129
|
fileCommand({
|
|
@@ -115,7 +132,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
115
132
|
description: language.t("palette.search.placeholder"),
|
|
116
133
|
keybind: "mod+p",
|
|
117
134
|
slash: "open",
|
|
118
|
-
onSelect: () =>
|
|
135
|
+
onSelect: () =>
|
|
136
|
+
dialog.show(() => <DialogSelectFile onOpenFile={showAllFiles} />),
|
|
119
137
|
}),
|
|
120
138
|
fileCommand({
|
|
121
139
|
id: "tab.close",
|
|
@@ -123,12 +141,12 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
123
141
|
keybind: "mod+w",
|
|
124
142
|
disabled: !tabs().active(),
|
|
125
143
|
onSelect: () => {
|
|
126
|
-
const active = tabs().active()
|
|
127
|
-
if (!active) return
|
|
128
|
-
tabs().close(active)
|
|
144
|
+
const active = tabs().active();
|
|
145
|
+
if (!active) return;
|
|
146
|
+
tabs().close(active);
|
|
129
147
|
},
|
|
130
148
|
}),
|
|
131
|
-
])
|
|
149
|
+
]);
|
|
132
150
|
|
|
133
151
|
const contextCommands = createMemo(() => [
|
|
134
152
|
contextCommand({
|
|
@@ -142,24 +160,29 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
142
160
|
selectedLines: file.selectedLines,
|
|
143
161
|
}),
|
|
144
162
|
onSelect: () => {
|
|
145
|
-
const active = tabs().active()
|
|
146
|
-
if (!active) return
|
|
147
|
-
const path = file.pathFromTab(active)
|
|
148
|
-
if (!path) return
|
|
163
|
+
const active = tabs().active();
|
|
164
|
+
if (!active) return;
|
|
165
|
+
const path = file.pathFromTab(active);
|
|
166
|
+
if (!path) return;
|
|
149
167
|
|
|
150
|
-
const range = file.selectedLines(path) as
|
|
168
|
+
const range = file.selectedLines(path) as
|
|
169
|
+
| SelectedLineRange
|
|
170
|
+
| null
|
|
171
|
+
| undefined;
|
|
151
172
|
if (!range) {
|
|
152
173
|
showToast({
|
|
153
174
|
title: language.t("toast.context.noLineSelection.title"),
|
|
154
|
-
description: language.t(
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
description: language.t(
|
|
176
|
+
"toast.context.noLineSelection.description",
|
|
177
|
+
),
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
157
180
|
}
|
|
158
181
|
|
|
159
|
-
addSelectionToContext(path, selectionFromLines(range))
|
|
182
|
+
addSelectionToContext(path, selectionFromLines(range));
|
|
160
183
|
},
|
|
161
184
|
}),
|
|
162
|
-
])
|
|
185
|
+
]);
|
|
163
186
|
|
|
164
187
|
const viewCommands = createMemo(() => [
|
|
165
188
|
viewCommand({
|
|
@@ -184,7 +207,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
184
207
|
viewCommand({
|
|
185
208
|
id: "input.focus",
|
|
186
209
|
title: language.t("command.input.focus"),
|
|
187
|
-
keybind: "ctrl+
|
|
210
|
+
keybind: "ctrl+i",
|
|
188
211
|
onSelect: () => focusInput(),
|
|
189
212
|
}),
|
|
190
213
|
terminalCommand({
|
|
@@ -193,11 +216,11 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
193
216
|
description: language.t("command.terminal.new.description"),
|
|
194
217
|
keybind: "ctrl+alt+t",
|
|
195
218
|
onSelect: () => {
|
|
196
|
-
if (terminal.all().length > 0) terminal.new()
|
|
197
|
-
view().terminal.open()
|
|
219
|
+
if (terminal.all().length > 0) terminal.new();
|
|
220
|
+
view().terminal.open();
|
|
198
221
|
},
|
|
199
222
|
}),
|
|
200
|
-
])
|
|
223
|
+
]);
|
|
201
224
|
|
|
202
225
|
const messageCommands = createMemo(() => [
|
|
203
226
|
sessionCommand({
|
|
@@ -216,14 +239,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
216
239
|
disabled: !params.id,
|
|
217
240
|
onSelect: () => navigateMessageByOffset(1),
|
|
218
241
|
}),
|
|
219
|
-
])
|
|
242
|
+
]);
|
|
220
243
|
|
|
221
244
|
const agentCommands = createMemo(() => [
|
|
222
245
|
modelCommand({
|
|
223
246
|
id: "model.choose",
|
|
224
247
|
title: language.t("command.model.choose"),
|
|
225
248
|
description: language.t("command.model.choose.description"),
|
|
226
|
-
keybind: "mod+
|
|
249
|
+
keybind: "mod+m",
|
|
227
250
|
slash: "model",
|
|
228
251
|
onSelect: () => dialog.show(() => <DialogSelectModel />),
|
|
229
252
|
}),
|
|
@@ -256,10 +279,10 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
256
279
|
description: language.t("command.model.variant.cycle.description"),
|
|
257
280
|
keybind: "shift+mod+d",
|
|
258
281
|
onSelect: () => {
|
|
259
|
-
local.model.variant.cycle()
|
|
282
|
+
local.model.variant.cycle();
|
|
260
283
|
},
|
|
261
284
|
}),
|
|
262
|
-
])
|
|
285
|
+
]);
|
|
263
286
|
|
|
264
287
|
const permissionCommands = createMemo(() => [
|
|
265
288
|
permissionsCommand({
|
|
@@ -271,9 +294,9 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
271
294
|
keybind: "mod+shift+a",
|
|
272
295
|
disabled: !params.id || !permission.permissionsEnabled(),
|
|
273
296
|
onSelect: () => {
|
|
274
|
-
const sessionID = params.id
|
|
275
|
-
if (!sessionID) return
|
|
276
|
-
permission.toggleAutoAccept(sessionID, sdk.directory)
|
|
297
|
+
const sessionID = params.id;
|
|
298
|
+
if (!sessionID) return;
|
|
299
|
+
permission.toggleAutoAccept(sessionID, sdk.directory);
|
|
277
300
|
showToast({
|
|
278
301
|
title: permission.isAutoAccepting(sessionID, sdk.directory)
|
|
279
302
|
? language.t("toast.permissions.autoaccept.on.title")
|
|
@@ -281,10 +304,10 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
281
304
|
description: permission.isAutoAccepting(sessionID, sdk.directory)
|
|
282
305
|
? language.t("toast.permissions.autoaccept.on.description")
|
|
283
306
|
: language.t("toast.permissions.autoaccept.off.description"),
|
|
284
|
-
})
|
|
307
|
+
});
|
|
285
308
|
},
|
|
286
309
|
}),
|
|
287
|
-
])
|
|
310
|
+
]);
|
|
288
311
|
|
|
289
312
|
const sessionActionCommands = createMemo(() => [
|
|
290
313
|
sessionCommand({
|
|
@@ -294,22 +317,27 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
294
317
|
slash: "undo",
|
|
295
318
|
disabled: !params.id || visibleUserMessages().length === 0,
|
|
296
319
|
onSelect: async () => {
|
|
297
|
-
const sessionID = params.id
|
|
298
|
-
if (!sessionID) return
|
|
320
|
+
const sessionID = params.id;
|
|
321
|
+
if (!sessionID) return;
|
|
299
322
|
if (status()?.type !== "idle") {
|
|
300
|
-
await sdk.client.session.abort({ sessionID }).catch(() => {})
|
|
323
|
+
await sdk.client.session.abort({ sessionID }).catch(() => { });
|
|
301
324
|
}
|
|
302
|
-
const revert = info()?.revert?.messageID
|
|
303
|
-
const message = findLast(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
325
|
+
const revert = info()?.revert?.messageID;
|
|
326
|
+
const message = findLast(
|
|
327
|
+
userMessages(),
|
|
328
|
+
(x) => !revert || x.id < revert,
|
|
329
|
+
);
|
|
330
|
+
if (!message) return;
|
|
331
|
+
await sdk.client.session.revert({ sessionID, messageID: message.id });
|
|
332
|
+
const parts = sync.data.part[message.id];
|
|
307
333
|
if (parts) {
|
|
308
|
-
const restored = extractPromptFromParts(parts, {
|
|
309
|
-
|
|
334
|
+
const restored = extractPromptFromParts(parts, {
|
|
335
|
+
directory: sdk.directory,
|
|
336
|
+
});
|
|
337
|
+
prompt.set(restored);
|
|
310
338
|
}
|
|
311
|
-
const priorMessage = findLast(userMessages(), (x) => x.id < message.id)
|
|
312
|
-
setActiveMessage(priorMessage)
|
|
339
|
+
const priorMessage = findLast(userMessages(), (x) => x.id < message.id);
|
|
340
|
+
setActiveMessage(priorMessage);
|
|
313
341
|
},
|
|
314
342
|
}),
|
|
315
343
|
sessionCommand({
|
|
@@ -319,21 +347,27 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
319
347
|
slash: "redo",
|
|
320
348
|
disabled: !params.id || !info()?.revert?.messageID,
|
|
321
349
|
onSelect: async () => {
|
|
322
|
-
const sessionID = params.id
|
|
323
|
-
if (!sessionID) return
|
|
324
|
-
const revertMessageID = info()?.revert?.messageID
|
|
325
|
-
if (!revertMessageID) return
|
|
326
|
-
const nextMessage = userMessages().find((x) => x.id > revertMessageID)
|
|
350
|
+
const sessionID = params.id;
|
|
351
|
+
if (!sessionID) return;
|
|
352
|
+
const revertMessageID = info()?.revert?.messageID;
|
|
353
|
+
if (!revertMessageID) return;
|
|
354
|
+
const nextMessage = userMessages().find((x) => x.id > revertMessageID);
|
|
327
355
|
if (!nextMessage) {
|
|
328
|
-
await sdk.client.session.unrevert({ sessionID })
|
|
329
|
-
prompt.reset()
|
|
330
|
-
const lastMsg = findLast(
|
|
331
|
-
|
|
332
|
-
|
|
356
|
+
await sdk.client.session.unrevert({ sessionID });
|
|
357
|
+
prompt.reset();
|
|
358
|
+
const lastMsg = findLast(
|
|
359
|
+
userMessages(),
|
|
360
|
+
(x) => x.id >= revertMessageID,
|
|
361
|
+
);
|
|
362
|
+
setActiveMessage(lastMsg);
|
|
363
|
+
return;
|
|
333
364
|
}
|
|
334
|
-
await sdk.client.session.revert({
|
|
335
|
-
|
|
336
|
-
|
|
365
|
+
await sdk.client.session.revert({
|
|
366
|
+
sessionID,
|
|
367
|
+
messageID: nextMessage.id,
|
|
368
|
+
});
|
|
369
|
+
const priorMsg = findLast(userMessages(), (x) => x.id < nextMessage.id);
|
|
370
|
+
setActiveMessage(priorMsg);
|
|
337
371
|
},
|
|
338
372
|
}),
|
|
339
373
|
sessionCommand({
|
|
@@ -343,21 +377,21 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
343
377
|
slash: "compact",
|
|
344
378
|
disabled: !params.id || visibleUserMessages().length === 0,
|
|
345
379
|
onSelect: async () => {
|
|
346
|
-
const sessionID = params.id
|
|
347
|
-
if (!sessionID) return
|
|
348
|
-
const model = local.model.current()
|
|
380
|
+
const sessionID = params.id;
|
|
381
|
+
if (!sessionID) return;
|
|
382
|
+
const model = local.model.current();
|
|
349
383
|
if (!model) {
|
|
350
384
|
showToast({
|
|
351
385
|
title: language.t("toast.model.none.title"),
|
|
352
386
|
description: language.t("toast.model.none.description"),
|
|
353
|
-
})
|
|
354
|
-
return
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
355
389
|
}
|
|
356
390
|
await sdk.client.session.summarize({
|
|
357
391
|
sessionID,
|
|
358
392
|
modelID: model.id,
|
|
359
393
|
providerID: model.provider.id,
|
|
360
|
-
})
|
|
394
|
+
});
|
|
361
395
|
},
|
|
362
396
|
}),
|
|
363
397
|
sessionCommand({
|
|
@@ -368,85 +402,93 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
368
402
|
disabled: !params.id || visibleUserMessages().length === 0,
|
|
369
403
|
onSelect: () => dialog.show(() => <DialogFork />),
|
|
370
404
|
}),
|
|
371
|
-
])
|
|
405
|
+
]);
|
|
372
406
|
|
|
373
407
|
const shareCommands = createMemo(() => {
|
|
374
|
-
if (sync.data.config.share === "disabled") return []
|
|
408
|
+
if (sync.data.config.share === "disabled") return [];
|
|
375
409
|
return [
|
|
376
410
|
sessionCommand({
|
|
377
411
|
id: "session.share",
|
|
378
|
-
title: info()?.share?.url
|
|
412
|
+
title: info()?.share?.url
|
|
413
|
+
? language.t("session.share.copy.copyLink")
|
|
414
|
+
: language.t("command.session.share"),
|
|
379
415
|
description: info()?.share?.url
|
|
380
416
|
? language.t("toast.session.share.success.description")
|
|
381
417
|
: language.t("command.session.share.description"),
|
|
382
418
|
slash: "share",
|
|
383
419
|
disabled: !params.id,
|
|
384
420
|
onSelect: async () => {
|
|
385
|
-
if (!params.id) return
|
|
421
|
+
if (!params.id) return;
|
|
386
422
|
|
|
387
423
|
const write = (value: string) => {
|
|
388
|
-
const body =
|
|
424
|
+
const body =
|
|
425
|
+
typeof document === "undefined" ? undefined : document.body;
|
|
389
426
|
if (body) {
|
|
390
|
-
const textarea = document.createElement("textarea")
|
|
391
|
-
textarea.value = value
|
|
392
|
-
textarea.setAttribute("readonly", "")
|
|
393
|
-
textarea.style.position = "fixed"
|
|
394
|
-
textarea.style.opacity = "0"
|
|
395
|
-
textarea.style.pointerEvents = "none"
|
|
396
|
-
body.appendChild(textarea)
|
|
397
|
-
textarea.select()
|
|
398
|
-
const copied = document.execCommand("copy")
|
|
399
|
-
body.removeChild(textarea)
|
|
400
|
-
if (copied) return Promise.resolve(true)
|
|
427
|
+
const textarea = document.createElement("textarea");
|
|
428
|
+
textarea.value = value;
|
|
429
|
+
textarea.setAttribute("readonly", "");
|
|
430
|
+
textarea.style.position = "fixed";
|
|
431
|
+
textarea.style.opacity = "0";
|
|
432
|
+
textarea.style.pointerEvents = "none";
|
|
433
|
+
body.appendChild(textarea);
|
|
434
|
+
textarea.select();
|
|
435
|
+
const copied = document.execCommand("copy");
|
|
436
|
+
body.removeChild(textarea);
|
|
437
|
+
if (copied) return Promise.resolve(true);
|
|
401
438
|
}
|
|
402
439
|
|
|
403
|
-
const clipboard =
|
|
404
|
-
|
|
440
|
+
const clipboard =
|
|
441
|
+
typeof navigator === "undefined"
|
|
442
|
+
? undefined
|
|
443
|
+
: navigator.clipboard;
|
|
444
|
+
if (!clipboard?.writeText) return Promise.resolve(false);
|
|
405
445
|
return clipboard.writeText(value).then(
|
|
406
446
|
() => true,
|
|
407
447
|
() => false,
|
|
408
|
-
)
|
|
409
|
-
}
|
|
448
|
+
);
|
|
449
|
+
};
|
|
410
450
|
|
|
411
451
|
const copy = async (url: string, existing: boolean) => {
|
|
412
|
-
const ok = await write(url)
|
|
452
|
+
const ok = await write(url);
|
|
413
453
|
if (!ok) {
|
|
414
454
|
showToast({
|
|
415
455
|
title: language.t("toast.session.share.copyFailed.title"),
|
|
416
456
|
variant: "error",
|
|
417
|
-
})
|
|
418
|
-
return
|
|
457
|
+
});
|
|
458
|
+
return;
|
|
419
459
|
}
|
|
420
460
|
|
|
421
461
|
showToast({
|
|
422
462
|
title: existing
|
|
423
463
|
? language.t("session.share.copy.copied")
|
|
424
464
|
: language.t("toast.session.share.success.title"),
|
|
425
|
-
description: language.t(
|
|
465
|
+
description: language.t(
|
|
466
|
+
"toast.session.share.success.description",
|
|
467
|
+
),
|
|
426
468
|
variant: "success",
|
|
427
|
-
})
|
|
428
|
-
}
|
|
469
|
+
});
|
|
470
|
+
};
|
|
429
471
|
|
|
430
|
-
const existing = info()?.share?.url
|
|
472
|
+
const existing = info()?.share?.url;
|
|
431
473
|
if (existing) {
|
|
432
|
-
await copy(existing, true)
|
|
433
|
-
return
|
|
474
|
+
await copy(existing, true);
|
|
475
|
+
return;
|
|
434
476
|
}
|
|
435
477
|
|
|
436
478
|
const url = await sdk.client.session
|
|
437
479
|
.share({ sessionID: params.id })
|
|
438
480
|
.then((res) => res.data?.share?.url)
|
|
439
|
-
.catch(() => undefined)
|
|
481
|
+
.catch(() => undefined);
|
|
440
482
|
if (!url) {
|
|
441
483
|
showToast({
|
|
442
484
|
title: language.t("toast.session.share.failed.title"),
|
|
443
485
|
description: language.t("toast.session.share.failed.description"),
|
|
444
486
|
variant: "error",
|
|
445
|
-
})
|
|
446
|
-
return
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
447
489
|
}
|
|
448
490
|
|
|
449
|
-
await copy(url, false)
|
|
491
|
+
await copy(url, false);
|
|
450
492
|
},
|
|
451
493
|
}),
|
|
452
494
|
sessionCommand({
|
|
@@ -456,27 +498,31 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
456
498
|
slash: "unshare",
|
|
457
499
|
disabled: !params.id || !info()?.share?.url,
|
|
458
500
|
onSelect: async () => {
|
|
459
|
-
if (!params.id) return
|
|
501
|
+
if (!params.id) return;
|
|
460
502
|
await sdk.client.session
|
|
461
503
|
.unshare({ sessionID: params.id })
|
|
462
504
|
.then(() =>
|
|
463
505
|
showToast({
|
|
464
506
|
title: language.t("toast.session.unshare.success.title"),
|
|
465
|
-
description: language.t(
|
|
507
|
+
description: language.t(
|
|
508
|
+
"toast.session.unshare.success.description",
|
|
509
|
+
),
|
|
466
510
|
variant: "success",
|
|
467
511
|
}),
|
|
468
512
|
)
|
|
469
513
|
.catch(() =>
|
|
470
514
|
showToast({
|
|
471
515
|
title: language.t("toast.session.unshare.failed.title"),
|
|
472
|
-
description: language.t(
|
|
516
|
+
description: language.t(
|
|
517
|
+
"toast.session.unshare.failed.description",
|
|
518
|
+
),
|
|
473
519
|
variant: "error",
|
|
474
520
|
}),
|
|
475
|
-
)
|
|
521
|
+
);
|
|
476
522
|
},
|
|
477
523
|
}),
|
|
478
|
-
]
|
|
479
|
-
})
|
|
524
|
+
];
|
|
525
|
+
});
|
|
480
526
|
|
|
481
527
|
command.register("session", () =>
|
|
482
528
|
[
|
|
@@ -490,5 +536,5 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
490
536
|
sessionActionCommands(),
|
|
491
537
|
shareCommands(),
|
|
492
538
|
].flatMap((x) => x),
|
|
493
|
-
)
|
|
494
|
-
}
|
|
539
|
+
);
|
|
540
|
+
};
|