|
|
@@ -128,380 +128,452 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
|
|
|
if (sessionID) return permission.isAutoAccepting(sessionID, sdk.directory)
|
|
|
return permission.isAutoAcceptingDirectory(sdk.directory)
|
|
|
}
|
|
|
- command.register("session", () => {
|
|
|
- const share =
|
|
|
- sync.data.config.share === "disabled"
|
|
|
- ? []
|
|
|
- : [
|
|
|
- sessionCommand({
|
|
|
- id: "session.share",
|
|
|
- title: info()?.share?.url
|
|
|
- ? language.t("session.share.copy.copyLink")
|
|
|
- : language.t("command.session.share"),
|
|
|
- description: info()?.share?.url
|
|
|
- ? language.t("toast.session.share.success.description")
|
|
|
- : language.t("command.session.share.description"),
|
|
|
- slash: "share",
|
|
|
- disabled: !params.id,
|
|
|
- onSelect: async () => {
|
|
|
- if (!params.id) return
|
|
|
-
|
|
|
- const write = (value: string) => {
|
|
|
- const body = typeof document === "undefined" ? undefined : document.body
|
|
|
- if (body) {
|
|
|
- const textarea = document.createElement("textarea")
|
|
|
- textarea.value = value
|
|
|
- textarea.setAttribute("readonly", "")
|
|
|
- textarea.style.position = "fixed"
|
|
|
- textarea.style.opacity = "0"
|
|
|
- textarea.style.pointerEvents = "none"
|
|
|
- body.appendChild(textarea)
|
|
|
- textarea.select()
|
|
|
- const copied = document.execCommand("copy")
|
|
|
- body.removeChild(textarea)
|
|
|
- if (copied) return Promise.resolve(true)
|
|
|
- }
|
|
|
-
|
|
|
- const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard
|
|
|
- if (!clipboard?.writeText) return Promise.resolve(false)
|
|
|
- return clipboard.writeText(value).then(
|
|
|
- () => true,
|
|
|
- () => false,
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- const copy = async (url: string, existing: boolean) => {
|
|
|
- const ok = await write(url)
|
|
|
- if (!ok) {
|
|
|
- showToast({
|
|
|
- title: language.t("toast.session.share.copyFailed.title"),
|
|
|
- variant: "error",
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- showToast({
|
|
|
- title: existing
|
|
|
- ? language.t("session.share.copy.copied")
|
|
|
- : language.t("toast.session.share.success.title"),
|
|
|
- description: language.t("toast.session.share.success.description"),
|
|
|
- variant: "success",
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- const existing = info()?.share?.url
|
|
|
- if (existing) {
|
|
|
- await copy(existing, true)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const url = await sdk.client.session
|
|
|
- .share({ sessionID: params.id })
|
|
|
- .then((res) => res.data?.share?.url)
|
|
|
- .catch(() => undefined)
|
|
|
- if (!url) {
|
|
|
- showToast({
|
|
|
- title: language.t("toast.session.share.failed.title"),
|
|
|
- description: language.t("toast.session.share.failed.description"),
|
|
|
- variant: "error",
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- await copy(url, false)
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "session.unshare",
|
|
|
- title: language.t("command.session.unshare"),
|
|
|
- description: language.t("command.session.unshare.description"),
|
|
|
- slash: "unshare",
|
|
|
- disabled: !params.id || !info()?.share?.url,
|
|
|
- onSelect: async () => {
|
|
|
- if (!params.id) return
|
|
|
- await sdk.client.session
|
|
|
- .unshare({ sessionID: params.id })
|
|
|
- .then(() =>
|
|
|
- showToast({
|
|
|
- title: language.t("toast.session.unshare.success.title"),
|
|
|
- description: language.t("toast.session.unshare.success.description"),
|
|
|
- variant: "success",
|
|
|
- }),
|
|
|
- )
|
|
|
- .catch(() =>
|
|
|
- showToast({
|
|
|
- title: language.t("toast.session.unshare.failed.title"),
|
|
|
- description: language.t("toast.session.unshare.failed.description"),
|
|
|
- variant: "error",
|
|
|
- }),
|
|
|
- )
|
|
|
- },
|
|
|
- }),
|
|
|
- ]
|
|
|
+ const write = async (value: string) => {
|
|
|
+ const body = typeof document === "undefined" ? undefined : document.body
|
|
|
+ if (body) {
|
|
|
+ const textarea = document.createElement("textarea")
|
|
|
+ textarea.value = value
|
|
|
+ textarea.setAttribute("readonly", "")
|
|
|
+ textarea.style.position = "fixed"
|
|
|
+ textarea.style.opacity = "0"
|
|
|
+ textarea.style.pointerEvents = "none"
|
|
|
+ body.appendChild(textarea)
|
|
|
+ textarea.select()
|
|
|
+ const copied = document.execCommand("copy")
|
|
|
+ body.removeChild(textarea)
|
|
|
+ if (copied) return true
|
|
|
+ }
|
|
|
|
|
|
+ const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard
|
|
|
+ if (!clipboard?.writeText) return false
|
|
|
+ return clipboard.writeText(value).then(
|
|
|
+ () => true,
|
|
|
+ () => false,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const copyShare = async (url: string, existing: boolean) => {
|
|
|
+ if (!(await write(url))) {
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.session.share.copyFailed.title"),
|
|
|
+ variant: "error",
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ showToast({
|
|
|
+ title: existing ? language.t("session.share.copy.copied") : language.t("toast.session.share.success.title"),
|
|
|
+ description: language.t("toast.session.share.success.description"),
|
|
|
+ variant: "success",
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const share = async () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (!sessionID) return
|
|
|
+
|
|
|
+ const existing = info()?.share?.url
|
|
|
+ if (existing) {
|
|
|
+ await copyShare(existing, true)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const url = await sdk.client.session
|
|
|
+ .share({ sessionID })
|
|
|
+ .then((res) => res.data?.share?.url)
|
|
|
+ .catch(() => undefined)
|
|
|
+ if (!url) {
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.session.share.failed.title"),
|
|
|
+ description: language.t("toast.session.share.failed.description"),
|
|
|
+ variant: "error",
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ await copyShare(url, false)
|
|
|
+ }
|
|
|
+
|
|
|
+ const unshare = async () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (!sessionID) return
|
|
|
+
|
|
|
+ await sdk.client.session
|
|
|
+ .unshare({ sessionID })
|
|
|
+ .then(() =>
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.session.unshare.success.title"),
|
|
|
+ description: language.t("toast.session.unshare.success.description"),
|
|
|
+ variant: "success",
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ .catch(() =>
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.session.unshare.failed.title"),
|
|
|
+ description: language.t("toast.session.unshare.failed.description"),
|
|
|
+ variant: "error",
|
|
|
+ }),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ const openFile = () => {
|
|
|
+ void import("@/components/dialog-select-file").then((x) => {
|
|
|
+ dialog.show(() => <x.DialogSelectFile onOpenFile={showAllFiles} />)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const closeTab = () => {
|
|
|
+ const tab = closableTab()
|
|
|
+ if (!tab) return
|
|
|
+ tabs().close(tab)
|
|
|
+ }
|
|
|
+
|
|
|
+ const addSelection = () => {
|
|
|
+ const tab = activeFileTab()
|
|
|
+ if (!tab) return
|
|
|
+
|
|
|
+ const path = file.pathFromTab(tab)
|
|
|
+ if (!path) return
|
|
|
+
|
|
|
+ const range = file.selectedLines(path) as SelectedLineRange | null | undefined
|
|
|
+ if (!range) {
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.context.noLineSelection.title"),
|
|
|
+ description: language.t("toast.context.noLineSelection.description"),
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ addSelectionToContext(path, selectionFromLines(range))
|
|
|
+ }
|
|
|
+
|
|
|
+ const openTerminal = () => {
|
|
|
+ if (terminal.all().length > 0) terminal.new()
|
|
|
+ view().terminal.open()
|
|
|
+ }
|
|
|
+
|
|
|
+ const chooseModel = () => {
|
|
|
+ void import("@/components/dialog-select-model").then((x) => {
|
|
|
+ dialog.show(() => <x.DialogSelectModel model={local.model} />)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const chooseMcp = () => {
|
|
|
+ void import("@/components/dialog-select-mcp").then((x) => {
|
|
|
+ dialog.show(() => <x.DialogSelectMcp />)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const toggleAutoAccept = () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (sessionID) permission.toggleAutoAccept(sessionID, sdk.directory)
|
|
|
+ else permission.toggleAutoAcceptDirectory(sdk.directory)
|
|
|
+
|
|
|
+ const active = sessionID
|
|
|
+ ? permission.isAutoAccepting(sessionID, sdk.directory)
|
|
|
+ : permission.isAutoAcceptingDirectory(sdk.directory)
|
|
|
+ showToast({
|
|
|
+ title: active
|
|
|
+ ? language.t("toast.permissions.autoaccept.on.title")
|
|
|
+ : language.t("toast.permissions.autoaccept.off.title"),
|
|
|
+ description: active
|
|
|
+ ? language.t("toast.permissions.autoaccept.on.description")
|
|
|
+ : language.t("toast.permissions.autoaccept.off.description"),
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const undo = async () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (!sessionID) return
|
|
|
+
|
|
|
+ if (status().type !== "idle") {
|
|
|
+ await sdk.client.session.abort({ sessionID }).catch(() => {})
|
|
|
+ }
|
|
|
+
|
|
|
+ const revert = info()?.revert?.messageID
|
|
|
+ const message = findLast(userMessages(), (x) => !revert || x.id < revert)
|
|
|
+ if (!message) return
|
|
|
+
|
|
|
+ await sdk.client.session.revert({ sessionID, messageID: message.id })
|
|
|
+ const parts = sync.data.part[message.id]
|
|
|
+ if (parts) {
|
|
|
+ const restored = extractPromptFromParts(parts, { directory: sdk.directory })
|
|
|
+ prompt.set(restored)
|
|
|
+ }
|
|
|
+
|
|
|
+ const prev = findLast(userMessages(), (x) => x.id < message.id)
|
|
|
+ setActiveMessage(prev)
|
|
|
+ }
|
|
|
+
|
|
|
+ const redo = async () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (!sessionID) return
|
|
|
+
|
|
|
+ const revertMessageID = info()?.revert?.messageID
|
|
|
+ if (!revertMessageID) return
|
|
|
+
|
|
|
+ const next = userMessages().find((x) => x.id > revertMessageID)
|
|
|
+ if (!next) {
|
|
|
+ await sdk.client.session.unrevert({ sessionID })
|
|
|
+ prompt.reset()
|
|
|
+ const last = findLast(userMessages(), (x) => x.id >= revertMessageID)
|
|
|
+ setActiveMessage(last)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ await sdk.client.session.revert({ sessionID, messageID: next.id })
|
|
|
+ const prev = findLast(userMessages(), (x) => x.id < next.id)
|
|
|
+ setActiveMessage(prev)
|
|
|
+ }
|
|
|
+
|
|
|
+ const compact = async () => {
|
|
|
+ const sessionID = params.id
|
|
|
+ if (!sessionID) return
|
|
|
+
|
|
|
+ const model = local.model.current()
|
|
|
+ if (!model) {
|
|
|
+ showToast({
|
|
|
+ title: language.t("toast.model.none.title"),
|
|
|
+ description: language.t("toast.model.none.description"),
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ await sdk.client.session.summarize({
|
|
|
+ sessionID,
|
|
|
+ modelID: model.id,
|
|
|
+ providerID: model.provider.id,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const fork = () => {
|
|
|
+ void import("@/components/dialog-fork").then((x) => {
|
|
|
+ dialog.show(() => <x.DialogFork />)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const shareCmds = () => {
|
|
|
+ if (sync.data.config.share === "disabled") return []
|
|
|
return [
|
|
|
sessionCommand({
|
|
|
- id: "session.new",
|
|
|
- title: language.t("command.session.new"),
|
|
|
- keybind: "mod+shift+s",
|
|
|
- slash: "new",
|
|
|
- onSelect: () => navigate(`/${params.dir}/session`),
|
|
|
- }),
|
|
|
- fileCommand({
|
|
|
- id: "file.open",
|
|
|
- title: language.t("command.file.open"),
|
|
|
- description: language.t("palette.search.placeholder"),
|
|
|
- keybind: "mod+k,mod+p",
|
|
|
- slash: "open",
|
|
|
- onSelect: () => {
|
|
|
- void import("@/components/dialog-select-file").then((x) => {
|
|
|
- dialog.show(() => <x.DialogSelectFile onOpenFile={showAllFiles} />)
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- fileCommand({
|
|
|
- id: "tab.close",
|
|
|
- title: language.t("command.tab.close"),
|
|
|
- keybind: "mod+w",
|
|
|
- disabled: !closableTab(),
|
|
|
- onSelect: () => {
|
|
|
- const tab = closableTab()
|
|
|
- if (!tab) return
|
|
|
- tabs().close(tab)
|
|
|
- },
|
|
|
- }),
|
|
|
- contextCommand({
|
|
|
- id: "context.addSelection",
|
|
|
- title: language.t("command.context.addSelection"),
|
|
|
- description: language.t("command.context.addSelection.description"),
|
|
|
- keybind: "mod+shift+l",
|
|
|
- disabled: !canAddSelectionContext(),
|
|
|
- onSelect: () => {
|
|
|
- const tab = activeFileTab()
|
|
|
- if (!tab) return
|
|
|
- const path = file.pathFromTab(tab)
|
|
|
- if (!path) return
|
|
|
-
|
|
|
- const range = file.selectedLines(path) as SelectedLineRange | null | undefined
|
|
|
- if (!range) {
|
|
|
- showToast({
|
|
|
- title: language.t("toast.context.noLineSelection.title"),
|
|
|
- description: language.t("toast.context.noLineSelection.description"),
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- addSelectionToContext(path, selectionFromLines(range))
|
|
|
- },
|
|
|
- }),
|
|
|
- viewCommand({
|
|
|
- id: "terminal.toggle",
|
|
|
- title: language.t("command.terminal.toggle"),
|
|
|
- keybind: "ctrl+`",
|
|
|
- slash: "terminal",
|
|
|
- onSelect: () => view().terminal.toggle(),
|
|
|
- }),
|
|
|
- viewCommand({
|
|
|
- id: "review.toggle",
|
|
|
- title: language.t("command.review.toggle"),
|
|
|
- keybind: "mod+shift+r",
|
|
|
- onSelect: () => view().reviewPanel.toggle(),
|
|
|
- }),
|
|
|
- viewCommand({
|
|
|
- id: "fileTree.toggle",
|
|
|
- title: language.t("command.fileTree.toggle"),
|
|
|
- keybind: "mod+\\",
|
|
|
- onSelect: () => layout.fileTree.toggle(),
|
|
|
- }),
|
|
|
- viewCommand({
|
|
|
- id: "input.focus",
|
|
|
- title: language.t("command.input.focus"),
|
|
|
- keybind: "ctrl+l",
|
|
|
- onSelect: focusInput,
|
|
|
- }),
|
|
|
- terminalCommand({
|
|
|
- id: "terminal.new",
|
|
|
- title: language.t("command.terminal.new"),
|
|
|
- description: language.t("command.terminal.new.description"),
|
|
|
- keybind: "ctrl+alt+t",
|
|
|
- onSelect: () => {
|
|
|
- if (terminal.all().length > 0) terminal.new()
|
|
|
- view().terminal.open()
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "message.previous",
|
|
|
- title: language.t("command.message.previous"),
|
|
|
- description: language.t("command.message.previous.description"),
|
|
|
- keybind: "mod+alt+[",
|
|
|
+ id: "session.share",
|
|
|
+ title: info()?.share?.url ? language.t("session.share.copy.copyLink") : language.t("command.session.share"),
|
|
|
+ description: info()?.share?.url
|
|
|
+ ? language.t("toast.session.share.success.description")
|
|
|
+ : language.t("command.session.share.description"),
|
|
|
+ slash: "share",
|
|
|
disabled: !params.id,
|
|
|
- onSelect: () => navigateMessageByOffset(-1),
|
|
|
+ onSelect: share,
|
|
|
}),
|
|
|
sessionCommand({
|
|
|
- id: "message.next",
|
|
|
- title: language.t("command.message.next"),
|
|
|
- description: language.t("command.message.next.description"),
|
|
|
- keybind: "mod+alt+]",
|
|
|
- disabled: !params.id,
|
|
|
- onSelect: () => navigateMessageByOffset(1),
|
|
|
- }),
|
|
|
- modelCommand({
|
|
|
- id: "model.choose",
|
|
|
- title: language.t("command.model.choose"),
|
|
|
- description: language.t("command.model.choose.description"),
|
|
|
- keybind: "mod+'",
|
|
|
- slash: "model",
|
|
|
- onSelect: () => {
|
|
|
- void import("@/components/dialog-select-model").then((x) => {
|
|
|
- dialog.show(() => <x.DialogSelectModel model={local.model} />)
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- mcpCommand({
|
|
|
- id: "mcp.toggle",
|
|
|
- title: language.t("command.mcp.toggle"),
|
|
|
- description: language.t("command.mcp.toggle.description"),
|
|
|
- keybind: "mod+;",
|
|
|
- slash: "mcp",
|
|
|
- onSelect: () => {
|
|
|
- void import("@/components/dialog-select-mcp").then((x) => {
|
|
|
- dialog.show(() => <x.DialogSelectMcp />)
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- agentCommand({
|
|
|
- id: "agent.cycle",
|
|
|
- title: language.t("command.agent.cycle"),
|
|
|
- description: language.t("command.agent.cycle.description"),
|
|
|
- keybind: "mod+.",
|
|
|
- slash: "agent",
|
|
|
- onSelect: () => local.agent.move(1),
|
|
|
- }),
|
|
|
- agentCommand({
|
|
|
- id: "agent.cycle.reverse",
|
|
|
- title: language.t("command.agent.cycle.reverse"),
|
|
|
- description: language.t("command.agent.cycle.reverse.description"),
|
|
|
- keybind: "shift+mod+.",
|
|
|
- onSelect: () => local.agent.move(-1),
|
|
|
+ id: "session.unshare",
|
|
|
+ title: language.t("command.session.unshare"),
|
|
|
+ description: language.t("command.session.unshare.description"),
|
|
|
+ slash: "unshare",
|
|
|
+ disabled: !params.id || !info()?.share?.url,
|
|
|
+ onSelect: unshare,
|
|
|
}),
|
|
|
- modelCommand({
|
|
|
- id: "model.variant.cycle",
|
|
|
- title: language.t("command.model.variant.cycle"),
|
|
|
- description: language.t("command.model.variant.cycle.description"),
|
|
|
- keybind: "shift+mod+d",
|
|
|
- onSelect: () => local.model.variant.cycle(),
|
|
|
- }),
|
|
|
- permissionsCommand({
|
|
|
- id: "permissions.autoaccept",
|
|
|
- title: isAutoAcceptActive()
|
|
|
- ? language.t("command.permissions.autoaccept.disable")
|
|
|
- : language.t("command.permissions.autoaccept.enable"),
|
|
|
- keybind: "mod+shift+a",
|
|
|
- disabled: false,
|
|
|
- onSelect: () => {
|
|
|
- const sessionID = params.id
|
|
|
- if (sessionID) permission.toggleAutoAccept(sessionID, sdk.directory)
|
|
|
- else permission.toggleAutoAcceptDirectory(sdk.directory)
|
|
|
-
|
|
|
- const active = sessionID
|
|
|
- ? permission.isAutoAccepting(sessionID, sdk.directory)
|
|
|
- : permission.isAutoAcceptingDirectory(sdk.directory)
|
|
|
- showToast({
|
|
|
- title: active
|
|
|
- ? language.t("toast.permissions.autoaccept.on.title")
|
|
|
- : language.t("toast.permissions.autoaccept.off.title"),
|
|
|
- description: active
|
|
|
- ? language.t("toast.permissions.autoaccept.on.description")
|
|
|
- : language.t("toast.permissions.autoaccept.off.description"),
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "session.undo",
|
|
|
- title: language.t("command.session.undo"),
|
|
|
- description: language.t("command.session.undo.description"),
|
|
|
- slash: "undo",
|
|
|
- disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
- onSelect: async () => {
|
|
|
- const sessionID = params.id
|
|
|
- if (!sessionID) return
|
|
|
- if (status().type !== "idle") {
|
|
|
- await sdk.client.session.abort({ sessionID }).catch(() => {})
|
|
|
- }
|
|
|
- const revert = info()?.revert?.messageID
|
|
|
- const message = findLast(userMessages(), (x) => !revert || x.id < revert)
|
|
|
- if (!message) return
|
|
|
- await sdk.client.session.revert({ sessionID, messageID: message.id })
|
|
|
- const parts = sync.data.part[message.id]
|
|
|
- if (parts) {
|
|
|
- const restored = extractPromptFromParts(parts, { directory: sdk.directory })
|
|
|
- prompt.set(restored)
|
|
|
- }
|
|
|
- const priorMessage = findLast(userMessages(), (x) => x.id < message.id)
|
|
|
- setActiveMessage(priorMessage)
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "session.redo",
|
|
|
- title: language.t("command.session.redo"),
|
|
|
- description: language.t("command.session.redo.description"),
|
|
|
- slash: "redo",
|
|
|
- disabled: !params.id || !info()?.revert?.messageID,
|
|
|
- onSelect: async () => {
|
|
|
- const sessionID = params.id
|
|
|
- if (!sessionID) return
|
|
|
- const revertMessageID = info()?.revert?.messageID
|
|
|
- if (!revertMessageID) return
|
|
|
- const nextMessage = userMessages().find((x) => x.id > revertMessageID)
|
|
|
- if (!nextMessage) {
|
|
|
- await sdk.client.session.unrevert({ sessionID })
|
|
|
- prompt.reset()
|
|
|
- const lastMsg = findLast(userMessages(), (x) => x.id >= revertMessageID)
|
|
|
- setActiveMessage(lastMsg)
|
|
|
- return
|
|
|
- }
|
|
|
- await sdk.client.session.revert({ sessionID, messageID: nextMessage.id })
|
|
|
- const priorMsg = findLast(userMessages(), (x) => x.id < nextMessage.id)
|
|
|
- setActiveMessage(priorMsg)
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "session.compact",
|
|
|
- title: language.t("command.session.compact"),
|
|
|
- description: language.t("command.session.compact.description"),
|
|
|
- slash: "compact",
|
|
|
- disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
- onSelect: async () => {
|
|
|
- const sessionID = params.id
|
|
|
- if (!sessionID) return
|
|
|
- const model = local.model.current()
|
|
|
- if (!model) {
|
|
|
- showToast({
|
|
|
- title: language.t("toast.model.none.title"),
|
|
|
- description: language.t("toast.model.none.description"),
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- await sdk.client.session.summarize({
|
|
|
- sessionID,
|
|
|
- modelID: model.id,
|
|
|
- providerID: model.provider.id,
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- sessionCommand({
|
|
|
- id: "session.fork",
|
|
|
- title: language.t("command.session.fork"),
|
|
|
- description: language.t("command.session.fork.description"),
|
|
|
- slash: "fork",
|
|
|
- disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
- onSelect: () => {
|
|
|
- void import("@/components/dialog-fork").then((x) => {
|
|
|
- dialog.show(() => <x.DialogFork />)
|
|
|
- })
|
|
|
- },
|
|
|
- }),
|
|
|
- ...share,
|
|
|
]
|
|
|
- })
|
|
|
+ }
|
|
|
+
|
|
|
+ const sessionCmds = () => [
|
|
|
+ sessionCommand({
|
|
|
+ id: "session.new",
|
|
|
+ title: language.t("command.session.new"),
|
|
|
+ keybind: "mod+shift+s",
|
|
|
+ slash: "new",
|
|
|
+ onSelect: () => navigate(`/${params.dir}/session`),
|
|
|
+ }),
|
|
|
+ sessionCommand({
|
|
|
+ id: "session.undo",
|
|
|
+ title: language.t("command.session.undo"),
|
|
|
+ description: language.t("command.session.undo.description"),
|
|
|
+ slash: "undo",
|
|
|
+ disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
+ onSelect: undo,
|
|
|
+ }),
|
|
|
+ sessionCommand({
|
|
|
+ id: "session.redo",
|
|
|
+ title: language.t("command.session.redo"),
|
|
|
+ description: language.t("command.session.redo.description"),
|
|
|
+ slash: "redo",
|
|
|
+ disabled: !params.id || !info()?.revert?.messageID,
|
|
|
+ onSelect: redo,
|
|
|
+ }),
|
|
|
+ sessionCommand({
|
|
|
+ id: "session.compact",
|
|
|
+ title: language.t("command.session.compact"),
|
|
|
+ description: language.t("command.session.compact.description"),
|
|
|
+ slash: "compact",
|
|
|
+ disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
+ onSelect: compact,
|
|
|
+ }),
|
|
|
+ sessionCommand({
|
|
|
+ id: "session.fork",
|
|
|
+ title: language.t("command.session.fork"),
|
|
|
+ description: language.t("command.session.fork.description"),
|
|
|
+ slash: "fork",
|
|
|
+ disabled: !params.id || visibleUserMessages().length === 0,
|
|
|
+ onSelect: fork,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const fileCmds = () => [
|
|
|
+ fileCommand({
|
|
|
+ id: "file.open",
|
|
|
+ title: language.t("command.file.open"),
|
|
|
+ description: language.t("palette.search.placeholder"),
|
|
|
+ keybind: "mod+k,mod+p",
|
|
|
+ slash: "open",
|
|
|
+ onSelect: openFile,
|
|
|
+ }),
|
|
|
+ fileCommand({
|
|
|
+ id: "tab.close",
|
|
|
+ title: language.t("command.tab.close"),
|
|
|
+ keybind: "mod+w",
|
|
|
+ disabled: !closableTab(),
|
|
|
+ onSelect: closeTab,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const contextCmds = () => [
|
|
|
+ contextCommand({
|
|
|
+ id: "context.addSelection",
|
|
|
+ title: language.t("command.context.addSelection"),
|
|
|
+ description: language.t("command.context.addSelection.description"),
|
|
|
+ keybind: "mod+shift+l",
|
|
|
+ disabled: !canAddSelectionContext(),
|
|
|
+ onSelect: addSelection,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const viewCmds = () => [
|
|
|
+ viewCommand({
|
|
|
+ id: "terminal.toggle",
|
|
|
+ title: language.t("command.terminal.toggle"),
|
|
|
+ keybind: "ctrl+`",
|
|
|
+ slash: "terminal",
|
|
|
+ onSelect: () => view().terminal.toggle(),
|
|
|
+ }),
|
|
|
+ viewCommand({
|
|
|
+ id: "review.toggle",
|
|
|
+ title: language.t("command.review.toggle"),
|
|
|
+ keybind: "mod+shift+r",
|
|
|
+ onSelect: () => view().reviewPanel.toggle(),
|
|
|
+ }),
|
|
|
+ viewCommand({
|
|
|
+ id: "fileTree.toggle",
|
|
|
+ title: language.t("command.fileTree.toggle"),
|
|
|
+ keybind: "mod+\\",
|
|
|
+ onSelect: () => layout.fileTree.toggle(),
|
|
|
+ }),
|
|
|
+ viewCommand({
|
|
|
+ id: "input.focus",
|
|
|
+ title: language.t("command.input.focus"),
|
|
|
+ keybind: "ctrl+l",
|
|
|
+ onSelect: focusInput,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const terminalCmds = () => [
|
|
|
+ terminalCommand({
|
|
|
+ id: "terminal.new",
|
|
|
+ title: language.t("command.terminal.new"),
|
|
|
+ description: language.t("command.terminal.new.description"),
|
|
|
+ keybind: "ctrl+alt+t",
|
|
|
+ onSelect: openTerminal,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const messageCmds = () => [
|
|
|
+ sessionCommand({
|
|
|
+ id: "message.previous",
|
|
|
+ title: language.t("command.message.previous"),
|
|
|
+ description: language.t("command.message.previous.description"),
|
|
|
+ keybind: "mod+alt+[",
|
|
|
+ disabled: !params.id,
|
|
|
+ onSelect: () => navigateMessageByOffset(-1),
|
|
|
+ }),
|
|
|
+ sessionCommand({
|
|
|
+ id: "message.next",
|
|
|
+ title: language.t("command.message.next"),
|
|
|
+ description: language.t("command.message.next.description"),
|
|
|
+ keybind: "mod+alt+]",
|
|
|
+ disabled: !params.id,
|
|
|
+ onSelect: () => navigateMessageByOffset(1),
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const modelCmds = () => [
|
|
|
+ modelCommand({
|
|
|
+ id: "model.choose",
|
|
|
+ title: language.t("command.model.choose"),
|
|
|
+ description: language.t("command.model.choose.description"),
|
|
|
+ keybind: "mod+'",
|
|
|
+ slash: "model",
|
|
|
+ onSelect: chooseModel,
|
|
|
+ }),
|
|
|
+ modelCommand({
|
|
|
+ id: "model.variant.cycle",
|
|
|
+ title: language.t("command.model.variant.cycle"),
|
|
|
+ description: language.t("command.model.variant.cycle.description"),
|
|
|
+ keybind: "shift+mod+d",
|
|
|
+ onSelect: () => local.model.variant.cycle(),
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const mcpCmds = () => [
|
|
|
+ mcpCommand({
|
|
|
+ id: "mcp.toggle",
|
|
|
+ title: language.t("command.mcp.toggle"),
|
|
|
+ description: language.t("command.mcp.toggle.description"),
|
|
|
+ keybind: "mod+;",
|
|
|
+ slash: "mcp",
|
|
|
+ onSelect: chooseMcp,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const agentCmds = () => [
|
|
|
+ agentCommand({
|
|
|
+ id: "agent.cycle",
|
|
|
+ title: language.t("command.agent.cycle"),
|
|
|
+ description: language.t("command.agent.cycle.description"),
|
|
|
+ keybind: "mod+.",
|
|
|
+ slash: "agent",
|
|
|
+ onSelect: () => local.agent.move(1),
|
|
|
+ }),
|
|
|
+ agentCommand({
|
|
|
+ id: "agent.cycle.reverse",
|
|
|
+ title: language.t("command.agent.cycle.reverse"),
|
|
|
+ description: language.t("command.agent.cycle.reverse.description"),
|
|
|
+ keybind: "shift+mod+.",
|
|
|
+ onSelect: () => local.agent.move(-1),
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ const permissionsCmds = () => [
|
|
|
+ permissionsCommand({
|
|
|
+ id: "permissions.autoaccept",
|
|
|
+ title: isAutoAcceptActive()
|
|
|
+ ? language.t("command.permissions.autoaccept.disable")
|
|
|
+ : language.t("command.permissions.autoaccept.enable"),
|
|
|
+ keybind: "mod+shift+a",
|
|
|
+ disabled: false,
|
|
|
+ onSelect: toggleAutoAccept,
|
|
|
+ }),
|
|
|
+ ]
|
|
|
+
|
|
|
+ command.register("session", () => [
|
|
|
+ ...sessionCmds(),
|
|
|
+ ...shareCmds(),
|
|
|
+ ...fileCmds(),
|
|
|
+ ...contextCmds(),
|
|
|
+ ...viewCmds(),
|
|
|
+ ...terminalCmds(),
|
|
|
+ ...messageCmds(),
|
|
|
+ ...modelCmds(),
|
|
|
+ ...mcpCmds(),
|
|
|
+ ...agentCmds(),
|
|
|
+ ...permissionsCmds(),
|
|
|
+ ])
|
|
|
}
|