From 057e698f226015a147042df9153480fcc4fec21e Mon Sep 17 00:00:00 2001 From: ypshidifu Date: Tue, 22 Sep 2026 17:49:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9A=94=E7=A6=BB=E7=89=88:?= =?UTF-8?q?=20keymap.layer=20commands=EF=BC=88=E6=97=A0=20bindings?= =?UTF-8?q?=E3=80=81=E6=97=A0=20data.on=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tui-plugins/oc-session-all.js | 158 +++------------------------------- 1 file changed, 11 insertions(+), 147 deletions(-) diff --git a/tui-plugins/oc-session-all.js b/tui-plugins/oc-session-all.js index e411ce5..d9f2ac3 100644 --- a/tui-plugins/oc-session-all.js +++ b/tui-plugins/oc-session-all.js @@ -1,146 +1,16 @@ -/** - * OpenCode v2 TUI Plugin: Show All Sessions - * - * Registers /sessions-all command to show all sessions - * across all projects via a searchable picker dialog. - * Selecting a session navigates directly to it. - * - * Also intercepts MCP connection error toasts to keep them quiet. - */ - import { Plugin } from "@opencode/plugin/tui" -// ── Helpers ──────────────────────────────────────────────────────────── - -function formatDateTime(ms) { - const d = new Date(ms); - const pad = (v) => String(v).padStart(2, "0"); - return `${d.getFullYear()}/${pad(d.getMonth()+1)}/${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; -} - -function projectDisplayName(session, projectMap) { - const project = session.projectID ? projectMap.get(session.projectID) : undefined; - if (project?.name?.trim()) return project.name.trim(); - const worktree = project?.worktree?.replace(/\/+$/, "").split(/[\\/]/).pop(); - if (worktree) return worktree; - const dir = session.directory?.replace(/\/+$/, "").split(/[\\/]/).pop(); - if (dir) return dir; - return session.projectID ? session.projectID.slice(0, 8) : "unknown"; -} - -// ── Plugin ───────────────────────────────────────────────────────────── - export default Plugin.define({ id: "oc-session-all", setup(context) { console.log("[oc-session-all] v2 Plugin loaded!") + context.ui.toast.show({ + message: "[oc-session-all] v2 loaded! Testing keymap...", + variant: "success", + duration: 3000, + }) - // ─── MCP Error Toast Suppression ───────────────────────────────────── - try { - context.data.on("tui.toast.show", (event) => { - const props = event.properties; - if ( - props.variant === "error" && - typeof props.message === "string" && - (props.message.toLowerCase().includes("mcp") || - props.message.toLowerCase().includes("mcp server") || - props.message.toLowerCase().includes("mcp error") || - props.message.toLowerCase().includes("failed to connect") || - props.message.toLowerCase().includes("connection refused")) - ) { - props.variant = "info"; - props.message = "MCP server offline (auto-reconnect when available)"; - props.duration = 2000; - } - }); - } catch (e) { - console.log("[oc-session-all] data.on not available:", e); - } - - // ─── Session Picker Dialog ──────────────────────────────────────────── - async function openPicker() { - let sessions = []; - let projects = []; - - try { - const [sessionResult, projectResult] = await Promise.all([ - context.client.experimental.session.list({ - limit: 1000, - roots: true, - directory: "", - }), - context.client.project.list({}), - ]); - - sessions = sessionResult?.data ?? sessionResult ?? []; - projects = projectResult?.data ?? projectResult ?? []; - } catch (err) { - context.ui.toast.show({ - message: "Failed to load sessions", - variant: "error", - }); - return; - } - - if (!Array.isArray(sessions) || !Array.isArray(projects)) return; - - const projectMap = new Map(projects.map((p) => [p.id, p])); - - const visible = sessions.filter( - (s) => !s.time?.archived && !s.parentID, - ); - - if (visible.length === 0) { - context.ui.toast.show({ - message: "No sessions found", - variant: "warning", - }); - return; - } - - const grouped = new Map(); - for (const session of visible) { - const name = projectDisplayName(session, projectMap); - const list = grouped.get(name) ?? []; - list.push(session); - grouped.set(name, list); - } - - for (const list of grouped.values()) { - list.sort((a, b) => (b.time?.updated ?? 0) - (a.time?.updated ?? 0)); - } - - const orderedProjects = [...grouped.keys()].sort((a, b) => a.localeCompare(b)); - - const options = orderedProjects.flatMap((project) => - grouped.get(project).map((session) => { - const updated = session.time?.updated ? formatDateTime(session.time.updated) : "unknown"; - const title = session.title || "Untitled"; - return { - title: `${project} ${updated} \u2502 ${title}`, - value: session.id, - category: project, - }; - }), - ); - - context.ui.dialog.setSize("xlarge"); - context.ui.dialog.replace( - () => - context.ui.DialogSelect({ - title: "All Sessions", - placeholder: "Search sessions across all projects\u2026", - options, - onSelect: (option) => { - context.ui.dialog.clear(); - context.route.navigate("session", { sessionID: option.value }); - }, - }), - () => {}, - ); - } - - // ─── Register Command + Keybinding ──────────────────────────────────── + // 只注册 command,不注册 binding context.keymap.layer({ commands: [ { @@ -148,18 +18,12 @@ export default Plugin.define({ value: "sessions.all", description: "Browse and switch to any session across all projects", category: "Session Picker", - slash: { name: "sessions-all", aliases: ["resume-all", "continue-all"] }, - onSelect: () => { void openPicker(); }, + slash: { name: "sessions-all" }, + onSelect: () => { + context.ui.toast.show({ message: "sessions-all triggered!", variant: "info" }); + }, }, ], - bindings: [ - { - key: "ctrl+o", - desc: "All sessions picker", - preventDefault: true, - cmd: () => { void openPicker(); }, - }, - ], - }); + }) }, })