fix: land sessions label edits (#1294) (thanks @bradleypriest)
This commit is contained in:
parent
26a5d02e69
commit
99546ca3d9
@ -18,6 +18,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Auth: dedupe codex-cli profiles when tokens match custom openai-codex entries. (#1264) — thanks @odrobnik.
|
- Auth: dedupe codex-cli profiles when tokens match custom openai-codex entries. (#1264) — thanks @odrobnik.
|
||||||
- Agents: avoid misclassifying context-window-too-small errors as context overflow. (#1266) — thanks @humanwritten.
|
- Agents: avoid misclassifying context-window-too-small errors as context overflow. (#1266) — thanks @humanwritten.
|
||||||
- Slack: resolve Bolt default-export shapes for monitor startup. (#1208) — thanks @24601.
|
- Slack: resolve Bolt default-export shapes for monitor startup. (#1208) — thanks @24601.
|
||||||
|
- UI: allow editing session labels in the Sessions table. (#1294) — thanks @bradleypriest.
|
||||||
|
|
||||||
## 2026.1.19-3
|
## 2026.1.19-3
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
signDevicePayload,
|
signDevicePayload,
|
||||||
} from "../infra/device-identity.js";
|
} from "../infra/device-identity.js";
|
||||||
import { emitHeartbeatEvent } from "../infra/heartbeat-events.js";
|
import { emitHeartbeatEvent } from "../infra/heartbeat-events.js";
|
||||||
import { loadOrCreateDeviceIdentity } from "../infra/device-identity.js";
|
|
||||||
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
||||||
import {
|
import {
|
||||||
connectOk,
|
connectOk,
|
||||||
|
|||||||
@ -111,7 +111,7 @@ export function enqueueCommand<T>(
|
|||||||
return enqueueCommandInLane(CommandLane.Main, task, opts);
|
return enqueueCommandInLane(CommandLane.Main, task, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getQueueSize(lane = CommandLane.Main) {
|
export function getQueueSize(lane: string = CommandLane.Main) {
|
||||||
const state = lanes.get(lane);
|
const state = lanes.get(lane);
|
||||||
if (!state) return 0;
|
if (!state) return 0;
|
||||||
return state.queue.length + state.active;
|
return state.queue.length + state.active;
|
||||||
@ -125,7 +125,7 @@ export function getTotalQueueSize() {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearCommandLane(lane = CommandLane.Main) {
|
export function clearCommandLane(lane: string = CommandLane.Main) {
|
||||||
const cleaned = lane.trim() || CommandLane.Main;
|
const cleaned = lane.trim() || CommandLane.Main;
|
||||||
const state = lanes.get(cleaned);
|
const state = lanes.get(cleaned);
|
||||||
if (!state) return 0;
|
if (!state) return 0;
|
||||||
|
|||||||
93
ui/src/ui/views/sessions.test.ts
Normal file
93
ui/src/ui/views/sessions.test.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { render } from "lit";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import type { GatewaySessionRow, SessionsListResult } from "../types";
|
||||||
|
import { renderSessions, type SessionsProps } from "./sessions";
|
||||||
|
|
||||||
|
function createRow(overrides: Partial<GatewaySessionRow> = {}): GatewaySessionRow {
|
||||||
|
return {
|
||||||
|
key: "session-1",
|
||||||
|
kind: "direct",
|
||||||
|
updatedAt: 0,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createResult(rows: GatewaySessionRow[]): SessionsListResult {
|
||||||
|
return {
|
||||||
|
ts: 0,
|
||||||
|
path: "/sessions",
|
||||||
|
count: rows.length,
|
||||||
|
defaults: {
|
||||||
|
model: null,
|
||||||
|
contextTokens: null,
|
||||||
|
},
|
||||||
|
sessions: rows,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProps(overrides: Partial<SessionsProps> = {}): SessionsProps {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
result: createResult([]),
|
||||||
|
error: null,
|
||||||
|
activeMinutes: "",
|
||||||
|
limit: "",
|
||||||
|
includeGlobal: true,
|
||||||
|
includeUnknown: true,
|
||||||
|
basePath: "/",
|
||||||
|
onFiltersChange: () => undefined,
|
||||||
|
onRefresh: () => undefined,
|
||||||
|
onPatch: () => undefined,
|
||||||
|
onDelete: () => undefined,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("sessions view", () => {
|
||||||
|
it("skips patching when the label is unchanged", () => {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
const onPatch = vi.fn();
|
||||||
|
const row = createRow({ label: "Alpha" });
|
||||||
|
|
||||||
|
render(
|
||||||
|
renderSessions(
|
||||||
|
createProps({
|
||||||
|
result: createResult([row]),
|
||||||
|
onPatch,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
container,
|
||||||
|
);
|
||||||
|
|
||||||
|
const input = container.querySelector("input") as HTMLInputElement | null;
|
||||||
|
expect(input).not.toBeNull();
|
||||||
|
input?.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
|
||||||
|
expect(onPatch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears labels when the input is empty", () => {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
const onPatch = vi.fn();
|
||||||
|
const row = createRow({ label: "Alpha" });
|
||||||
|
|
||||||
|
render(
|
||||||
|
renderSessions(
|
||||||
|
createProps({
|
||||||
|
result: createResult([row]),
|
||||||
|
onPatch,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
container,
|
||||||
|
);
|
||||||
|
|
||||||
|
const input = container.querySelector("input") as HTMLInputElement | null;
|
||||||
|
expect(input).not.toBeNull();
|
||||||
|
if (!input) return;
|
||||||
|
input.value = " ";
|
||||||
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
|
||||||
|
expect(onPatch).toHaveBeenCalledWith("session-1", { label: null });
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -203,6 +203,8 @@ function renderRow(
|
|||||||
placeholder="(optional)"
|
placeholder="(optional)"
|
||||||
@change=${(e: Event) => {
|
@change=${(e: Event) => {
|
||||||
const value = (e.target as HTMLInputElement).value.trim();
|
const value = (e.target as HTMLInputElement).value.trim();
|
||||||
|
const current = (row.label ?? "").trim();
|
||||||
|
if (value === current) return;
|
||||||
onPatch(row.key, { label: value || null });
|
onPatch(row.key, { label: value || null });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user