@scrylog/cli 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/highlights-eq9cgrbb.scm +604 -0
- package/dist/assets/highlights-ghv9g403.scm +205 -0
- package/dist/assets/highlights-hk7bwhj4.scm +284 -0
- package/dist/assets/highlights-r812a2qc.scm +150 -0
- package/dist/assets/highlights-x6tmsnaa.scm +115 -0
- package/dist/assets/injections-73j83es3.scm +27 -0
- package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
- package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/cli/index.js +499 -0
- package/dist/cli/index.js.map +61 -0
- package/dist/daemon/index.js +670 -0
- package/dist/daemon/index.js.map +1475 -0
- package/dist/hooks/claude.js +6 -0
- package/dist/hooks/claude.js.map +11 -0
- package/dist/hooks/codex.js +10 -0
- package/dist/hooks/codex.js.map +11 -0
- package/dist/mcp/index.js +100 -0
- package/dist/mcp/index.js.map +231 -0
- package/dist/oc/index.js +21 -0
- package/dist/oc/index.js.map +10 -0
- package/package.json +16 -13
- package/src/api/docs/sessions.keys.ts +0 -6
- package/src/api/docs/sessions.ts +0 -82
- package/src/api/docs/system.keys.ts +0 -5
- package/src/api/docs/system.ts +0 -69
- package/src/api/docs/usage.keys.ts +0 -3
- package/src/api/docs/usage.ts +0 -24
- package/src/api/docs/webhooks.keys.ts +0 -5
- package/src/api/docs/webhooks.ts +0 -78
- package/src/api/index.ts +0 -18
- package/src/api/lib/auth.ts +0 -43
- package/src/api/lib/errors.ts +0 -28
- package/src/api/lib/route.ts +0 -18
- package/src/api/models/claude.ts +0 -4
- package/src/api/models/codex.ts +0 -4
- package/src/api/models/opencode.ts +0 -11
- package/src/api/routes/sessions.ts +0 -51
- package/src/api/routes/system.ts +0 -106
- package/src/api/routes/usage.ts +0 -15
- package/src/api/routes/webhooks/claude.ts +0 -149
- package/src/api/routes/webhooks/codex.ts +0 -175
- package/src/api/routes/webhooks/opencode.ts +0 -112
- package/src/api/routes.ts +0 -93
- package/src/api/schemas/common.ts +0 -29
- package/src/api/schemas/sessions.ts +0 -86
- package/src/api/schemas/usage.ts +0 -51
- package/src/api/schemas/webhooks/claude.ts +0 -32
- package/src/api/schemas/webhooks/codex.ts +0 -34
- package/src/api/schemas/webhooks/opencode.ts +0 -29
- package/src/api/services/claude-session.ts +0 -9
- package/src/api/services/codex-session.ts +0 -8
- package/src/api/services/opencode-session.ts +0 -73
- package/src/classifier.ts +0 -206
- package/src/cli/commands.ts +0 -714
- package/src/cli/index.ts +0 -18
- package/src/cli/program.ts +0 -175
- package/src/cli/prompt.ts +0 -44
- package/src/daemon/cli.ts +0 -27
- package/src/daemon/collectors/claude/README.md +0 -108
- package/src/daemon/collectors/claude/collector.ts +0 -423
- package/src/daemon/collectors/claude/discovery.ts +0 -127
- package/src/daemon/collectors/claude/hooks.ts +0 -181
- package/src/daemon/collectors/claude/index.ts +0 -8
- package/src/daemon/collectors/claude/jsonl.ts +0 -85
- package/src/daemon/collectors/claude/transcript.ts +0 -246
- package/src/daemon/collectors/codex/README.md +0 -147
- package/src/daemon/collectors/codex/collector.ts +0 -661
- package/src/daemon/collectors/codex/discovery.ts +0 -104
- package/src/daemon/collectors/codex/hooks.ts +0 -179
- package/src/daemon/collectors/codex/index.ts +0 -7
- package/src/daemon/collectors/codex/jsonl.ts +0 -108
- package/src/daemon/collectors/codex/transcript.ts +0 -299
- package/src/daemon/collectors/git.ts +0 -353
- package/src/daemon/collectors/herdr/collector.ts +0 -128
- package/src/daemon/collectors/herdr/index.ts +0 -2
- package/src/daemon/collectors/herdr/normalization.ts +0 -111
- package/src/daemon/collectors/opencode/README.md +0 -106
- package/src/daemon/collectors/opencode/collector.ts +0 -545
- package/src/daemon/collectors/opencode/discovery.ts +0 -257
- package/src/daemon/collectors/opencode/index.ts +0 -5
- package/src/daemon/collectors/opencode/session-builder.ts +0 -59
- package/src/daemon/collectors/opencode-db.ts +0 -294
- package/src/daemon/collectors/process.ts +0 -201
- package/src/daemon/collectors/tmux/README.md +0 -128
- package/src/daemon/collectors/tmux/collector.ts +0 -492
- package/src/daemon/collectors/tmux/enrichment.ts +0 -242
- package/src/daemon/collectors/tmux/index.ts +0 -4
- package/src/daemon/collectors/tmux/parse.ts +0 -128
- package/src/daemon/collectors/usage.ts +0 -259
- package/src/daemon/index.ts +0 -209
- package/src/daemon/scheduler.ts +0 -86
- package/src/daemon/store.ts +0 -630
- package/src/detect.ts +0 -143
- package/src/hooks/claude.ts +0 -596
- package/src/hooks/codex.ts +0 -517
- package/src/legacy/classifier.ts +0 -215
- package/src/legacy/detect.ts +0 -143
- package/src/mcp/client-server.ts +0 -21
- package/src/mcp/http-handler.ts +0 -126
- package/src/mcp/index.ts +0 -61
- package/src/mcp/resources/config-current.ts +0 -19
- package/src/mcp/resources/index.ts +0 -11
- package/src/mcp/resources/sessions-active.ts +0 -19
- package/src/mcp/resources/sessions-all.ts +0 -19
- package/src/mcp/schemas/sessions.ts +0 -3
- package/src/mcp/server.ts +0 -17
- package/src/mcp/services/api-adapter.ts +0 -48
- package/src/mcp/services/data-source.ts +0 -22
- package/src/mcp/services/store-adapter.ts +0 -40
- package/src/mcp/tools/get-active-sessions.ts +0 -16
- package/src/mcp/tools/get-session-children.ts +0 -24
- package/src/mcp/tools/get-session-messages.ts +0 -24
- package/src/mcp/tools/get-session.ts +0 -23
- package/src/mcp/tools/index.ts +0 -15
- package/src/mcp/tools/list-sessions.ts +0 -16
- package/src/oc/index.ts +0 -302
- package/src/service/manager.ts +0 -284
- package/src/shared/config.ts +0 -511
- package/src/shared/ipc.ts +0 -80
- package/src/shared/logger.ts +0 -248
- package/src/shared/paths.ts +0 -73
- package/src/shared/platform.ts +0 -110
- package/src/shared/session-exclusions.ts +0 -5
- package/src/shared/session-view.ts +0 -302
- package/src/shared/terminal-capabilities.ts +0 -130
- package/src/shared/types.ts +0 -330
- package/src/shared/utils.ts +0 -339
- package/src/shared/version.ts +0 -44
- package/src/tui/App.tsx +0 -240
- package/src/tui/client.ts +0 -163
- package/src/tui/components/BindingsModal.tsx +0 -82
- package/src/tui/components/DashboardHeader.tsx +0 -256
- package/src/tui/components/KeyBar.tsx +0 -67
- package/src/tui/components/SessionDetailModal.tsx +0 -169
- package/src/tui/components/SessionsView.tsx +0 -184
- package/src/tui/components/TabBar.tsx +0 -40
- package/src/tui/components/UsageSummaryPane.tsx +0 -272
- package/src/tui/components/UsageView.tsx +0 -242
- package/src/tui/components/tool-icons.ts +0 -24
- package/src/tui/index.tsx +0 -106
- package/src/tui/keybindings.ts +0 -49
- package/src/tui/model.ts +0 -295
- package/src/tui/state.ts +0 -77
- package/src/tui/theme.ts +0 -110
- package/src/types.ts +0 -113
- package/src/usage/README.md +0 -323
- package/src/usage/api.ts +0 -99
- package/src/usage/client.ts +0 -3
- package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
- package/src/usage/clients/anthropic-web-usage.ts +0 -329
- package/src/usage/clients/claude-cli-probe.ts +0 -513
- package/src/usage/clients/codex-app-server.ts +0 -354
- package/src/usage/clients/codex-oauth-usage.ts +0 -240
- package/src/usage/clients/opencode-go-api.ts +0 -100
- package/src/usage/clients/opencode-go-usage.ts +0 -577
- package/src/usage/errors.ts +0 -33
- package/src/usage/index.ts +0 -20
- package/src/usage/local/opencode-go-local.ts +0 -194
- package/src/usage/mappers.ts +0 -282
- package/src/usage/planner.ts +0 -118
- package/src/usage/providers/claude.ts +0 -233
- package/src/usage/providers/codex.ts +0 -115
- package/src/usage/providers/opencode-go.ts +0 -115
- package/src/usage/resolver.ts +0 -4
- package/src/usage/resolvers/browser-cookies.ts +0 -808
- package/src/usage/resolvers/claude-auth.ts +0 -208
- package/src/usage/resolvers/claude-cli.ts +0 -24
- package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
- package/src/usage/resolvers/claude-web.ts +0 -82
- package/src/usage/resolvers/codex-auth.ts +0 -174
- package/src/usage/resolvers/keychain-coordinator.ts +0 -397
- package/src/usage/resolvers/opencode-go-web.ts +0 -55
- package/src/usage/types.ts +0 -225
- package/src/usage/web-session-cache.ts +0 -37
- /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
package/src/tui/keybindings.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { DashboardView } from "./model";
|
|
2
|
-
|
|
3
|
-
export type BindingScope = "global" | DashboardView;
|
|
4
|
-
|
|
5
|
-
export interface KeyBinding {
|
|
6
|
-
key: string;
|
|
7
|
-
description: string;
|
|
8
|
-
scope: BindingScope;
|
|
9
|
-
essential?: boolean;
|
|
10
|
-
footer?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const KEYBINDINGS: KeyBinding[] = [
|
|
14
|
-
{ key: "Tab", description: "Switch tab", scope: "global", essential: true },
|
|
15
|
-
{ key: "q", description: "Quit", scope: "global", essential: true },
|
|
16
|
-
{ key: "r", description: "Refresh", scope: "global" },
|
|
17
|
-
{ key: "s", description: "Open the sessions tab", scope: "global", essential: true, footer: false },
|
|
18
|
-
{ key: "u", description: "Open the usage tab", scope: "global", essential: true, footer: false },
|
|
19
|
-
{ key: "?", description: "Keybindings", scope: "global", essential: true },
|
|
20
|
-
|
|
21
|
-
{ key: "j / Down", description: "Next session", scope: "sessions" },
|
|
22
|
-
{ key: "k / Up", description: "Prev session", scope: "sessions" },
|
|
23
|
-
{ key: "Enter", description: "Detail", scope: "sessions" },
|
|
24
|
-
{ key: "t", description: "Attach", scope: "sessions" },
|
|
25
|
-
{ key: "/", description: "Search", scope: "sessions" },
|
|
26
|
-
{ key: "a", description: "Actionable filter", scope: "sessions" },
|
|
27
|
-
{ key: "f", description: "Cycle filters", scope: "sessions" },
|
|
28
|
-
{ key: "Esc", description: "Clear selection or close overlay", scope: "sessions" },
|
|
29
|
-
|
|
30
|
-
{ key: "Click", description: "Select a session", scope: "sessions", footer: false },
|
|
31
|
-
{ key: "2x Click", description: "Open session detail", scope: "sessions", footer: false },
|
|
32
|
-
{ key: "Scroll", description: "Select next or previous session", scope: "sessions", footer: false },
|
|
33
|
-
{ key: "Click tab", description: "Switch tabs", scope: "global", footer: false },
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
export function contextBindings(view: DashboardView): KeyBinding[] {
|
|
37
|
-
return KEYBINDINGS.filter((binding) => binding.scope === "global" || binding.scope === view);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function essentialBindings(view: DashboardView): KeyBinding[] {
|
|
41
|
-
return contextBindings(view).filter((binding) => binding.essential);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function bindingsForModal(): { scope: BindingScope; bindings: KeyBinding[] }[] {
|
|
45
|
-
const scopes: BindingScope[] = ["global", "sessions", "usage"];
|
|
46
|
-
return scopes
|
|
47
|
-
.map((scope) => ({ scope, bindings: KEYBINDINGS.filter((binding) => binding.scope === scope) }))
|
|
48
|
-
.filter((group) => group.bindings.length > 0);
|
|
49
|
-
}
|
package/src/tui/model.ts
DELETED
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import type { DetectionReason, ProcessInfo, SessionModel, SessionState, SessionTool } from "../shared/types";
|
|
2
|
-
import type { SessionListItem } from "../shared/session-view";
|
|
3
|
-
import { formatDuration, truncate } from "../shared/utils";
|
|
4
|
-
import { BRAND } from "./theme";
|
|
5
|
-
|
|
6
|
-
export type DashboardView = "sessions" | "usage";
|
|
7
|
-
export type DashboardMode = "list" | "search" | "detail" | "message" | "bindings";
|
|
8
|
-
export type SessionToolFilter = SessionTool | "all";
|
|
9
|
-
export type SessionStateFilter = SessionState | "all";
|
|
10
|
-
|
|
11
|
-
export interface DashboardFilters {
|
|
12
|
-
query: string;
|
|
13
|
-
actionableOnly: boolean;
|
|
14
|
-
tool: SessionToolFilter;
|
|
15
|
-
state: SessionStateFilter;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type DashboardRow = ProjectHeaderRow | SessionRow | GapRow;
|
|
19
|
-
|
|
20
|
-
export interface ProjectHeaderRow {
|
|
21
|
-
kind: "project-header";
|
|
22
|
-
projectKey: string;
|
|
23
|
-
projectName: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface SessionRow {
|
|
27
|
-
kind: "session";
|
|
28
|
-
projectKey: string;
|
|
29
|
-
session: SessionListItem;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface GapRow {
|
|
33
|
-
kind: "gap";
|
|
34
|
-
projectKey: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface SessionColumnWidths {
|
|
38
|
-
session: number;
|
|
39
|
-
state: number;
|
|
40
|
-
tool: number;
|
|
41
|
-
summary: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const TOOL_FILTERS: SessionToolFilter[] = ["all", "opencode", "claude", "codex", "copilotcli", "shell", "unknown"];
|
|
45
|
-
export const STATE_FILTERS: SessionStateFilter[] = ["all", "working", "waiting-input", "idle", "shell", "unknown"];
|
|
46
|
-
export const WORKING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
47
|
-
|
|
48
|
-
export const STATE_COLORS: Record<string, string> = {
|
|
49
|
-
working: BRAND.stateWorking,
|
|
50
|
-
idle: BRAND.stateIdle,
|
|
51
|
-
"waiting-input": BRAND.violet,
|
|
52
|
-
shell: BRAND.accentBlue,
|
|
53
|
-
error: BRAND.danger,
|
|
54
|
-
unknown: BRAND.grey,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export function applySessionFilters(
|
|
58
|
-
sessions: SessionListItem[],
|
|
59
|
-
filters: DashboardFilters,
|
|
60
|
-
standaloneMaxAgeHours: number,
|
|
61
|
-
): SessionListItem[] {
|
|
62
|
-
const query = filters.query.toLowerCase();
|
|
63
|
-
const cutoff = standaloneMaxAgeHours > 0 ? Date.now() - standaloneMaxAgeHours * 3_600_000 : 0;
|
|
64
|
-
return sessions.filter((session) => {
|
|
65
|
-
if (session.parentSessionId) {
|
|
66
|
-
if (session.state !== "working" && session.state !== "waiting-input") return false;
|
|
67
|
-
} else if (cutoff > 0 && session.source === "standalone" && session.lastActivity < cutoff) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
if (filters.actionableOnly && !(session.hasAttention ?? session.state === "waiting-input")) return false;
|
|
71
|
-
if (filters.tool !== "all" && session.tool !== filters.tool) return false;
|
|
72
|
-
if (filters.state !== "all" && session.state !== filters.state) return false;
|
|
73
|
-
if (!query) return true;
|
|
74
|
-
const searchable = [
|
|
75
|
-
session.id,
|
|
76
|
-
session.tool,
|
|
77
|
-
session.state,
|
|
78
|
-
session.summary,
|
|
79
|
-
session.project?.displayName,
|
|
80
|
-
session.project?.compactLabel,
|
|
81
|
-
session.project?.branch,
|
|
82
|
-
session.displayName,
|
|
83
|
-
session.tmux?.session,
|
|
84
|
-
session.herdr?.workspace,
|
|
85
|
-
session.herdr?.name,
|
|
86
|
-
session.herdr?.tab,
|
|
87
|
-
session.herdr?.agent,
|
|
88
|
-
session.currentPath,
|
|
89
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
90
|
-
return searchable.includes(query);
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function cycleFilters(filters: DashboardFilters): DashboardFilters {
|
|
95
|
-
const nextTool = TOOL_FILTERS[(TOOL_FILTERS.indexOf(filters.tool) + 1) % TOOL_FILTERS.length] ?? "all";
|
|
96
|
-
if (nextTool !== "all") return { ...filters, tool: nextTool, state: "all" };
|
|
97
|
-
const nextState = STATE_FILTERS[(STATE_FILTERS.indexOf(filters.state) + 1) % STATE_FILTERS.length] ?? "all";
|
|
98
|
-
return { ...filters, tool: "all", state: nextState };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function sessionSortWeight(session: SessionListItem): number {
|
|
102
|
-
// Active work first (working/waiting), then idle workloads.
|
|
103
|
-
const statePenalty = session.state === "working" ? 0 : session.state === "waiting-input" ? 20 : 60;
|
|
104
|
-
const toolPenalty = session.tool === "unknown" ? 30 : session.tool === "shell" ? 20 : 0;
|
|
105
|
-
return statePenalty + toolPenalty;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function groupSortWeight(group: { sessions: SessionListItem[] }): number {
|
|
109
|
-
const minWeight = Math.min(...group.sessions.map(sessionSortWeight));
|
|
110
|
-
return group.sessions.every((session) => !session.project) ? 1000 + minWeight : minWeight;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function buildRows(sessions: SessionListItem[], narrow = false): DashboardRow[] {
|
|
114
|
-
const roots = sessions.filter((session) => !session.parentSessionId);
|
|
115
|
-
const childrenByParent = new Map<string, SessionListItem[]>();
|
|
116
|
-
for (const session of sessions) {
|
|
117
|
-
if (!session.parentSessionId) continue;
|
|
118
|
-
const children = childrenByParent.get(session.parentSessionId) ?? [];
|
|
119
|
-
children.push(session);
|
|
120
|
-
childrenByParent.set(session.parentSessionId, children);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const groups = new Map<string, { projectName: string; sessions: SessionListItem[] }>();
|
|
124
|
-
for (const session of roots) {
|
|
125
|
-
const key = getProjectGroupKey(session);
|
|
126
|
-
const group = groups.get(key);
|
|
127
|
-
if (group) {
|
|
128
|
-
group.sessions.push(session);
|
|
129
|
-
} else {
|
|
130
|
-
groups.set(key, {
|
|
131
|
-
projectName: session.project?.displayName ?? (session.source === "standalone"
|
|
132
|
-
? (session.tool !== "unknown" ? session.tool : "standalone")
|
|
133
|
-
: session.source === "herdr"
|
|
134
|
-
? session.herdr?.workspace ?? "herdr"
|
|
135
|
-
: session.tmux?.session ?? key),
|
|
136
|
-
sessions: [session],
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const recency = (group: { sessions: SessionListItem[] }) => Math.max(...group.sessions.map((s) => s.lastActivity));
|
|
142
|
-
const sortedGroups = [...groups.entries()].sort(
|
|
143
|
-
([, left], [, right]) => groupSortWeight(left) - groupSortWeight(right) || recency(right) - recency(left),
|
|
144
|
-
);
|
|
145
|
-
const rows: DashboardRow[] = sortedGroups.length > 0 ? [{ kind: "gap", projectKey: "__initial-gap__" }] : [];
|
|
146
|
-
sortedGroups.forEach(([projectKey, group], groupIndex) => {
|
|
147
|
-
if (groupIndex > 0) rows.push({ kind: "gap", projectKey });
|
|
148
|
-
group.sessions.sort((left, right) => sessionSortWeight(left) - sessionSortWeight(right) || right.lastActivity - left.lastActivity);
|
|
149
|
-
rows.push({ kind: "project-header", projectKey, projectName: group.projectName });
|
|
150
|
-
for (const session of group.sessions) {
|
|
151
|
-
rows.push({ kind: "session", projectKey, session });
|
|
152
|
-
if (!narrow) {
|
|
153
|
-
const children = (childrenByParent.get(session.id) ?? []).sort(
|
|
154
|
-
(left, right) => sessionSortWeight(left) - sessionSortWeight(right) || right.lastActivity - left.lastActivity,
|
|
155
|
-
);
|
|
156
|
-
for (const child of children) rows.push({ kind: "session", projectKey, session: child });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
return rows;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function getProjectGroupKey(session: SessionListItem): string {
|
|
164
|
-
return session.project?.repositoryIdentityPath
|
|
165
|
-
?? session.project?.gitRootPath
|
|
166
|
-
?? session.project?.sourcePath
|
|
167
|
-
?? session.project?.displayName
|
|
168
|
-
?? (session.source === "standalone"
|
|
169
|
-
? `standalone:${session.tool}`
|
|
170
|
-
: session.source === "herdr"
|
|
171
|
-
? `herdr:${session.herdr?.workspaceId ?? session.herdr?.workspace ?? session.herdr?.paneId ?? session.id}`
|
|
172
|
-
: session.tmux?.session ?? session.id);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function moveSelection(rows: DashboardRow[], selectedId: string | undefined, direction: 1 | -1): string | undefined {
|
|
176
|
-
if (rows.length === 0) return selectedId;
|
|
177
|
-
if (!selectedId) {
|
|
178
|
-
const candidates = direction > 0 ? rows : [...rows].reverse();
|
|
179
|
-
return candidates.find((row): row is SessionRow => row.kind === "session")?.session.id;
|
|
180
|
-
}
|
|
181
|
-
const current = rows.findIndex((row) => row.kind === "session" && row.session.id === selectedId);
|
|
182
|
-
if (current < 0) return undefined;
|
|
183
|
-
for (let index = current + direction; index >= 0 && index < rows.length; index += direction) {
|
|
184
|
-
const row = rows[index];
|
|
185
|
-
if (row?.kind === "session") return row.session.id;
|
|
186
|
-
}
|
|
187
|
-
return selectedId;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function visibleRowWindow(rows: DashboardRow[], selectedId: string | undefined, limit: number): DashboardRow[] {
|
|
191
|
-
if (rows.length <= limit) return rows;
|
|
192
|
-
const selected = rows.findIndex((row) => row.kind === "session" && row.session.id === selectedId);
|
|
193
|
-
const start = Math.max(0, Math.min(selected - Math.floor(limit / 2), rows.length - limit));
|
|
194
|
-
return rows.slice(start, start + limit);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export function stateGlyph(state: string, spinnerFrame = 0): string {
|
|
198
|
-
if (state === "working") return WORKING_FRAMES[spinnerFrame % WORKING_FRAMES.length] ?? WORKING_FRAMES[0]!;
|
|
199
|
-
if (state === "idle") return "○";
|
|
200
|
-
if (state === "waiting-input") return "?";
|
|
201
|
-
if (state === "shell") return ">";
|
|
202
|
-
if (state === "error") return "✗";
|
|
203
|
-
return "?";
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function formatBranchLabel(session: SessionListItem): string {
|
|
207
|
-
if (!session.project?.isGitRepository) return "";
|
|
208
|
-
return session.project.branch || "detached";
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function formatTargetLabel(session: SessionListItem, narrow = false): string {
|
|
212
|
-
let base: string;
|
|
213
|
-
if (session.parentSessionId) {
|
|
214
|
-
base = ` ↳ ${truncate(session.summary || session.id, narrow ? 22 : 26)}`;
|
|
215
|
-
} else if (session.tmux) {
|
|
216
|
-
const label = session.displayName?.trim()
|
|
217
|
-
|| session.tmux.session?.trim()
|
|
218
|
-
|| session.id;
|
|
219
|
-
base = ` ▸ ${label}`;
|
|
220
|
-
} else if (session.herdr) {
|
|
221
|
-
const label = session.displayName?.trim()
|
|
222
|
-
|| session.herdr.tab?.trim()
|
|
223
|
-
|| session.herdr.agent?.trim()
|
|
224
|
-
|| session.herdr.paneId;
|
|
225
|
-
base = ` ▹ ${label}`;
|
|
226
|
-
} else {
|
|
227
|
-
const placeholder = session.activeTool
|
|
228
|
-
? `[${session.activeTool}]`
|
|
229
|
-
: (session.id.includes(":") ? session.id.split(":")[1]!.slice(0, 8) : session.id.slice(0, 8));
|
|
230
|
-
base = ` ◇ ${placeholder}`;
|
|
231
|
-
}
|
|
232
|
-
const branch = formatBranchLabel(session);
|
|
233
|
-
return branch ? `${base}@${branch}` : base;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export type SessionToolMode = "text" | "monogram" | "icons";
|
|
237
|
-
|
|
238
|
-
export function sessionColumnWidths(width: number, toolMode: SessionToolMode = "text", toolContentWidth = 0): SessionColumnWidths {
|
|
239
|
-
const narrow = width < 110;
|
|
240
|
-
const available = Math.max(36, width - 9);
|
|
241
|
-
if (narrow) {
|
|
242
|
-
const enhanced = toolMode !== "text";
|
|
243
|
-
const state = 11;
|
|
244
|
-
// On very narrow widths the summary column is a few truncated characters;
|
|
245
|
-
// drop it entirely and hand its space to the session column. The budget
|
|
246
|
-
// matches the pane's real content width (borders + padding + gutter) so
|
|
247
|
-
// rows fill from border to border.
|
|
248
|
-
if (width < 100) {
|
|
249
|
-
const filledAvailable = Math.max(36, width - 6);
|
|
250
|
-
const tool = Math.max(5, (toolContentWidth || (toolMode === "text" ? 10 : 7)) + 1);
|
|
251
|
-
return { session: Math.max(14, filledAvailable - state - tool), state, tool, summary: 0 };
|
|
252
|
-
}
|
|
253
|
-
const tool = toolMode === "text"
|
|
254
|
-
? Math.min(12, Math.max(9, Math.floor(available * 0.18)))
|
|
255
|
-
: toolMode === "icons" ? 7 : 8;
|
|
256
|
-
const session = Math.min(enhanced ? 32 : 28, Math.max(14, Math.floor(available * (enhanced ? 0.4 : 0.34))));
|
|
257
|
-
return { session, state, tool, summary: Math.max(8, available - session - state - tool) };
|
|
258
|
-
}
|
|
259
|
-
const state = 11;
|
|
260
|
-
const tool = toolMode === "text" ? 13 : toolMode === "icons" ? 3 : 5;
|
|
261
|
-
if (toolMode !== "text") {
|
|
262
|
-
const remaining = available - state - tool;
|
|
263
|
-
const session = Math.min(72, Math.max(30, Math.floor(remaining * 0.55)));
|
|
264
|
-
return { session, state, tool, summary: Math.max(8, available - session - state - tool) };
|
|
265
|
-
}
|
|
266
|
-
const session = Math.min(40, Math.max(30, available - state - tool - 39));
|
|
267
|
-
return { session, state, tool, summary: Math.max(8, available - session - state - tool) };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export function formatStatusCell(session: SessionListItem, spinnerFrame: number): string {
|
|
271
|
-
const glyph = stateGlyph(session.state, spinnerFrame);
|
|
272
|
-
return `${glyph} (${formatDuration(session.stableForMs ?? 0)})`;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
export function activeChildCounts(sessions: SessionListItem[]): Map<string, number> {
|
|
276
|
-
const counts = new Map<string, number>();
|
|
277
|
-
for (const session of sessions) {
|
|
278
|
-
if (!session.parentSessionId || (session.state !== "working" && session.state !== "waiting-input")) continue;
|
|
279
|
-
counts.set(session.parentSessionId, (counts.get(session.parentSessionId) ?? 0) + 1);
|
|
280
|
-
}
|
|
281
|
-
return counts;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export function sessionAttachCommand(session: SessionListItem, insideTmux: boolean): string[] | null {
|
|
285
|
-
if (session.source === "tmux" && session.tmux) {
|
|
286
|
-
return insideTmux
|
|
287
|
-
? ["tmux", "switch-client", "-t", session.tmux.session]
|
|
288
|
-
: ["tmux", "attach-session", "-t", session.tmux.session];
|
|
289
|
-
}
|
|
290
|
-
if (session.source === "herdr" && session.herdr?.terminalId) {
|
|
291
|
-
return ["herdr", "terminal", "attach", session.herdr.terminalId];
|
|
292
|
-
}
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
|
-
|
package/src/tui/state.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { SessionModel } from "../shared/types";
|
|
2
|
-
import type { SessionListItem, SessionViewCounts } from "../shared/session-view";
|
|
3
|
-
import type { UsageState } from "../usage/types";
|
|
4
|
-
import { cycleFilters, type DashboardFilters, type DashboardMode, type DashboardView } from "./model";
|
|
5
|
-
|
|
6
|
-
export interface DashboardState {
|
|
7
|
-
view: DashboardView;
|
|
8
|
-
mode: DashboardMode;
|
|
9
|
-
sessions: SessionListItem[];
|
|
10
|
-
counts: SessionViewCounts | null;
|
|
11
|
-
usage: UsageState;
|
|
12
|
-
selectedSessionId?: string;
|
|
13
|
-
detail?: SessionModel;
|
|
14
|
-
message?: string;
|
|
15
|
-
daemonConnected: boolean;
|
|
16
|
-
filters: DashboardFilters;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const INITIAL_DASHBOARD_STATE: DashboardState = {
|
|
20
|
-
view: "sessions",
|
|
21
|
-
mode: "list",
|
|
22
|
-
sessions: [],
|
|
23
|
-
counts: null,
|
|
24
|
-
usage: { providers: [], lastRefreshedAt: 0 },
|
|
25
|
-
daemonConnected: true,
|
|
26
|
-
filters: { query: "", actionableOnly: false, tool: "all", state: "all" },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type DashboardAction =
|
|
30
|
-
| { type: "sessions-loaded"; sessions: SessionListItem[]; counts: SessionViewCounts }
|
|
31
|
-
| { type: "usage-loaded"; usage: UsageState }
|
|
32
|
-
| { type: "connection"; connected: boolean }
|
|
33
|
-
| { type: "select"; id?: string }
|
|
34
|
-
| { type: "view"; view: DashboardView }
|
|
35
|
-
| { type: "search" }
|
|
36
|
-
| { type: "query"; query: string }
|
|
37
|
-
| { type: "toggle-actionable" }
|
|
38
|
-
| { type: "cycle-filters" }
|
|
39
|
-
| { type: "detail"; session: SessionModel }
|
|
40
|
-
| { type: "message"; message: string }
|
|
41
|
-
| { type: "bindings" }
|
|
42
|
-
| { type: "close-overlay" };
|
|
43
|
-
|
|
44
|
-
export function dashboardReducer(state: DashboardState, action: DashboardAction): DashboardState {
|
|
45
|
-
switch (action.type) {
|
|
46
|
-
case "sessions-loaded": {
|
|
47
|
-
const selectedSessionId = action.sessions.some((session) => session.id === state.selectedSessionId)
|
|
48
|
-
? state.selectedSessionId
|
|
49
|
-
: undefined;
|
|
50
|
-
return { ...state, sessions: action.sessions, counts: action.counts, selectedSessionId, daemonConnected: true };
|
|
51
|
-
}
|
|
52
|
-
case "usage-loaded":
|
|
53
|
-
return { ...state, usage: action.usage };
|
|
54
|
-
case "connection":
|
|
55
|
-
return { ...state, daemonConnected: action.connected };
|
|
56
|
-
case "select":
|
|
57
|
-
return { ...state, selectedSessionId: action.id };
|
|
58
|
-
case "view":
|
|
59
|
-
return { ...state, view: action.view, mode: "list", detail: undefined, message: undefined };
|
|
60
|
-
case "search":
|
|
61
|
-
return { ...state, mode: "search" };
|
|
62
|
-
case "query":
|
|
63
|
-
return { ...state, filters: { ...state.filters, query: action.query } };
|
|
64
|
-
case "toggle-actionable":
|
|
65
|
-
return { ...state, filters: { ...state.filters, actionableOnly: !state.filters.actionableOnly } };
|
|
66
|
-
case "cycle-filters":
|
|
67
|
-
return { ...state, filters: cycleFilters(state.filters) };
|
|
68
|
-
case "detail":
|
|
69
|
-
return { ...state, mode: "detail", detail: action.session, message: undefined };
|
|
70
|
-
case "message":
|
|
71
|
-
return { ...state, mode: "message", message: action.message, detail: undefined };
|
|
72
|
-
case "bindings":
|
|
73
|
-
return { ...state, mode: "bindings", detail: undefined, message: undefined };
|
|
74
|
-
case "close-overlay":
|
|
75
|
-
return { ...state, mode: "list", detail: undefined, message: undefined };
|
|
76
|
-
}
|
|
77
|
-
}
|
package/src/tui/theme.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scrylog brand palette (source: brand guide, OpenTUI-compatible ANSI sheet).
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for every color used by the TUI: change a value here
|
|
5
|
-
* (or swap the whole object for an alternate theme) and the whole UI follows.
|
|
6
|
-
*/
|
|
7
|
-
export const BRAND = {
|
|
8
|
-
/** Main Teal/Cyan — primary brand color (xterm 80). */
|
|
9
|
-
primary: "#5CCCCC",
|
|
10
|
-
/** Accent Light Blue (xterm 159). */
|
|
11
|
-
accentBlue: "#A1E6FF",
|
|
12
|
-
/** Highlight Violet (xterm 177). */
|
|
13
|
-
violet: "#CFA6FF",
|
|
14
|
-
/** Detail Grey — secondary text (xterm 246). */
|
|
15
|
-
grey: "#A0A0A0",
|
|
16
|
-
/** Background Dark (xterm 234). */
|
|
17
|
-
bgDark: "#1A1A1A",
|
|
18
|
-
/** Modal / floating panel surface (near-black, kept from legacy scheme). */
|
|
19
|
-
surface: "#11151c",
|
|
20
|
-
/** Functional colors kept unchanged (no palette equivalent). */
|
|
21
|
-
danger: "#f05252",
|
|
22
|
-
warning: "#f5a524",
|
|
23
|
-
|
|
24
|
-
/** Legacy chrome visuals preserved by design: pane borders and separators. */
|
|
25
|
-
border: "#495365",
|
|
26
|
-
/** Selected session row background (legacy slate). */
|
|
27
|
-
selection: "#263244",
|
|
28
|
-
/** Fallback foreground for rows whose state has no mapping. */
|
|
29
|
-
fallback: "#d8dee9",
|
|
30
|
-
|
|
31
|
-
/** Session state colors (palette-family tints). */
|
|
32
|
-
stateWorking: "#E8BC5D",
|
|
33
|
-
stateIdle: "#6BD9A4",
|
|
34
|
-
/** Usage semaphore ≥90% (soft coral, danger family). */
|
|
35
|
-
usageHigh: "#E28686",
|
|
36
|
-
} as const;
|
|
37
|
-
|
|
38
|
-
export interface ProviderVisual {
|
|
39
|
-
name: string;
|
|
40
|
-
short: string;
|
|
41
|
-
brandColor: string;
|
|
42
|
-
borderColor: string;
|
|
43
|
-
/** Alternative brand color readable on dark terminal backgrounds. */
|
|
44
|
-
onDarkColor?: string;
|
|
45
|
-
tileColor?: string;
|
|
46
|
-
terminalAsset?: URL;
|
|
47
|
-
graphicsAsset?: URL;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Third-party provider brand colors: data, not brand palette. */
|
|
51
|
-
export const PROVIDER_BRANDS: Record<string, ProviderVisual> = {
|
|
52
|
-
claude: {
|
|
53
|
-
name: "Claude",
|
|
54
|
-
short: "CL",
|
|
55
|
-
brandColor: "#d97757",
|
|
56
|
-
borderColor: "#d97757",
|
|
57
|
-
terminalAsset: new URL("./assets/providers/claude-terminal.png", import.meta.url),
|
|
58
|
-
graphicsAsset: new URL("./assets/providers/claude.png", import.meta.url),
|
|
59
|
-
},
|
|
60
|
-
codex: {
|
|
61
|
-
name: "Codex",
|
|
62
|
-
short: "CX",
|
|
63
|
-
brandColor: "#539af8",
|
|
64
|
-
borderColor: "#539af8",
|
|
65
|
-
terminalAsset: new URL("./assets/providers/openai-terminal.png", import.meta.url),
|
|
66
|
-
graphicsAsset: new URL("./assets/providers/openai.png", import.meta.url),
|
|
67
|
-
},
|
|
68
|
-
"opencode-go": {
|
|
69
|
-
name: "OpenCode Go",
|
|
70
|
-
short: "OC",
|
|
71
|
-
brandColor: "#111111",
|
|
72
|
-
onDarkColor: "#c8cdd8",
|
|
73
|
-
borderColor: BRAND.grey,
|
|
74
|
-
tileColor: "#f3f3f3",
|
|
75
|
-
terminalAsset: new URL("./assets/providers/opencode-terminal.png", import.meta.url),
|
|
76
|
-
graphicsAsset: new URL("./assets/providers/opencode.png", import.meta.url),
|
|
77
|
-
},
|
|
78
|
-
copilot: {
|
|
79
|
-
name: "Copilot",
|
|
80
|
-
short: "CP",
|
|
81
|
-
brandColor: "#c8cdd8",
|
|
82
|
-
borderColor: BRAND.grey,
|
|
83
|
-
graphicsAsset: new URL("./assets/providers/copilot.png", import.meta.url),
|
|
84
|
-
},
|
|
85
|
-
shell: {
|
|
86
|
-
name: "Shell",
|
|
87
|
-
short: "SH",
|
|
88
|
-
brandColor: "#c8cdd8",
|
|
89
|
-
borderColor: BRAND.grey,
|
|
90
|
-
graphicsAsset: new URL("./assets/providers/shell.png", import.meta.url),
|
|
91
|
-
},
|
|
92
|
-
unknown: {
|
|
93
|
-
name: "Unknown",
|
|
94
|
-
short: "?",
|
|
95
|
-
brandColor: BRAND.grey,
|
|
96
|
-
borderColor: BRAND.grey,
|
|
97
|
-
graphicsAsset: new URL("./assets/providers/unknown.png", import.meta.url),
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const BRAND_MARK = {
|
|
102
|
-
/** Blocks: front-view crystal ball + prompt + circuit pedestal, brand palette. */
|
|
103
|
-
art: [
|
|
104
|
-
{ fg: BRAND.accentBlue, content: " ▗▄▄▄▄▄▄▖" },
|
|
105
|
-
{ fg: BRAND.primary, content: " ▐▓▒░░░░▒▓▌" },
|
|
106
|
-
{ fg: BRAND.primary, content: " ░ ◉◈◉ ›_ ░" },
|
|
107
|
-
{ fg: BRAND.violet, content: " ▜▓▒░░░░▒▓▛" },
|
|
108
|
-
{ fg: BRAND.violet, content: " ▐█╿██╵█▌" },
|
|
109
|
-
],
|
|
110
|
-
} as const;
|
package/src/types.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
export type ToolKind = "opencode" | "claude" | "codex" | "copilotcli" | "shell" | "unknown";
|
|
2
|
-
|
|
3
|
-
export type PaneState = "working" | "idle" | "waiting-input" | "shell" | "unknown";
|
|
4
|
-
|
|
5
|
-
export interface SessionInfo {
|
|
6
|
-
name: string;
|
|
7
|
-
attached: boolean;
|
|
8
|
-
windows: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface TmuxPaneRecord {
|
|
12
|
-
sessionName: string;
|
|
13
|
-
windowIndex: number;
|
|
14
|
-
windowName: string;
|
|
15
|
-
paneIndex: number;
|
|
16
|
-
paneId: string;
|
|
17
|
-
panePid: number;
|
|
18
|
-
currentCommand: string;
|
|
19
|
-
currentPath: string;
|
|
20
|
-
active: boolean;
|
|
21
|
-
tty: string;
|
|
22
|
-
title: string;
|
|
23
|
-
width: number;
|
|
24
|
-
height: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface ProjectInfo {
|
|
28
|
-
displayName: string;
|
|
29
|
-
compactLabel: string;
|
|
30
|
-
sourcePath: string;
|
|
31
|
-
isGitRepository: boolean;
|
|
32
|
-
gitRootPath?: string;
|
|
33
|
-
repositoryIdentityPath?: string;
|
|
34
|
-
branch?: string;
|
|
35
|
-
isWorktree?: boolean;
|
|
36
|
-
remoteName?: string;
|
|
37
|
-
remoteUrl?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface EnrichedPaneRecord extends TmuxPaneRecord {
|
|
41
|
-
project: ProjectInfo;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface ProcessInfo {
|
|
45
|
-
pid: number;
|
|
46
|
-
ppid: number;
|
|
47
|
-
state: string;
|
|
48
|
-
elapsed: string;
|
|
49
|
-
command: string;
|
|
50
|
-
comm: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface DetectionReason {
|
|
54
|
-
label: string;
|
|
55
|
-
detail: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface PaneSample {
|
|
59
|
-
paneId: string;
|
|
60
|
-
lines: string[];
|
|
61
|
-
preview: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface PaneModel extends EnrichedPaneRecord {
|
|
65
|
-
processTree: ProcessInfo[];
|
|
66
|
-
processChain: string[];
|
|
67
|
-
sample: PaneSample | null;
|
|
68
|
-
tool: ToolKind;
|
|
69
|
-
state: PaneState;
|
|
70
|
-
toolReasons: DetectionReason[];
|
|
71
|
-
stateReasons: DetectionReason[];
|
|
72
|
-
summary: string;
|
|
73
|
-
lastChangedAt: number;
|
|
74
|
-
stableForMs: number;
|
|
75
|
-
hasAttention: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface SnapshotError {
|
|
79
|
-
source: string;
|
|
80
|
-
message: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface DashboardSnapshot {
|
|
84
|
-
sessions: SessionInfo[];
|
|
85
|
-
panes: PaneModel[];
|
|
86
|
-
generatedAt: number;
|
|
87
|
-
errors: SnapshotError[];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface FilterOptions {
|
|
91
|
-
query: string;
|
|
92
|
-
actionableOnly: boolean;
|
|
93
|
-
tool: ToolKind | "all";
|
|
94
|
-
state: PaneState | "all";
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface SnapshotHistory {
|
|
98
|
-
tool: ToolKind;
|
|
99
|
-
state: PaneState;
|
|
100
|
-
lastFingerprint: string;
|
|
101
|
-
lastChangedAt: number;
|
|
102
|
-
stableCount: number;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface CollectorOptions {
|
|
106
|
-
previewLines: number;
|
|
107
|
-
detailLines: number;
|
|
108
|
-
refreshMs: number;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface AppOptions extends CollectorOptions {
|
|
112
|
-
selectedPaneId?: string;
|
|
113
|
-
}
|