feat(sessions): add session browsing for OpenCode and OpenClaw

Add two new session providers following the existing convention-based
pattern. OpenCode reads three-layer JSON storage, OpenClaw parses JSONL
event streams. Wire up backend dispatch, frontend filter dropdown, icon
mappings, toolbar button for OpenClaw, and i18n subtitle updates.
This commit is contained in:
Jason
2026-02-21 18:10:57 +08:00
parent 8b9c09d994
commit 165af5eec4
10 changed files with 533 additions and 10 deletions
+17 -2
View File
@@ -179,7 +179,9 @@ function App() {
if (
currentView === "sessions" &&
activeApp !== "claude" &&
activeApp !== "codex"
activeApp !== "codex" &&
activeApp !== "opencode" &&
activeApp !== "openclaw"
) {
setCurrentView("providers");
}
@@ -223,7 +225,11 @@ function App() {
const providers = useMemo(() => data?.providers ?? {}, [data]);
const currentProviderId = data?.currentProviderId ?? "";
const hasSkillsSupport = true;
const hasSessionSupport = activeApp === "claude" || activeApp === "codex";
const hasSessionSupport =
activeApp === "claude" ||
activeApp === "codex" ||
activeApp === "opencode" ||
activeApp === "openclaw";
const {
addProvider,
@@ -1122,6 +1128,15 @@ function App() {
>
<Cpu className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setCurrentView("sessions")}
className="text-muted-foreground hover:text-foreground hover:bg-black/5 dark:hover:bg-white/5"
title={t("sessionManager.title")}
>
<History className="w-4 h-4" />
</Button>
</>
) : (
<>
+22 -4
View File
@@ -46,7 +46,7 @@ import {
getSessionKey,
} from "./utils";
type ProviderFilter = "all" | "codex" | "claude";
type ProviderFilter = "all" | "codex" | "claude" | "opencode" | "openclaw";
export function SessionManagerPage() {
const { t } = useTranslation();
@@ -265,9 +265,7 @@ export function SessionManagerPage() {
icon={
providerFilter === "all"
? "apps"
: providerFilter === "codex"
? "openai"
: "claude"
: getProviderIconName(providerFilter)
}
name={providerFilter}
size={14}
@@ -309,6 +307,26 @@ export function SessionManagerPage() {
<span>Claude Code</span>
</div>
</SelectItem>
<SelectItem value="opencode">
<div className="flex items-center gap-2">
<ProviderIcon
icon="opencode"
name="opencode"
size={14}
/>
<span>OpenCode</span>
</div>
</SelectItem>
<SelectItem value="openclaw">
<div className="flex items-center gap-2">
<ProviderIcon
icon="openclaw"
name="openclaw"
size={14}
/>
<span>OpenClaw</span>
</div>
</SelectItem>
</SelectContent>
</Select>
+2
View File
@@ -48,6 +48,8 @@ export const getProviderLabel = (
export const getProviderIconName = (providerId: string) => {
if (providerId === "codex") return "openai";
if (providerId === "claude") return "claude";
if (providerId === "opencode") return "opencode";
if (providerId === "openclaw") return "openclaw";
return providerId;
};
+1 -1
View File
@@ -515,7 +515,7 @@
},
"sessionManager": {
"title": "Session Manager",
"subtitle": "Manage Codex and Claude Code sessions",
"subtitle": "Manage Claude Code, Codex, OpenCode and OpenClaw sessions",
"searchPlaceholder": "Search by content, directory, or ID",
"searchSessions": "Search sessions",
"providerFilterAll": "All",
+1 -1
View File
@@ -515,7 +515,7 @@
},
"sessionManager": {
"title": "セッション管理",
"subtitle": "Codex / Claude Code のセッションを管理",
"subtitle": "Claude Code / Codex / OpenCode / OpenClaw のセッションを管理",
"searchPlaceholder": "内容・ディレクトリ・ID で検索",
"searchSessions": "セッションを検索",
"providerFilterAll": "すべて",
+1 -1
View File
@@ -515,7 +515,7 @@
},
"sessionManager": {
"title": "会话管理",
"subtitle": "管理 Codex 与 Claude Code 会话记录",
"subtitle": "管理 Claude Code、Codex、OpenCode 与 OpenClaw 会话记录",
"searchPlaceholder": "搜索会话内容、目录或 ID",
"searchSessions": "搜索会话",
"providerFilterAll": "全部",