| 1 | import type { ProjectNode } from "./types"; |
| 2 | import type { DictKey } from "./i18n"; |
| 3 | import type { SessionSelector } from "../generated/desktopContract.generated"; |
| 4 | |
| 5 | // Keep the RPC's string argument compatible with older topic callers while |
| 6 | // selecting the exact durable row whenever the catalog supplies its identity. |
| 7 | export function sessionTitleTarget(node: ProjectNode): string { |
| 8 | if (node.session?.sessionId) return `session-id:${node.session.sessionId}`; |
| 9 | if (node.source) return `session-source:${encodeURIComponent(JSON.stringify(node.source))}`; |
| 10 | return node.sessionPath?.trim() || node.topicId?.trim() || ""; |
| 11 | } |
| 12 | |
| 13 | export function sessionTitleSelector(target: string): SessionSelector { |
| 14 | const value = target.trim(); |
| 15 | if (value.startsWith("session-source:")) return { source: JSON.parse(decodeURIComponent(value.slice("session-source:".length))) }; |
| 16 | if (value.startsWith("session-id:")) { |
| 17 | return { ref: { hostId: "local", sessionId: value.slice("session-id:".length) } }; |
| 18 | } |
| 19 | if (value.includes("/") || value.includes("\\")) return { sessionPath: value }; |
| 20 | return { topicId: value }; |
| 21 | } |
| 22 | |
| 23 | export function sessionTitleErrorKey(error: unknown): DictKey { |
| 24 | const record = typeof error === "object" && error !== null ? error as Record<string, unknown> : undefined; |
| 25 | const data = record && typeof record.data === "object" && record.data !== null |
| 26 | ? record.data as Record<string, unknown> |
| 27 | : undefined; |
| 28 | const message = error instanceof Error ? error.message : String(error); |
| 29 | const code = typeof data?.sessionCode === "string" |
| 30 | ? data.sessionCode |
| 31 | : message.match(/session_operation:([a-z_]+):/)?.[1]; |
| 32 | const keys: Record<string, DictKey> = { |
| 33 | target_not_found: "projectTree.sessionError.targetNotFound", no_messages: "projectTree.sessionError.noMessages", |
| 34 | ambiguous_target: "projectTree.sessionError.ambiguousTarget", |
| 35 | runtime_not_open: "projectTree.sessionError.runtimeNotOpen", runtime_not_ready: "projectTree.sessionError.runtimeNotReady", |
| 36 | remote_disconnected: "projectTree.sessionError.remoteDisconnected", title_conflict: "projectTree.sessionError.titleConflict", |
| 37 | target_changed: "projectTree.sessionError.targetChanged", archived: "projectTree.sessionError.archived", |
| 38 | operation_busy: "projectTree.sessionError.operationBusy", provider_unavailable: "projectTree.sessionError.providerUnavailable", |
| 39 | stale_cursor: "projectTree.sessionError.staleCursor", unsupported: "projectTree.sessionError.unsupported", |
| 40 | operation_failed: "projectTree.sessionError.failed", |
| 41 | }; |
| 42 | // Providers, disk errors and older hosts can return paths or credentials. |
| 43 | // Never render unclassified backend details in the product toast. |
| 44 | return keys[code ?? ""] ?? "projectTree.sessionError.failed"; |
| 45 | } |
| 46 |