From e4924ebf64930690372fc0d1ec35dd2f993e9ec5 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:57:47 +0800 Subject: [PATCH] feat(app): add copy project ID command (#44126) Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com> --- packages/app/src/runtime/i18n/en.ts | 3 ++ .../session/commands/use-session-commands.tsx | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/app/src/runtime/i18n/en.ts b/packages/app/src/runtime/i18n/en.ts index 0acc1796bb4..fe3b072f462 100644 --- a/packages/app/src/runtime/i18n/en.ts +++ b/packages/app/src/runtime/i18n/en.ts @@ -30,6 +30,7 @@ export const dict = { "command.project.previous": "Previous project", "command.project.next": "Next project", "command.project.index": "Switch to project {{index}}", + "command.project.copyID": "Copy Project ID", "command.provider.connect": "Connect provider", "command.server.switch": "Switch server", "command.settings.open": "Open settings", @@ -556,6 +557,8 @@ export const dict = { "toast.session.export.failed.description": "An error occurred while exporting the session", "toast.session.copyID.failed.title": "Failed to copy session ID", "toast.session.copyID.failed.description": "An error occurred while copying the session ID", + "toast.project.copyID.failed.title": "Failed to copy project ID", + "toast.project.copyID.failed.description": "An error occurred while copying the project ID", "toast.session.listFailed.title": "Failed to load sessions for {{project}}", "toast.project.reloadFailed.title": "Failed to reload {{project}}", diff --git a/packages/app/src/session/commands/use-session-commands.tsx b/packages/app/src/session/commands/use-session-commands.tsx index d4b1f31b215..72e738284a2 100644 --- a/packages/app/src/session/commands/use-session-commands.tsx +++ b/packages/app/src/session/commands/use-session-commands.tsx @@ -89,6 +89,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => { const focusInput = actions.focusInput const sessionCommand = withCategory(language.t("command.category.session")) + const projectCommand = withCategory(language.t("command.category.project")) const fileCommand = withCategory(language.t("command.category.file")) const contextCommand = withCategory(language.t("command.category.context")) const viewCommand = withCategory(language.t("command.category.view")) @@ -146,6 +147,26 @@ export const useSessionCommands = (actions: SessionCommandContext) => { } } + const copyProjectID = async () => { + const projectID = actions.session.data.info()?.projectID + if (!projectID) return + try { + await navigator.clipboard.writeText(projectID) + showToast({ + variant: "success", + icon: "circle-check", + title: language.t("common.copied"), + description: projectID, + }) + } catch (err) { + showToast({ + variant: "error", + title: language.t("toast.project.copyID.failed.title"), + description: err instanceof Error ? err.message : language.t("toast.project.copyID.failed.description"), + }) + } + } + const openFile = () => { void openDialog( () => import("@/shell/commands/dialog"), @@ -320,6 +341,15 @@ export const useSessionCommands = (actions: SessionCommandContext) => { ].filter((v) => !!v) } + const projectCmds = () => [ + projectCommand({ + id: "project.copyID", + title: language.t("command.project.copyID"), + disabled: !actions.session.data.info()?.projectID, + onSelect: copyProjectID, + }), + ] + const contextCmds = () => [ contextCommand({ id: "context.addSelection", @@ -433,6 +463,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => { command.register("session", () => [ ...sessionCmds(), + ...projectCmds(), ...fileCmds(), ...contextCmds(), ...viewCmds(),