refactor(desktop): harden workspace shell foundation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Senior Frontend Engineer
2026-03-31 04:03:38 +00:00
parent 7c1abf2fd1
commit 291f744e0f
10 changed files with 805 additions and 82 deletions

View File

@@ -37,6 +37,13 @@ import {
} from "./components/ui/dialog";
import { Input } from "./components/ui/input";
import { Textarea } from "./components/ui/textarea";
import {
isLocalSmokeFixtureConnection,
localSmokeFixtureCommandPresets,
localSmokeFixtureExpectedKeys,
localSmokeFixtureProfileId,
localSmokeFixtureSearchText,
} from "./lib/local-smoke-fixture";
import {
toBackendError,
type BackendErrorContext,
@@ -45,10 +52,24 @@ import {
toBackendErrorNotice,
type OperatorNotice,
} from "./lib/operator-notices";
import { buildQaSnapshot } from "./lib/qa-snapshot";
import {
buildStructuredInspectorSections,
type StructuredInspectorSection,
} from "./lib/structured-inspector";
import {
appendWorkspaceKeyRecords,
filterWorkspaceKeys,
replaceWorkspaceKeyRecord,
resolveSelectedWorkspaceKey,
resolveSelectedWorkspaceKeyId,
} from "./lib/key-workspace-state";
import {
parseDemoTtlToSeconds,
persistDemoKeyTtl,
updateDemoKeyTtl,
updateDemoStringValue,
} from "./lib/demo-workspace";
import { cn } from "./lib/utils";
declare const __APP_VERSION__: string;
@@ -293,7 +314,7 @@ const initialConnections: ConnectionProfile[] = [
},
];
const keyData: KeyRecord[] = [
const initialDemoKeys: KeyRecord[] = [
{
id: "session:42",
name: "session:42",
@@ -373,11 +394,12 @@ const initialConsoleHistory: ConsoleEntry[] = [
function App() {
const [connections, setConnections] = useState(initialConnections);
const [demoKeys, setDemoKeys] = useState(initialDemoKeys);
const [activeConnectionId, setActiveConnectionId] = useState(initialConnections[0].id);
const [activeDb, setActiveDb] = useState(databases[0]);
const [search, setSearch] = useState("");
const [selectedKeyId, setSelectedKeyId] = useState<string | null>(keyData[1].id);
const [draftValue, setDraftValue] = useState(keyData[1].value);
const [selectedKeyId, setSelectedKeyId] = useState<string | null>(initialDemoKeys[1].id);
const [draftValue, setDraftValue] = useState(initialDemoKeys[1].value);
const [consoleInput, setConsoleInput] = useState("GET cache:homepage");
const [consoleHistory, setConsoleHistory] = useState(initialConsoleHistory);
const [bootstrap, setBootstrap] = useState(fallbackBootstrap);
@@ -422,18 +444,12 @@ function App() {
connections.find((connection) => connection.id === activeConnectionId) ??
connections[0];
const demoQuery = deferredSearch.trim().toLowerCase();
const demoKeys = !demoQuery
? keyData
: keyData.filter((item) => item.name.toLowerCase().includes(demoQuery));
const filteredKeys = tauriAvailable ? liveKeys : demoKeys;
const filteredDemoKeys = filterWorkspaceKeys(demoKeys, deferredSearch);
const filteredKeys = tauriAvailable ? liveKeys : filteredDemoKeys;
const draftErrors = validateConnectionDraft(connectionDraft, connections);
const draftErrorList = Object.values(draftErrors);
const selectedKey =
filteredKeys.length === 0
? null
: filteredKeys.find((item) => item.id === selectedKeyId) ?? filteredKeys[0];
const selectedKey = resolveSelectedWorkspaceKey(filteredKeys, selectedKeyId);
const isEditable = tauriAvailable
? selectedValueRecord?.write_capability === "replace_string" &&
@@ -459,6 +475,8 @@ function App() {
const ttlWriteSupported = tauriAvailable
? selectedValueRecord?.ttl_write_supported === true
: selectedKey !== null;
const localSmokeFixtureActive = isLocalSmokeFixtureConnection(activeConnection);
const localSmokeFilterActive = search.trim() === localSmokeFixtureSearchText;
const ttlDraftError = ttlDraft.trim() ? validateTtlDraft(ttlDraft) : null;
const canSubmitTtlUpdate =
selectedKey !== null &&
@@ -474,23 +492,25 @@ function App() {
? selectedValueRecord?.metadata.ttl.kind !== "persistent"
: selectedKey?.ttl !== "persistent");
const latestConsoleEntry = consoleHistory[0] ?? null;
const qaSnapshot = [
`app=${bootstrap.app_name}`,
`version=${appVersion}`,
`runtime=${runtimeMode}`,
`build=${buildMode}`,
`connection=${activeConnection.name}`,
`endpoint=${formatEndpoint(activeConnection.target)}`,
`database=${activeDb}`,
`selectedKey=${selectedKey?.name ?? "none"}`,
`selectedType=${selectedKey?.type ?? "none"}`,
`selectedTtl=${selectedKey?.ttl ?? "none"}`,
`browseVisible=${filteredKeys.length}${tauriAvailable && liveHasMore ? "+" : ""}`,
`latestCommand=${latestConsoleEntry?.command ?? "none"}`,
`latestCommandSource=${latestConsoleEntry?.source ?? "none"}`,
`latestCommandStatus=${latestConsoleEntry?.status ?? "none"}`,
`banner=${banner.message}`,
].join("\n");
const qaSnapshot = buildQaSnapshot({
appName: bootstrap.app_name,
appVersion,
runtimeMode,
buildMode,
connectionName: activeConnection.name,
endpoint: formatEndpoint(activeConnection.target),
databaseLabel: activeDb,
smokeFixtureActive: localSmokeFixtureActive,
smokeFilter: localSmokeFilterActive ? localSmokeFixtureSearchText : search,
selectedKeyName: selectedKey?.name ?? null,
selectedKeyType: selectedKey?.type ?? null,
selectedKeyTtl: selectedKey?.ttl ?? null,
browseVisibleCount: `${filteredKeys.length}${tauriAvailable && liveHasMore ? "+" : ""}`,
latestCommand: latestConsoleEntry?.command ?? null,
latestCommandSource: latestConsoleEntry?.source ?? null,
latestCommandStatus: latestConsoleEntry?.status ?? null,
bannerMessage: banner.message,
});
useEffect(() => {
if (!tauriAvailable) {
@@ -569,9 +589,7 @@ function App() {
setLiveKeys(nextKeys);
setLiveNextCursor(result.next_cursor);
setLiveHasMore(result.has_more);
setSelectedKeyId((current) =>
nextKeys.some((item) => item.id === current) ? current : (nextKeys[0]?.id ?? null),
);
setSelectedKeyId((current) => resolveSelectedWorkspaceKeyId(nextKeys, current));
setIsBrowseLoading(false);
});
})
@@ -644,7 +662,7 @@ function App() {
const nextRecord = toLiveKeyRecordFromValue(record);
startTransition(() => {
setSelectedValueRecord(record);
setLiveKeys((current) => replaceKeyRecord(current, nextRecord));
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
record.data.kind === "string" && record.write_capability === "replace_string"
? formatRedisValueContent(record.data.value)
@@ -723,6 +741,60 @@ function App() {
});
};
const handleUseLocalSmokeFixture = () => {
const existingFixture = connections.find(isLocalSmokeFixtureConnection);
const fixtureConnection =
existingFixture ??
{
id: localSmokeFixtureProfileId,
name: localSmokeFixtureProfileId,
environment: "local" as const,
detail: "Fixture profile",
status: "idle" as const,
password: "",
target: {
host: "127.0.0.1",
port: 6380,
database: 0,
username: null,
tls_mode: "disabled" as const,
},
};
startTransition(() => {
if (!existingFixture) {
setConnections((current) => [fixtureConnection, ...current]);
}
setActiveConnectionId(fixtureConnection.id);
setActiveDb(formatDatabaseLabel(fixtureConnection.target.database));
setSelectedKeyId(null);
setSearch(localSmokeFixtureSearchText);
setConsoleInput(localSmokeFixtureCommandPresets[0].command);
showAccentBanner(
"Loaded the local smoke fixture profile, narrowed the browser to smoke:* keys, and primed the command tray with PING.",
);
});
};
const handleFocusLocalSmokeKeys = () => {
startTransition(() => {
setSearch(localSmokeFixtureSearchText);
setSelectedKeyId(null);
showNeutralBanner(
`Focused the browser on ${localSmokeFixtureSearchText} for local smoke verification in ${activeDb}.`,
);
});
};
const handleApplyLocalSmokeCommandPreset = (command: string) => {
startTransition(() => {
setConsoleInput(command);
showNeutralBanner(
`Loaded ${command} into the command tray so QA can execute the next safe smoke step without retyping.`,
);
});
};
const handleCreateDraftConnection = () => {
if (draftErrorList.length > 0) {
showNeutralBanner(`Connection draft is incomplete: ${draftErrorList[0]}`);
@@ -766,9 +838,12 @@ function App() {
}
if (!tauriAvailable) {
showNeutralBanner(
`Saved mock string value for ${selectedKey.name} in ${activeDb}. Backend persistence is not wired yet.`,
);
startTransition(() => {
setDemoKeys((current) => updateDemoStringValue(current, selectedKey.name, draftValue));
showNeutralBanner(
`Saved session-local mock string value for ${selectedKey.name} in ${activeDb}. Backend persistence is still unavailable in browser demo mode.`,
);
});
return;
}
@@ -791,7 +866,7 @@ function App() {
const nextRecord = toLiveKeyRecordFromValue(result);
startTransition(() => {
setSelectedValueRecord(result);
setLiveKeys((current) => replaceKeyRecord(current, nextRecord));
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
result.data.kind === "string"
? formatRedisValueContent(result.data.value)
@@ -839,9 +914,14 @@ function App() {
}
if (!tauriAvailable) {
showNeutralBanner(
`Browser dev mode keeps TTL changes in demo fallback for ${selectedKey.name}. Use desktop runtime for a live TTL update.`,
);
startTransition(() => {
setDemoKeys((current) =>
updateDemoKeyTtl(current, selectedKey.name, Number.parseInt(ttlDraft, 10)),
);
showNeutralBanner(
`Updated session-local mock TTL for ${selectedKey.name} in ${activeDb}. Use desktop runtime for a live TTL change.`,
);
});
return;
}
@@ -893,9 +973,13 @@ function App() {
}
if (!tauriAvailable) {
showNeutralBanner(
`Browser dev mode keeps TTL removal in demo fallback for ${selectedKey.name}. Use desktop runtime for a live TTL change.`,
);
startTransition(() => {
setDemoKeys((current) => persistDemoKeyTtl(current, selectedKey.name));
setTtlDraft("");
showNeutralBanner(
`Removed session-local mock TTL for ${selectedKey.name} in ${activeDb}. Use desktop runtime for a live TTL change.`,
);
});
return;
}
@@ -967,7 +1051,7 @@ function App() {
const nextRecord = toLiveKeyRecordFromValue(result);
startTransition(() => {
setSelectedValueRecord(result);
setLiveKeys((current) => replaceKeyRecord(current, nextRecord));
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
result.data.kind === "string" && result.write_capability === "replace_string"
? formatRedisValueContent(result.data.value)
@@ -1020,11 +1104,15 @@ function App() {
metadata,
};
setSelectedValueRecord(nextRecord);
setLiveKeys((current) => replaceKeyRecord(current, toLiveKeyRecordFromValue(nextRecord)));
setLiveKeys((current) =>
replaceWorkspaceKeyRecord(current, toLiveKeyRecordFromValue(nextRecord)),
);
return;
}
setLiveKeys((current) => replaceKeyRecord(current, toLiveKeyRecordFromMetadata(metadata)));
setLiveKeys((current) =>
replaceWorkspaceKeyRecord(current, toLiveKeyRecordFromMetadata(metadata)),
);
};
const handleLoadMoreKeys = async () => {
@@ -1048,7 +1136,7 @@ function App() {
const nextKeys = result.keys.map(toLiveKeyRecordFromMetadata);
startTransition(() => {
setLiveKeys((current) => appendKeyRecords(current, nextKeys));
setLiveKeys((current) => appendWorkspaceKeyRecords(current, nextKeys));
setLiveNextCursor(result.next_cursor);
setLiveHasMore(result.has_more);
setIsBrowseLoading(false);
@@ -1532,6 +1620,67 @@ function App() {
</div>
</section>
<section>
<SectionEyebrow icon={<Activity className="h-4 w-4" />} label="Local smoke assist" />
<div className="mt-3 rounded-[22px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4">
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--ink-0)]">
Seeded fixture path for desktop smoke runs
</p>
<p className="mt-1 text-sm text-[var(--ink-soft)]">
Mirrors `docs/qa/local-desktop-smoke.md` and keeps the safe validation path visible inside the shell.
</p>
</div>
<Badge variant={localSmokeFixtureActive ? "accent" : "neutral"}>
{localSmokeFixtureActive ? "active" : "inactive"}
</Badge>
</div>
<div className="mt-3 flex flex-wrap gap-2">
<Badge variant="neutral">127.0.0.1:6380</Badge>
<Badge variant="neutral">db0</Badge>
<Badge variant="neutral">{localSmokeFixtureExpectedKeys.length} seeded keys</Badge>
</div>
<div className="mt-3 grid grid-cols-2 gap-2">
<Button variant="outline" size="sm" onClick={handleUseLocalSmokeFixture}>
Use fixture
</Button>
<Button variant="outline" size="sm" onClick={handleFocusLocalSmokeKeys}>
Focus smoke:*
</Button>
</div>
<div className="mt-3 space-y-2">
{localSmokeFixtureCommandPresets.map((preset) => (
<button
key={preset.id}
type="button"
onClick={() => handleApplyLocalSmokeCommandPreset(preset.command)}
className="w-full rounded-[18px] border border-[var(--line-soft)] bg-[var(--surface-muted)] px-3 py-2 text-left transition hover:border-[var(--line-strong)]"
>
<p className="font-mono text-sm text-[var(--ink-0)]">{preset.label}</p>
<p className="mt-1 text-xs text-[var(--ink-soft)]">{preset.summary}</p>
</button>
))}
</div>
<div className="mt-3 rounded-[18px] border border-[var(--line-soft)] bg-[var(--surface-muted)] px-3 py-3">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
Seeded keys
</p>
<div className="mt-2 flex flex-wrap gap-2">
{localSmokeFixtureExpectedKeys.map((keyName) => (
<Badge key={keyName} variant="neutral" className="normal-case tracking-normal">
{keyName}
</Badge>
))}
</div>
</div>
</div>
</section>
<section>
<SectionEyebrow icon={<ClipboardList className="h-4 w-4" />} label="QA snapshot" />
<div className="mt-3 rounded-[22px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4">
@@ -1571,7 +1720,7 @@ function App() {
<Badge variant="neutral">
{tauriAvailable
? `${filteredKeys.length}${liveHasMore ? "+" : ""} visible in ${activeDb}`
: `${filteredKeys.length} of ${keyData.length} keys in ${activeDb}`}
: `${filteredKeys.length} of ${demoKeys.length} keys in ${activeDb}`}
</Badge>
</div>
@@ -2147,7 +2296,7 @@ function buildMockCommandOutput(command: string, selectedKey: KeyRecord | null)
}
if (upper === "TTL") {
return `(integer) ${selectedKey.ttl === "persistent" ? -1 : selectedKey.ttl.replace(/[^0-9]/g, "")}`;
return `(integer) ${parseDemoTtlToSeconds(selectedKey.ttl)}`;
}
if (upper === "TYPE") {
@@ -2290,39 +2439,6 @@ function toLiveKeyRecordFromValue(record: RedisValueRecord): KeyRecord {
};
}
function replaceKeyRecord(current: KeyRecord[], next: KeyRecord) {
let replaced = false;
const updated = current.map((item) => {
if (item.id !== next.id) {
return item;
}
replaced = true;
return {
...item,
...next,
};
});
return replaced ? updated : [next, ...current];
}
function appendKeyRecords(current: KeyRecord[], next: KeyRecord[]) {
const seen = new Set(current.map((item) => item.id));
const appended = [...current];
for (const item of next) {
if (seen.has(item.id)) {
continue;
}
seen.add(item.id);
appended.push(item);
}
return appended;
}
function buildMetadataPreview(metadata: RedisKeyMetadata) {
return `${formatRedisKeyTypeLabel(metadata.key_type)} key · TTL ${formatRedisKeyTtl(metadata.ttl)}`;
}

View File

@@ -0,0 +1,55 @@
import { describe, expect, it } from "vitest";
import {
buildDemoStringPreview,
formatDemoDurationFromMillis,
parseDemoTtlToSeconds,
persistDemoKeyTtl,
updateDemoKeyTtl,
updateDemoStringValue,
type DemoWorkspaceKey,
} from "./demo-workspace";
const demoKeys: DemoWorkspaceKey[] = [
{
id: "cache:homepage",
name: "cache:homepage",
type: "string",
ttl: "48s",
preview: "serialized landing payload",
editable: true,
value: "{\"hero\":\"Redis GUI\"}",
summary: "String keys are editable in V1.",
},
];
describe("demo workspace helpers", () => {
it("updates string values and refreshes the visible preview", () => {
expect(
updateDemoStringValue(demoKeys, "cache:homepage", "next value"),
).toEqual([
{
...demoKeys[0],
value: "next value",
preview: "next value",
},
]);
});
it("updates TTL state for session-local demo flows", () => {
expect(updateDemoKeyTtl(demoKeys, "cache:homepage", 90_000)[0]?.ttl).toBe("1m");
expect(persistDemoKeyTtl(demoKeys, "cache:homepage")[0]?.ttl).toBe("persistent");
});
it("formats preview text and durations consistently", () => {
expect(buildDemoStringPreview("alpha beta gamma delta epsilon zeta eta theta", 16)).toBe(
"alpha beta gamm…",
);
expect(formatDemoDurationFromMillis(750)).toBe("750ms");
expect(formatDemoDurationFromMillis(5_000)).toBe("5s");
expect(formatDemoDurationFromMillis(7_200_000)).toBe("2h");
expect(parseDemoTtlToSeconds("750ms")).toBe(0);
expect(parseDemoTtlToSeconds("5s")).toBe(5);
expect(parseDemoTtlToSeconds("2h")).toBe(7200);
expect(parseDemoTtlToSeconds("persistent")).toBe(-1);
});
});

View File

@@ -0,0 +1,114 @@
export type DemoWorkspaceKey = {
id: string;
name: string;
type: string;
ttl: string;
preview: string;
editable: boolean;
value: string;
summary: string;
};
export function updateDemoStringValue(
keys: DemoWorkspaceKey[],
keyName: string,
value: string,
) {
return keys.map((key) =>
key.name === keyName
? {
...key,
value,
preview: buildDemoStringPreview(value),
}
: key,
);
}
export function updateDemoKeyTtl(
keys: DemoWorkspaceKey[],
keyName: string,
ttlMillis: number,
) {
return keys.map((key) =>
key.name === keyName
? {
...key,
ttl: formatDemoDurationFromMillis(ttlMillis),
}
: key,
);
}
export function persistDemoKeyTtl(keys: DemoWorkspaceKey[], keyName: string) {
return keys.map((key) =>
key.name === keyName
? {
...key,
ttl: "persistent",
}
: key,
);
}
export function buildDemoStringPreview(value: string, maxLength = 48) {
const collapsed = value.replace(/\s+/g, " ").trim();
if (collapsed.length <= maxLength) {
return collapsed;
}
return `${collapsed.slice(0, maxLength - 1)}`;
}
export function formatDemoDurationFromMillis(value: number) {
if (value < 1_000) {
return `${value}ms`;
}
const totalSeconds = Math.floor(value / 1_000);
if (totalSeconds < 60) {
return `${totalSeconds}s`;
}
const totalMinutes = Math.floor(totalSeconds / 60);
if (totalMinutes < 60) {
return `${totalMinutes}m`;
}
const totalHours = Math.floor(totalMinutes / 60);
if (totalHours < 24) {
return `${totalHours}h`;
}
const totalDays = Math.floor(totalHours / 24);
return `${totalDays}d`;
}
export function parseDemoTtlToSeconds(value: string) {
if (value === "persistent") {
return -1;
}
const match = value.trim().match(/^(\d+)(ms|s|m|h|d)$/);
if (!match) {
return 0;
}
const amount = Number.parseInt(match[1], 10);
const unit = match[2];
switch (unit) {
case "ms":
return Math.floor(amount / 1_000);
case "s":
return amount;
case "m":
return amount * 60;
case "h":
return amount * 60 * 60;
case "d":
return amount * 60 * 60 * 24;
default:
return 0;
}
}

View File

@@ -0,0 +1,105 @@
import { describe, expect, it } from "vitest";
import {
appendWorkspaceKeyRecords,
filterWorkspaceKeys,
replaceWorkspaceKeyRecord,
resolveSelectedWorkspaceKey,
resolveSelectedWorkspaceKeyId,
} from "./key-workspace-state";
const workspaceKeys = [
{
id: "session:42",
name: "session:42",
ttl: "13m",
preview: "hash value",
},
{
id: "cache:homepage",
name: "cache:homepage",
ttl: "48s",
preview: "string value",
},
];
describe("key workspace state helpers", () => {
it("filters keys by name using trimmed case-insensitive search", () => {
expect(filterWorkspaceKeys(workspaceKeys, " CACHE ")).toEqual([
workspaceKeys[1],
]);
expect(filterWorkspaceKeys(workspaceKeys, " ")).toEqual(workspaceKeys);
});
it("resolves the selected key and falls back to the first visible key", () => {
expect(resolveSelectedWorkspaceKey(workspaceKeys, "cache:homepage")).toEqual(
workspaceKeys[1],
);
expect(resolveSelectedWorkspaceKey(workspaceKeys, "missing")).toEqual(
workspaceKeys[0],
);
expect(resolveSelectedWorkspaceKey([], "missing")).toBeNull();
});
it("resolves the selected key id for list refresh flows", () => {
expect(resolveSelectedWorkspaceKeyId(workspaceKeys, "cache:homepage")).toBe(
"cache:homepage",
);
expect(resolveSelectedWorkspaceKeyId(workspaceKeys, "missing")).toBe(
"session:42",
);
expect(resolveSelectedWorkspaceKeyId([], "missing")).toBeNull();
});
it("replaces an existing key while preserving untouched fields", () => {
expect(
replaceWorkspaceKeyRecord(workspaceKeys, {
id: "cache:homepage",
name: "cache:homepage",
ttl: "persistent",
}),
).toEqual([
workspaceKeys[0],
{
...workspaceKeys[1],
ttl: "persistent",
},
]);
});
it("prepends unseen replacements and appends only new paged keys", () => {
expect(
replaceWorkspaceKeyRecord(workspaceKeys, {
id: "smoke:string",
name: "smoke:string",
ttl: "5m",
}),
).toEqual([
{
id: "smoke:string",
name: "smoke:string",
ttl: "5m",
},
...workspaceKeys,
]);
expect(
appendWorkspaceKeyRecords(workspaceKeys, [
workspaceKeys[1],
{
id: "smoke:string",
name: "smoke:string",
ttl: "5m",
preview: "fixture key",
},
]),
).toEqual([
...workspaceKeys,
{
id: "smoke:string",
name: "smoke:string",
ttl: "5m",
preview: "fixture key",
},
]);
});
});

View File

@@ -0,0 +1,76 @@
export type WorkspaceKeyIdentity = {
id: string;
};
export type WorkspaceSearchableKey = WorkspaceKeyIdentity & {
name: string;
};
export function filterWorkspaceKeys<T extends WorkspaceSearchableKey>(
keys: T[],
search: string,
) {
const query = search.trim().toLowerCase();
if (!query) {
return keys;
}
return keys.filter((item) => item.name.toLowerCase().includes(query));
}
export function resolveSelectedWorkspaceKey<T extends WorkspaceKeyIdentity>(
keys: T[],
selectedKeyId: string | null,
) {
if (keys.length === 0) {
return null;
}
return keys.find((item) => item.id === selectedKeyId) ?? keys[0];
}
export function resolveSelectedWorkspaceKeyId<T extends WorkspaceKeyIdentity>(
keys: T[],
selectedKeyId: string | null,
) {
return resolveSelectedWorkspaceKey(keys, selectedKeyId)?.id ?? null;
}
export function replaceWorkspaceKeyRecord<T extends WorkspaceKeyIdentity>(
current: T[],
next: T,
) {
let replaced = false;
const updated = current.map((item) => {
if (item.id !== next.id) {
return item;
}
replaced = true;
return {
...item,
...next,
};
});
return replaced ? updated : [next, ...current];
}
export function appendWorkspaceKeyRecords<T extends WorkspaceKeyIdentity>(
current: T[],
next: T[],
) {
const seen = new Set(current.map((item) => item.id));
const appended = [...current];
for (const item of next) {
if (seen.has(item.id)) {
continue;
}
seen.add(item.id);
appended.push(item);
}
return appended;
}

View File

@@ -0,0 +1,55 @@
import { describe, expect, it } from "vitest";
import {
isLocalSmokeFixtureConnection,
localSmokeFixtureCommandPresets,
localSmokeFixtureExpectedKeys,
localSmokeFixtureProfileId,
localSmokeFixtureSearchText,
} from "./local-smoke-fixture";
describe("local smoke fixture helpers", () => {
it("keeps the expected local smoke verification targets stable", () => {
expect(localSmokeFixtureProfileId).toBe("redis-local-fixture");
expect(localSmokeFixtureSearchText).toBe("smoke:*");
expect(localSmokeFixtureExpectedKeys).toEqual([
"smoke:string",
"smoke:string:ttl",
"smoke:hash",
"smoke:list",
"smoke:set",
"smoke:zset",
"smoke:stream",
]);
});
it("detects the built-in local fixture profile by id or target", () => {
expect(
isLocalSmokeFixtureConnection({
id: "redis-local-fixture",
target: { host: "10.0.0.1", port: 6379, database: 2 },
}),
).toBe(true);
expect(
isLocalSmokeFixtureConnection({
id: "custom-profile",
target: { host: "127.0.0.1", port: 6380, database: 0 },
}),
).toBe(true);
expect(
isLocalSmokeFixtureConnection({
id: "redis-local-fixture",
target: { host: "127.0.0.1", port: 6380, database: 1 },
}),
).toBe(true);
});
it("keeps fixture quick commands on a safe read-only path", () => {
expect(localSmokeFixtureCommandPresets.map((preset) => preset.command)).toEqual([
"PING",
"GET smoke:string",
"TTL smoke:string:ttl",
]);
});
});

View File

@@ -0,0 +1,53 @@
export const localSmokeFixtureProfileId = "redis-local-fixture";
export const localSmokeFixtureSearchText = "smoke:*";
export const localSmokeFixtureExpectedKeys = [
"smoke:string",
"smoke:string:ttl",
"smoke:hash",
"smoke:list",
"smoke:set",
"smoke:zset",
"smoke:stream",
] as const;
export const localSmokeFixtureCommandPresets = [
{
id: "ping",
label: "PING",
command: "PING",
summary: "Validate the desktop command bridge against the seeded fixture.",
},
{
id: "get-string",
label: "GET smoke:string",
command: "GET smoke:string",
summary: "Verify the string read path against the seeded smoke key.",
},
{
id: "ttl-string",
label: "TTL smoke:string:ttl",
command: "TTL smoke:string:ttl",
summary: "Verify the TTL path before using the inspector controls.",
},
] as const;
type LocalSmokeFixtureTarget = {
id: string;
target: {
host: string;
port: number;
database: number;
};
};
export function isLocalSmokeFixtureConnection(
connection: LocalSmokeFixtureTarget,
) {
return (
connection.id === localSmokeFixtureProfileId ||
(connection.target.host === "127.0.0.1" &&
connection.target.port === 6380 &&
connection.target.database === 0)
);
}

View File

@@ -0,0 +1,72 @@
import { describe, expect, it } from "vitest";
import { buildQaSnapshot } from "./qa-snapshot";
describe("qa snapshot", () => {
it("serializes the current smoke context into a stable line-oriented summary", () => {
expect(
buildQaSnapshot({
appName: "Redis GUI Foundation",
appVersion: "0.1.0",
runtimeMode: "live desktop bridge",
buildMode: "production",
connectionName: "redis-local-fixture",
endpoint: "127.0.0.1:6380",
databaseLabel: "db0",
smokeFixtureActive: true,
smokeFilter: "smoke:*",
selectedKeyName: "smoke:string",
selectedKeyType: "string",
selectedKeyTtl: "persistent",
browseVisibleCount: "7",
latestCommand: "PING",
latestCommandSource: "live",
latestCommandStatus: "ok",
bannerMessage: "Copied the current QA snapshot to the clipboard for smoke evidence.",
}),
).toEqual(
[
"app=Redis GUI Foundation",
"version=0.1.0",
"runtime=live desktop bridge",
"build=production",
"connection=redis-local-fixture",
"endpoint=127.0.0.1:6380",
"database=db0",
"smokeFixtureActive=yes",
"smokeFilter=smoke:*",
"selectedKey=smoke:string",
"selectedType=string",
"selectedTtl=persistent",
"browseVisible=7",
"latestCommand=PING",
"latestCommandSource=live",
"latestCommandStatus=ok",
"banner=Copied the current QA snapshot to the clipboard for smoke evidence.",
].join("\n"),
);
});
it("normalizes empty or missing values so smoke logs stay readable", () => {
expect(
buildQaSnapshot({
appName: "Redis GUI Foundation",
appVersion: "0.1.0",
runtimeMode: "browser demo fallback",
buildMode: "dev",
connectionName: "redis-dev-eu-1",
endpoint: "127.0.0.1:6379",
databaseLabel: "db1",
smokeFixtureActive: false,
smokeFilter: " ",
selectedKeyName: null,
selectedKeyType: null,
selectedKeyTtl: null,
browseVisibleCount: "0",
latestCommand: null,
latestCommandSource: null,
latestCommandStatus: null,
bannerMessage: "No key matches the current pattern.",
}),
).toContain("smokeFilter=none");
});
});

View File

@@ -0,0 +1,41 @@
export type QaSnapshotInput = {
appName: string;
appVersion: string;
runtimeMode: string;
buildMode: string;
connectionName: string;
endpoint: string;
databaseLabel: string;
smokeFixtureActive: boolean;
smokeFilter: string;
selectedKeyName: string | null;
selectedKeyType: string | null;
selectedKeyTtl: string | null;
browseVisibleCount: string;
latestCommand: string | null;
latestCommandSource: string | null;
latestCommandStatus: string | null;
bannerMessage: string;
};
export function buildQaSnapshot(input: QaSnapshotInput) {
return [
`app=${input.appName}`,
`version=${input.appVersion}`,
`runtime=${input.runtimeMode}`,
`build=${input.buildMode}`,
`connection=${input.connectionName}`,
`endpoint=${input.endpoint}`,
`database=${input.databaseLabel}`,
`smokeFixtureActive=${input.smokeFixtureActive ? "yes" : "no"}`,
`smokeFilter=${input.smokeFilter.trim() || "none"}`,
`selectedKey=${input.selectedKeyName ?? "none"}`,
`selectedType=${input.selectedKeyType ?? "none"}`,
`selectedTtl=${input.selectedKeyTtl ?? "none"}`,
`browseVisible=${input.browseVisibleCount}`,
`latestCommand=${input.latestCommand ?? "none"}`,
`latestCommandSource=${input.latestCommandSource ?? "none"}`,
`latestCommandStatus=${input.latestCommandStatus ?? "none"}`,
`banner=${input.bannerMessage}`,
].join("\n");
}