添加调试版本插件,修改gitignore允许提交js文件
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
*.js
|
*.js
|
||||||
|
!tui-plugins/oc-session-all-debug.js
|
||||||
|
!tui-plugins/oc-session-all.js
|
||||||
src/
|
src/
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* OpenCode TUI Plugin: Show All Sessions (DEBUG VERSION)
|
||||||
|
*
|
||||||
|
* Minimal version to test if plugin loads without crashes.
|
||||||
|
* Only includes basic MCP toast suppression and keymap.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── Plugin ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const tui = async (api) => {
|
||||||
|
// Log that we're alive
|
||||||
|
console.log("[oc-session-all] Plugin loaded successfully!");
|
||||||
|
|
||||||
|
// ─── MCP Error Toast Suppression ─────────────────────────────────────
|
||||||
|
api.event.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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Minimal keymap (just to test registration works) ─────────────────
|
||||||
|
try {
|
||||||
|
api.keymap.registerLayer({
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
key: "ctrl+o",
|
||||||
|
desc: "All sessions picker (debug)",
|
||||||
|
preventDefault: true,
|
||||||
|
cmd: () => {
|
||||||
|
console.log("[oc-session-all] Ctrl+O pressed!");
|
||||||
|
api.ui.toast({
|
||||||
|
message: "Plugin loaded! Ctrl+O works!",
|
||||||
|
variant: "info",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
console.log("[oc-session-all] Keymap registered successfully!");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[oc-session-all] Keymap registration failed:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Export ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export default { id: "oc-session-all-debug", tui };
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* OpenCode 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.
|
||||||
|
*
|
||||||
|
* TUI format: export default { id, tui }
|
||||||
|
*
|
||||||
|
* Also intercepts MCP connection error toasts to keep them quiet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── Helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function formatDateTime(ms) {
|
||||||
|
const d = new Date(ms);
|
||||||
|
const pad = (v) => String(v).padStart(2, "0");
|
||||||
|
const month = pad(d.getMonth() + 1);
|
||||||
|
const day = pad(d.getDate());
|
||||||
|
const hour = pad(d.getHours());
|
||||||
|
const min = pad(d.getMinutes());
|
||||||
|
return `${d.getFullYear()}/${month}/${day} ${hour}:${min}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const tui = async (api) => {
|
||||||
|
// ─── MCP Error Toast Suppression ─────────────────────────────────────
|
||||||
|
api.event.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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Session Picker Dialog ────────────────────────────────────────────
|
||||||
|
const openPicker = async () => {
|
||||||
|
if (api.mode.current() !== "base") return;
|
||||||
|
|
||||||
|
let sessions = [];
|
||||||
|
let projects = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [sessionResult, projectResult] = await Promise.all([
|
||||||
|
api.client.experimental.session.list({
|
||||||
|
limit: 1000,
|
||||||
|
roots: true,
|
||||||
|
directory: "",
|
||||||
|
}),
|
||||||
|
api.client.project.list({}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
sessions = sessionResult?.data ?? sessionResult ?? [];
|
||||||
|
projects = projectResult?.data ?? projectResult ?? [];
|
||||||
|
} catch (err) {
|
||||||
|
api.ui.toast({
|
||||||
|
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) {
|
||||||
|
api.ui.toast({
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
api.ui.dialog.setSize("xlarge");
|
||||||
|
api.ui.dialog.replace(
|
||||||
|
() =>
|
||||||
|
api.ui.DialogSelect({
|
||||||
|
title: "All Sessions",
|
||||||
|
placeholder: "Search sessions across all projects\u2026",
|
||||||
|
options,
|
||||||
|
onSelect: (option) => {
|
||||||
|
api.ui.dialog.clear();
|
||||||
|
api.route.navigate("session", { sessionID: option.value });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Register Command ─────────────────────────────────────────────────
|
||||||
|
if (api.command) {
|
||||||
|
api.command.register(() => [
|
||||||
|
{
|
||||||
|
title: "All Sessions",
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Keybinding ───────────────────────────────────────────────────────
|
||||||
|
api.keymap.registerLayer({
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
key: "ctrl+o",
|
||||||
|
desc: "All sessions picker",
|
||||||
|
preventDefault: true,
|
||||||
|
cmd: () => void openPicker(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Export ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export default { id: "oc-session-all", tui };
|
||||||
Reference in New Issue
Block a user