mirror of
https://github.com/blamouche/obsidian-any-ai-code.git
synced 2026-07-22 06:53:38 +00:00
The session tab status dot now reflects live CLI activity rather than just whether the process is alive: green = a manual session's AI is working, purple = an automation session's AI is working, gray = the CLI is idle (finished its turn) or stopped. CliSession tracks activity continuously — output flowing = working, quiet for ~5s = idle — and notifies the view via onActivityChange to repaint the dot. Add a new opt-in setting "Auto-close automation sessions when idle": closes an automation tab once its CLI goes quiet after the prompt ran, even if the process stays interactive (Claude/Codex). It complements the existing on-exit close (renamed "...on exit"); both are kept. Idle-close is armed only after the prompt is sent and driven by real CLI output, so boot and slow first responses don't close the tab early. Extract the pure tabDotClass helper to session-utils.ts with tests. Update README. Bump to 0.2.11. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import type { CliRuntimeConfig } from "./runtime-utils";
|
|
|
|
/**
|
|
* True when `target` (an automation's declared runtime) refers to `runtime`,
|
|
* matched either by id or by display name (case-insensitive). Mirrors the rule
|
|
* the sidebar view used before sessions existed.
|
|
*/
|
|
export function runtimeMatches(runtime: CliRuntimeConfig, target: string): boolean {
|
|
const wanted = target.trim().toLowerCase();
|
|
if (!wanted) {
|
|
return false;
|
|
}
|
|
if (runtime.id.trim().toLowerCase() === wanted) {
|
|
return true;
|
|
}
|
|
return runtime.name.trim().toLowerCase() === wanted;
|
|
}
|
|
|
|
/**
|
|
* Resolve which runtime an automation should spawn.
|
|
* - If `declared` is set, find the runtime matching it (id or name); returns
|
|
* `null` when none matches (the automation is then skipped).
|
|
* - If `declared` is empty/null, fall back to the default runtime
|
|
* (`defaultId`), then to the first configured runtime.
|
|
*/
|
|
export function resolveRuntimeForAutomation(
|
|
runtimes: CliRuntimeConfig[],
|
|
declared: string | null | undefined,
|
|
defaultId: string
|
|
): CliRuntimeConfig | null {
|
|
const wanted = (declared ?? "").trim();
|
|
if (wanted) {
|
|
return runtimes.find((runtime) => runtimeMatches(runtime, wanted)) ?? null;
|
|
}
|
|
return runtimes.find((runtime) => runtime.id === defaultId) ?? runtimes[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Build a tab label for a new session, disambiguating duplicates of the same
|
|
* runtime: "Claude", "Claude (2)", "Claude (3)", ...
|
|
*/
|
|
export function nextSessionLabel(existingLabels: string[], runtimeName: string): string {
|
|
const base = runtimeName.trim() || "(Unnamed)";
|
|
const taken = new Set(existingLabels);
|
|
if (!taken.has(base)) {
|
|
return base;
|
|
}
|
|
let counter = 2;
|
|
while (taken.has(`${base} (${counter})`)) {
|
|
counter += 1;
|
|
}
|
|
return `${base} (${counter})`;
|
|
}
|
|
|
|
/**
|
|
* Whether another session may be opened given the current count and the
|
|
* configured cap. `max <= 0` means unlimited.
|
|
*/
|
|
export function canOpenSession(currentCount: number, max: number): boolean {
|
|
if (!Number.isFinite(max) || max <= 0) {
|
|
return true;
|
|
}
|
|
return currentCount < max;
|
|
}
|
|
|
|
/**
|
|
* CSS modifier class for a session tab's status dot, from its activity:
|
|
* - `is-working` — a manual session whose CLI is currently producing output (green)
|
|
* - `is-automation` — an automation session whose CLI is currently working (purple)
|
|
* - `is-idle` — the CLI is quiet/finished, or the process has stopped (gray)
|
|
*/
|
|
export function tabDotClass(opts: {
|
|
running: boolean;
|
|
activity: "working" | "idle";
|
|
origin: "manual" | "automation";
|
|
}): "is-working" | "is-automation" | "is-idle" {
|
|
if (opts.running && opts.activity === "working") {
|
|
return opts.origin === "automation" ? "is-automation" : "is-working";
|
|
}
|
|
return "is-idle";
|
|
}
|