Files
redis-gui-foundation/apps/desktop/src/App.tsx
Senior Frontend Engineer bf6f8bc0bf feat(desktop): clarify key browser search states
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 08:02:18 +00:00

2658 lines
90 KiB
TypeScript

import { invoke } from "@tauri-apps/api/core";
import {
startTransition,
useDeferredValue,
useEffect,
useState,
type ChangeEvent,
type FormEvent,
type ReactElement,
} from "react";
import {
Activity,
CircleAlert,
ClipboardList,
Copy,
Database,
KeyRound,
PanelLeft,
PlugZap,
Search,
ShieldAlert,
TerminalSquare,
TimerReset,
Trash2,
} from "lucide-react";
import { Badge } from "./components/ui/badge";
import { Button } from "./components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} 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,
createAccentNotice,
createNeutralNotice,
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 {
buildInspectorEmptyStateMessage,
buildKeyBrowserEmptyStateMessage,
buildKeyBrowserStatusMessage,
resolveKeyBrowserSearchState,
} from "./lib/key-browser-search";
import {
parseDemoTtlToSeconds,
persistDemoKeyTtl,
updateDemoKeyTtl,
updateDemoStringValue,
} from "./lib/demo-workspace";
import { cn } from "./lib/utils";
declare const __APP_VERSION__: string;
type RedisKeyType = string;
type TlsMode = "disabled" | "preferred" | "required";
type ConnectionStatus = "healthy" | "idle" | "checking" | "error";
type ConsoleEntryStatus = "ok" | "blocked" | "error";
type ConsoleEntrySource = "live" | "mock";
type RedisValueWriteCapability = "none" | "replace_string";
type ConnectionProfile = {
id: string;
name: string;
environment: "dev" | "test" | "local";
detail: string;
status: ConnectionStatus;
password: string;
target: {
host: string;
port: number;
database: number;
username: string | null;
tls_mode: TlsMode;
};
};
type KeyRecord = {
id: string;
name: string;
type: RedisKeyType;
ttl: string;
preview: string;
editable: boolean;
value: string;
summary: string;
};
type ConsoleEntry = {
command: string;
output: string;
status: ConsoleEntryStatus;
source: ConsoleEntrySource;
};
type ConnectionDraft = {
name: string;
host: string;
port: string;
database: string;
user: string;
password: string;
};
type ConnectionDraftErrors = Partial<
Record<"name" | "host" | "port" | "database", string>
>;
type BackendBootstrap = {
app_name: string;
surface: string;
persistence: {
connection_catalog: string;
secrets: string;
};
command_execution: string;
key_browsing: string;
next_backend_milestone: string;
};
type RedisConnectionRequest = {
target: ConnectionProfile["target"];
password: string | null;
};
type ConnectionTestResult = {
selected_database: number;
authenticated_as: string | null;
round_trip_status: string;
};
type RedisCommandRequest = {
connection: RedisConnectionRequest;
command: string;
arguments: string[];
};
type RedisKeyTtl =
| { kind: "persistent" }
| { kind: "missing" }
| { kind: "expires_in_millis"; value: number };
type RedisKeyMetadata = {
name: string;
database: number;
exists: boolean;
key_type: string;
ttl: RedisKeyTtl;
};
type RedisKeyBrowseRequest = {
connection: RedisConnectionRequest;
cursor: string;
pattern: string | null;
page_size: number;
};
type RedisKeyBrowseResult = {
database: number;
submitted_cursor: string;
next_cursor: string;
has_more: boolean;
page_size: number;
pattern: string | null;
keys: RedisKeyMetadata[];
};
type RedisValueContent =
| { kind: "string"; value: string }
| { kind: "binary"; bytes: number[] };
type RedisFieldValueEntry = {
field: RedisValueContent;
value: RedisValueContent;
};
type RedisSortedSetEntry = {
member: RedisValueContent;
score: string;
};
type RedisStreamEntry = {
id: string;
fields: RedisFieldValueEntry[];
};
type RedisValueData =
| { kind: "missing" }
| { kind: "string"; value: RedisValueContent }
| { kind: "hash"; entries: RedisFieldValueEntry[] }
| { kind: "list"; items: RedisValueContent[] }
| { kind: "set"; members: RedisValueContent[] }
| { kind: "sorted_set"; entries: RedisSortedSetEntry[] }
| { kind: "stream"; entries: RedisStreamEntry[] };
type RedisValueRecord = {
metadata: RedisKeyMetadata;
data: RedisValueData;
write_capability: RedisValueWriteCapability;
ttl_write_supported: boolean;
};
type RedisValueReadRequest = {
connection: RedisConnectionRequest;
key: string;
};
type RedisValueWriteRequest = {
connection: RedisConnectionRequest;
key: string;
value: {
kind: "string";
value: string;
};
};
type RedisKeyTtlUpdate =
| { kind: "persist" }
| { kind: "expires_in_millis"; value: number };
type RedisKeyTtlUpdateRequest = {
connection: RedisConnectionRequest;
key: string;
operation: RedisKeyTtlUpdate;
};
type RedisKeyTtlUpdateResult = {
metadata: RedisKeyMetadata;
};
type RedisResponse =
| { kind: "null" }
| { kind: "integer"; value: number }
| { kind: "string"; value: string }
| { kind: "binary"; bytes: number[] }
| { kind: "array"; items: RedisResponse[] };
type CommandExecutionResult = {
command: string;
arguments: string[];
database: number;
response: RedisResponse;
};
const browsePageSize = 50;
const initialConnections: ConnectionProfile[] = [
{
id: "redis-dev-eu-1",
name: "redis-dev-eu-1",
environment: "dev",
detail: "13 ms",
status: "healthy",
password: "",
target: {
host: "127.0.0.1",
port: 6379,
database: 0,
username: null,
tls_mode: "disabled",
},
},
{
id: "redis-test-audit",
name: "redis-test-audit",
environment: "test",
detail: "28 ms",
status: "healthy",
password: "",
target: {
host: "10.42.2.19",
port: 6379,
database: 0,
username: "readonly",
tls_mode: "disabled",
},
},
{
id: "redis-local-fixture",
name: "redis-local-fixture",
environment: "local",
detail: "Fixture profile",
status: "idle",
password: "",
target: {
host: "127.0.0.1",
port: 6380,
database: 0,
username: null,
tls_mode: "disabled",
},
},
];
const initialDemoKeys: KeyRecord[] = [
{
id: "session:42",
name: "session:42",
type: "hash",
ttl: "13m",
preview: "user profile and auth state",
editable: false,
value: "userId=42, role=editor, featureFlag=beta",
summary: "Read-only in V1. Structured hash inspection ships before editing support to avoid unsafe partial writes.",
},
{
id: "cache:homepage",
name: "cache:homepage",
type: "string",
ttl: "48s",
preview: "serialized landing payload",
editable: true,
value: "{\"hero\":\"Redis GUI\",\"updatedAt\":\"2026-03-27T12:00:00Z\"}",
summary: "String keys are editable in V1 and should refresh browser metadata after save without reconnecting.",
},
{
id: "jobs:failed",
name: "jobs:failed",
type: "list",
ttl: "persistent",
preview: "3 queued failure payloads",
editable: false,
value: "[payload-1, payload-2, payload-3]",
summary: "Lists remain inspectable but read only in the first visible shell.",
},
{
id: "feature:enabled",
name: "feature:enabled",
type: "set",
ttl: "1h",
preview: "5 active feature flags",
editable: false,
value: "{search_v2, audit_log, compact_ui, qa_mode, canary_cli}",
summary: "Collections keep structural hints visible so support work stays fast without raw CLI fallback.",
},
];
const databases = ["db0", "db1", "db2", "db5"];
const fallbackBootstrap: BackendBootstrap = {
app_name: "Redis GUI Foundation",
surface: "desktop",
persistence: {
connection_catalog: "not_configured",
secrets: "memory_only",
},
command_execution: "single_instance_enabled",
key_browsing: "scan_metadata_enabled",
next_backend_milestone: "value_surface_and_catalog_persistence",
};
const initialConsoleHistory: ConsoleEntry[] = [
{
command: "TTL cache:homepage",
output: "(integer) 48",
status: "ok",
source: "mock",
},
{
command: "TYPE session:42",
output: "hash",
status: "ok",
source: "mock",
},
{
command: "FLUSHALL",
output: "Blocked in V1. Dangerous administrative commands are intentionally unavailable.",
status: "blocked",
source: "mock",
},
];
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>(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);
const [banner, setBanner] = useState<OperatorNotice>(
createNeutralNotice(
"String edits stay local to the shell until the backend write contract is wired.",
),
);
const [connectionDialogOpen, setConnectionDialogOpen] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [isConnectionTestRunning, setIsConnectionTestRunning] = useState(false);
const [isCommandRunning, setIsCommandRunning] = useState(false);
const [isBrowseLoading, setIsBrowseLoading] = useState(false);
const [isInspectorLoading, setIsInspectorLoading] = useState(false);
const [isSaveRunning, setIsSaveRunning] = useState(false);
const [isTtlUpdateRunning, setIsTtlUpdateRunning] = useState(false);
const [copyFeedback, setCopyFeedback] = useState<"idle" | "copied" | "unavailable">("idle");
const [liveKeys, setLiveKeys] = useState<KeyRecord[]>([]);
const [liveNextCursor, setLiveNextCursor] = useState("0");
const [liveHasMore, setLiveHasMore] = useState(false);
const [selectedValueRecord, setSelectedValueRecord] = useState<RedisValueRecord | null>(null);
const [ttlDraft, setTtlDraft] = useState("");
const [connectionDraft, setConnectionDraft] = useState<ConnectionDraft>({
name: "redis-staging-preview",
host: "10.42.8.12",
port: "6379",
database: "0",
user: "readonly",
password: "",
});
const deferredSearch = useDeferredValue(search);
const tauriAvailable = isTauriRuntimeAvailable();
const appVersion = __APP_VERSION__;
const buildMode = import.meta.env.DEV ? "dev" : "production";
const runtimeMode = tauriAvailable ? "live desktop bridge" : "browser demo fallback";
const showNeutralBanner = (message: string) => setBanner(createNeutralNotice(message));
const showAccentBanner = (message: string) => setBanner(createAccentNotice(message));
const showBackendErrorBanner = (error: unknown, context: BackendErrorContext) =>
setBanner(toBackendErrorNotice(error, context));
const activeConnection =
connections.find((connection) => connection.id === activeConnectionId) ??
connections[0];
const searchState = resolveKeyBrowserSearchState(search);
const deferredSearchState = resolveKeyBrowserSearchState(deferredSearch);
const filteredDemoKeys = filterWorkspaceKeys(demoKeys, deferredSearchState.trimmedQuery);
const filteredKeys = tauriAvailable ? liveKeys : filteredDemoKeys;
const draftErrors = validateConnectionDraft(connectionDraft, connections);
const draftErrorList = Object.values(draftErrors);
const selectedKey = resolveSelectedWorkspaceKey(filteredKeys, selectedKeyId);
const isEditable = tauriAvailable
? selectedValueRecord?.write_capability === "replace_string" &&
selectedValueRecord.data.kind === "string"
: selectedKey?.editable === true && selectedKey.type === "string";
const inspectorText = tauriAvailable
? formatInspectorValue(selectedValueRecord, selectedKey, isInspectorLoading)
: (selectedKey?.value ?? "");
const structuredInspectorSections = tauriAvailable
? buildStructuredInspectorSections(selectedValueRecord)
: [];
const showStructuredInspector =
selectedKey !== null &&
!isInspectorLoading &&
structuredInspectorSections.length > 0 &&
!isEditable;
const supportNote = tauriAvailable
? buildLiveSupportNote(selectedValueRecord, selectedKey)
: (selectedKey?.summary ??
`Search currently returns zero keys in ${activeDb}. This is an intentional empty state, not a backend failure.`);
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 &&
ttlWriteSupported &&
ttlDraft.trim() !== "" &&
ttlDraftError === null &&
!isTtlUpdateRunning;
const canPersistTtl =
selectedKey !== null &&
ttlWriteSupported &&
!isTtlUpdateRunning &&
(tauriAvailable
? selectedValueRecord?.metadata.ttl.kind !== "persistent"
: selectedKey?.ttl !== "persistent");
const latestConsoleEntry = consoleHistory[0] ?? null;
const keyBrowserStatusMessage = buildKeyBrowserStatusMessage({
databaseLabel: activeDb,
visibleCount: filteredKeys.length,
hasMore: tauriAvailable && liveHasMore,
isLoading: isBrowseLoading,
search: deferredSearchState,
});
const keyBrowserEmptyStateMessage = buildKeyBrowserEmptyStateMessage({
databaseLabel: activeDb,
isLoading: isBrowseLoading,
search: deferredSearchState,
});
const inspectorEmptyStateMessage = buildInspectorEmptyStateMessage(
activeDb,
deferredSearchState,
);
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) {
startTransition(() => {
setBootstrap(fallbackBootstrap);
});
return;
}
let active = true;
invoke<BackendBootstrap>("backend_bootstrap")
.then((result) => {
if (!active) {
return;
}
startTransition(() => {
setBootstrap(result);
});
})
.catch(() => {
if (!active) {
return;
}
startTransition(() => {
setBootstrap(fallbackBootstrap);
});
});
return () => {
active = false;
};
}, [tauriAvailable]);
useEffect(() => {
if (!tauriAvailable) {
startTransition(() => {
setLiveKeys([]);
setLiveNextCursor("0");
setLiveHasMore(false);
setSelectedValueRecord(null);
setDraftValue(selectedKey?.value ?? "");
setTtlDraft("");
});
return;
}
let active = true;
const request: RedisKeyBrowseRequest = {
connection: toRedisConnectionRequest(activeConnection, activeDb),
cursor: "0",
pattern: deferredSearchState.requestPattern,
page_size: browsePageSize,
};
startTransition(() => {
setIsBrowseLoading(true);
setLiveKeys([]);
setLiveNextCursor("0");
setLiveHasMore(false);
setSelectedValueRecord(null);
setDraftValue("");
setTtlDraft("");
});
invoke<RedisKeyBrowseResult>("browse_redis_keys", { request })
.then((result) => {
if (!active) {
return;
}
const nextKeys = result.keys.map(toLiveKeyRecordFromMetadata);
startTransition(() => {
setLiveKeys(nextKeys);
setLiveNextCursor(result.next_cursor);
setLiveHasMore(result.has_more);
setSelectedKeyId((current) => resolveSelectedWorkspaceKeyId(nextKeys, current));
setIsBrowseLoading(false);
});
})
.catch((error) => {
if (!active) {
return;
}
startTransition(() => {
setLiveKeys([]);
setLiveNextCursor("0");
setLiveHasMore(false);
setSelectedKeyId(null);
setSelectedValueRecord(null);
setDraftValue("");
setTtlDraft("");
setIsBrowseLoading(false);
showBackendErrorBanner(error, {
operation: "key_browse",
connectionName: activeConnection.name,
databaseLabel: activeDb,
});
});
});
return () => {
active = false;
};
}, [activeConnectionId, activeDb, deferredSearchState.requestPattern, tauriAvailable]);
useEffect(() => {
if (!tauriAvailable) {
startTransition(() => {
setSelectedValueRecord(null);
setDraftValue(selectedKey?.value ?? "");
setTtlDraft("");
});
return;
}
if (!selectedKey) {
startTransition(() => {
setSelectedValueRecord(null);
setDraftValue("");
setTtlDraft("");
setIsInspectorLoading(false);
});
return;
}
let active = true;
const request: RedisValueReadRequest = {
connection: toRedisConnectionRequest(activeConnection, activeDb),
key: selectedKey.name,
};
startTransition(() => {
setSelectedValueRecord(null);
setDraftValue("");
setTtlDraft("");
setIsInspectorLoading(true);
});
invoke<RedisValueRecord>("read_redis_value", { request })
.then((record) => {
if (!active) {
return;
}
const nextRecord = toLiveKeyRecordFromValue(record);
startTransition(() => {
setSelectedValueRecord(record);
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
record.data.kind === "string" && record.write_capability === "replace_string"
? formatRedisValueContent(record.data.value)
: formatRedisValueData(record.data),
);
setTtlDraft(record.metadata.ttl.kind === "expires_in_millis" ? String(record.metadata.ttl.value) : "");
setIsInspectorLoading(false);
});
})
.catch((error) => {
if (!active) {
return;
}
startTransition(() => {
setSelectedValueRecord(null);
setDraftValue("");
setTtlDraft("");
setIsInspectorLoading(false);
showBackendErrorBanner(error, {
operation: "value_read",
connectionName: activeConnection.name,
databaseLabel: activeDb,
keyName: selectedKey.name,
});
});
});
return () => {
active = false;
};
}, [activeConnectionId, activeDb, selectedKey?.id, tauriAvailable]);
const handleConnectionDraftChange =
(field: keyof typeof connectionDraft) =>
(event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setConnectionDraft((current) => ({
...current,
[field]: value,
}));
};
const handleSelectConnection = (connection: ConnectionProfile) => {
startTransition(() => {
setActiveConnectionId(connection.id);
setActiveDb(formatDatabaseLabel(connection.target.database));
setSelectedKeyId(null);
showNeutralBanner(
`Switched workspace context to ${connection.name} on ${formatDatabaseLabel(connection.target.database)}.`,
);
});
};
const handleSelectDatabase = (database: string) => {
const nextDatabase = parseDatabaseLabel(database);
startTransition(() => {
setActiveDb(database);
setSelectedKeyId(null);
setConnections((current) =>
current.map((connection) =>
connection.id === activeConnectionId
? {
...connection,
target: {
...connection.target,
database: nextDatabase,
},
}
: connection,
),
);
showNeutralBanner(`Scoped the workspace to ${database} for ${activeConnection.name}.`);
});
};
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]}`);
return;
}
const nextDatabase = parseDatabaseInput(connectionDraft.database);
const nextName = connectionDraft.name.trim();
const nextConnection: ConnectionProfile = {
id: nextName,
name: nextName,
environment: "dev",
detail: "Draft profile",
status: "idle",
password: connectionDraft.password,
target: {
host: connectionDraft.host.trim() || "127.0.0.1",
port: parsePortInput(connectionDraft.port),
database: nextDatabase,
username: normalizeOptionalString(connectionDraft.user),
tls_mode: "disabled",
},
};
startTransition(() => {
setConnections((current) => [nextConnection, ...current]);
setActiveConnectionId(nextConnection.id);
setActiveDb(formatDatabaseLabel(nextDatabase));
setSelectedKeyId(null);
showAccentBanner(
"A draft connection profile was added to the shell. Credentials remain memory-only.",
);
setConnectionDialogOpen(false);
});
};
const handleSaveString = async () => {
if (!selectedKey) {
showNeutralBanner(`Select a key in ${activeDb} before attempting a shell-level save.`);
return;
}
if (!tauriAvailable) {
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;
}
startTransition(() => {
setIsSaveRunning(true);
});
try {
const result = await invoke<RedisValueRecord>("write_redis_value", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
key: selectedKey.name,
value: {
kind: "string",
value: draftValue,
},
} satisfies RedisValueWriteRequest,
});
const nextRecord = toLiveKeyRecordFromValue(result);
startTransition(() => {
setSelectedValueRecord(result);
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
result.data.kind === "string"
? formatRedisValueContent(result.data.value)
: formatRedisValueData(result.data),
);
setIsSaveRunning(false);
showAccentBanner(
`Saved live string value for ${selectedKey.name} in ${activeDb} and preserved the current TTL boundary.`,
);
});
} catch (error) {
startTransition(() => {
setIsSaveRunning(false);
showBackendErrorBanner(error, {
operation: "value_write",
connectionName: activeConnection.name,
databaseLabel: activeDb,
keyName: selectedKey.name,
});
});
}
};
const handleDeleteKey = () => {
if (!selectedKey) {
return;
}
setDeleteDialogOpen(false);
showNeutralBanner(
`Delete confirmation completed for ${selectedKey.name} in ${activeDb}. Mutation stays mocked until a delete contract is approved.`,
);
};
const handleApplyTtl = async () => {
if (!selectedKey) {
showNeutralBanner(`Select a key in ${activeDb} before attempting a TTL update.`);
return;
}
const validationError = validateTtlDraft(ttlDraft);
if (validationError) {
showNeutralBanner(`TTL update is invalid: ${validationError}`);
return;
}
if (!tauriAvailable) {
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;
}
startTransition(() => {
setIsTtlUpdateRunning(true);
});
try {
const result = await invoke<RedisKeyTtlUpdateResult>("update_redis_key_ttl", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
key: selectedKey.name,
operation: {
kind: "expires_in_millis",
value: Number.parseInt(ttlDraft, 10),
},
} satisfies RedisKeyTtlUpdateRequest,
});
startTransition(() => {
syncUpdatedMetadata(result.metadata);
setTtlDraft(
result.metadata.ttl.kind === "expires_in_millis"
? String(result.metadata.ttl.value)
: "",
);
setIsTtlUpdateRunning(false);
showAccentBanner(
`Updated live TTL for ${selectedKey.name} to ${formatRedisKeyTtl(result.metadata.ttl)} in ${activeDb}.`,
);
});
} catch (error) {
startTransition(() => {
setIsTtlUpdateRunning(false);
showBackendErrorBanner(error, {
operation: "ttl_update",
connectionName: activeConnection.name,
databaseLabel: activeDb,
keyName: selectedKey.name,
});
});
}
};
const handlePersistTtl = async () => {
if (!selectedKey) {
showNeutralBanner(`Select a key in ${activeDb} before attempting to remove TTL.`);
return;
}
if (!tauriAvailable) {
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;
}
startTransition(() => {
setIsTtlUpdateRunning(true);
});
try {
const result = await invoke<RedisKeyTtlUpdateResult>("update_redis_key_ttl", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
key: selectedKey.name,
operation: {
kind: "persist",
},
} satisfies RedisKeyTtlUpdateRequest,
});
startTransition(() => {
syncUpdatedMetadata(result.metadata);
setTtlDraft("");
setIsTtlUpdateRunning(false);
showAccentBanner(
`Removed TTL for ${selectedKey.name}; the key is now ${formatRedisKeyTtl(result.metadata.ttl)}.`,
);
});
} catch (error) {
startTransition(() => {
setIsTtlUpdateRunning(false);
showBackendErrorBanner(error, {
operation: "ttl_remove",
connectionName: activeConnection.name,
databaseLabel: activeDb,
keyName: selectedKey.name,
});
});
}
};
const handleRefreshInspector = async () => {
if (!selectedKey) {
showNeutralBanner(
`No key is selected in ${activeDb}. Adjust or clear the current search pattern first.`,
);
return;
}
if (!tauriAvailable) {
showNeutralBanner(
`Refreshed shell metadata for ${selectedKey.name} in ${activeDb}. Live value reload remains downstream of backend browse contracts.`,
);
return;
}
startTransition(() => {
setIsInspectorLoading(true);
setSelectedValueRecord(null);
setDraftValue("");
});
try {
const result = await invoke<RedisValueRecord>("read_redis_value", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
key: selectedKey.name,
} satisfies RedisValueReadRequest,
});
const nextRecord = toLiveKeyRecordFromValue(result);
startTransition(() => {
setSelectedValueRecord(result);
setLiveKeys((current) => replaceWorkspaceKeyRecord(current, nextRecord));
setDraftValue(
result.data.kind === "string" && result.write_capability === "replace_string"
? formatRedisValueContent(result.data.value)
: formatRedisValueData(result.data),
);
setTtlDraft(result.metadata.ttl.kind === "expires_in_millis" ? String(result.metadata.ttl.value) : "");
setIsInspectorLoading(false);
showAccentBanner(
`Refreshed live metadata and value surface for ${selectedKey.name} in ${activeDb}.`,
);
});
} catch (error) {
startTransition(() => {
setIsInspectorLoading(false);
showBackendErrorBanner(error, {
operation: "inspector_refresh",
connectionName: activeConnection.name,
databaseLabel: activeDb,
keyName: selectedKey.name,
});
});
}
};
const handleCopyQaSnapshot = async () => {
if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) {
setCopyFeedback("unavailable");
showNeutralBanner(
"Clipboard export is unavailable in this runtime. Copy the QA snapshot text manually from the side rail.",
);
return;
}
try {
await navigator.clipboard.writeText(qaSnapshot);
setCopyFeedback("copied");
showAccentBanner("Copied the current QA snapshot to the clipboard for smoke evidence.");
} catch {
setCopyFeedback("unavailable");
showNeutralBanner(
"Clipboard export failed in this runtime. Copy the QA snapshot text manually from the side rail.",
);
}
};
const syncUpdatedMetadata = (metadata: RedisKeyMetadata) => {
if (selectedValueRecord) {
const nextRecord = {
...selectedValueRecord,
metadata,
};
setSelectedValueRecord(nextRecord);
setLiveKeys((current) =>
replaceWorkspaceKeyRecord(current, toLiveKeyRecordFromValue(nextRecord)),
);
return;
}
setLiveKeys((current) =>
replaceWorkspaceKeyRecord(current, toLiveKeyRecordFromMetadata(metadata)),
);
};
const handleLoadMoreKeys = async () => {
if (!tauriAvailable || !liveHasMore || isBrowseLoading) {
return;
}
startTransition(() => {
setIsBrowseLoading(true);
});
try {
const result = await invoke<RedisKeyBrowseResult>("browse_redis_keys", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
cursor: liveNextCursor,
pattern: deferredSearchState.requestPattern,
page_size: browsePageSize,
} satisfies RedisKeyBrowseRequest,
});
const nextKeys = result.keys.map(toLiveKeyRecordFromMetadata);
startTransition(() => {
setLiveKeys((current) => appendWorkspaceKeyRecords(current, nextKeys));
setLiveNextCursor(result.next_cursor);
setLiveHasMore(result.has_more);
setIsBrowseLoading(false);
});
} catch (error) {
startTransition(() => {
setIsBrowseLoading(false);
showBackendErrorBanner(error, {
operation: "key_browse",
connectionName: activeConnection.name,
databaseLabel: activeDb,
});
});
}
};
const handleTestConnection = async () => {
const connection = activeConnection;
const request = toRedisConnectionRequest(connection, activeDb);
startTransition(() => {
setIsConnectionTestRunning(true);
setConnections((current) =>
current.map((item) =>
item.id === connection.id
? {
...item,
status: "checking",
detail: "Live test in progress",
}
: item,
),
);
showNeutralBanner(
`Testing ${connection.name} against ${activeDb} using the current desktop contract.`,
);
});
if (!tauriAvailable) {
startTransition(() => {
setConnections((current) =>
current.map((item) =>
item.id === connection.id
? {
...item,
status: "idle",
detail: "Demo fallback",
}
: item,
),
);
showNeutralBanner(
`Tauri runtime is unavailable in browser dev mode, so connection testing stayed in demo fallback for ${connection.name}.`,
);
setIsConnectionTestRunning(false);
});
return;
}
try {
const result = await invoke<ConnectionTestResult>("test_redis_connection", {
request,
});
startTransition(() => {
setConnections((current) =>
current.map((item) =>
item.id === connection.id
? {
...item,
status: "healthy",
detail: result.round_trip_status,
target: {
...item.target,
database: result.selected_database,
username: result.authenticated_as ?? item.target.username,
},
}
: item,
),
);
setActiveDb(formatDatabaseLabel(result.selected_database));
showAccentBanner(
`Live Redis test passed for ${connection.name} on ${formatDatabaseLabel(result.selected_database)}. Round-trip status: ${result.round_trip_status}.`,
);
setIsConnectionTestRunning(false);
});
} catch (error) {
startTransition(() => {
setConnections((current) =>
current.map((item) =>
item.id === connection.id
? {
...item,
status: "error",
detail: formatBackendErrorCodeLabel(toBackendError(error).code),
}
: item,
),
);
showBackendErrorBanner(error, {
operation: "connection_test",
connectionName: connection.name,
databaseLabel: activeDb,
});
setIsConnectionTestRunning(false);
});
}
};
const handleConsoleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const trimmed = consoleInput.trim();
if (!trimmed) {
return;
}
const tokens = tokenizeCommand(trimmed);
if (tokens.length === 0) {
return;
}
const [command, ...argumentsList] = tokens;
const upper = command.toUpperCase();
const blocked = upper.includes("FLUSH") || upper.includes("CONFIG") || upper.includes("SHUTDOWN");
if (blocked) {
startTransition(() => {
setConsoleHistory((current) => [
{
command: trimmed,
output: "Blocked in V1. Destructive or administrative commands remain outside the visible shell.",
status: "blocked",
source: "mock",
},
...current.slice(0, 4),
]);
showNeutralBanner(
`Blocked ${command.toUpperCase()} in ${activeDb}. Administrative commands remain outside the visible shell.`,
);
});
return;
}
startTransition(() => {
setIsCommandRunning(true);
});
if (!tauriAvailable) {
startTransition(() => {
setConsoleHistory((current) => [
{
command: trimmed,
output: buildMockCommandOutput(command, selectedKey),
status: "ok",
source: "mock",
},
...current.slice(0, 4),
]);
showNeutralBanner(
`Tauri runtime is unavailable in browser dev mode, so ${command.toUpperCase()} ran against the visible demo data only.`,
);
setConsoleInput(trimmed);
setIsCommandRunning(false);
});
return;
}
try {
const result = await invoke<CommandExecutionResult>("execute_redis_command", {
request: {
connection: toRedisConnectionRequest(activeConnection, activeDb),
command,
arguments: argumentsList,
} satisfies RedisCommandRequest,
});
startTransition(() => {
setConsoleHistory((current) => [
{
command: [result.command, ...result.arguments].join(" "),
output: formatRedisResponse(result.response),
status: "ok",
source: "live",
},
...current.slice(0, 4),
]);
showAccentBanner(
`Live Redis command completed on ${activeConnection.name} / ${formatDatabaseLabel(result.database)}.`,
);
setConsoleInput(trimmed);
setIsCommandRunning(false);
});
} catch (error) {
startTransition(() => {
setConsoleHistory((current) => [
{
command: trimmed,
output: toBackendErrorNotice(error, {
operation: "command_execution",
connectionName: activeConnection.name,
databaseLabel: activeDb,
commandName: command,
}).message,
status: "error",
source: "live",
},
...current.slice(0, 4),
]);
showBackendErrorBanner(error, {
operation: "command_execution",
connectionName: activeConnection.name,
databaseLabel: activeDb,
commandName: command,
});
setConsoleInput(trimmed);
setIsCommandRunning(false);
});
}
};
return (
<div className="min-h-screen bg-[var(--surface-base)] text-[var(--ink-0)]">
<div className="mx-auto flex min-h-screen max-w-[1600px] flex-col px-4 py-4 sm:px-6 lg:px-8">
<header className="mb-4 flex flex-col gap-3 rounded-[28px] border border-[var(--line-soft)] bg-[var(--surface-raised)] px-5 py-4 shadow-[var(--shadow-panel)] lg:flex-row lg:items-center lg:justify-between">
<div className="flex items-start gap-3">
<div className="mt-0.5 rounded-2xl border border-[var(--line-strong)] bg-[var(--surface-strong)] p-3 text-[var(--accent-strong)]">
<PanelLeft className="h-5 w-5" />
</div>
<div>
<p className="font-mono text-xs uppercase tracking-[0.28em] text-[var(--ink-muted)]">
{bootstrap.app_name}
</p>
<h1 className="mt-1 text-2xl font-semibold tracking-[-0.03em]">
Quiet desktop workspace for Redis inspection and low-risk edits
</h1>
<div className="mt-3 flex flex-wrap gap-2">
<Badge variant="neutral">v{appVersion}</Badge>
<Badge variant={tauriAvailable ? "accent" : "neutral"}>{runtimeMode}</Badge>
<Badge variant="neutral">{buildMode}</Badge>
</div>
</div>
</div>
<div className="grid gap-2 text-sm text-[var(--ink-soft)] sm:grid-cols-2 xl:grid-cols-4">
<StatusPill
icon={<PlugZap className="h-4 w-4" />}
label="Connection"
value={activeConnection.name}
/>
<StatusPill
icon={<Database className="h-4 w-4" />}
label="Database"
value={activeDb}
/>
<StatusPill
icon={<Activity className="h-4 w-4" />}
label="Status"
value={formatConnectionStatus(activeConnection)}
/>
<StatusPill
icon={<PanelLeft className="h-4 w-4" />}
label="Build"
value={`v${appVersion}`}
/>
</div>
</header>
<div
className={cn(
"mb-4 rounded-[24px] border px-4 py-3 shadow-[var(--shadow-panel)]",
banner.tone === "danger"
? "border-[var(--danger-soft)] bg-[var(--danger-faint)]"
: banner.tone === "accent"
? "border-[var(--accent-strong)] bg-[var(--accent-faint)]"
: "border-[var(--line-soft)] bg-[var(--surface-raised)]",
)}
>
<p
className={cn(
"text-sm",
banner.tone === "danger"
? "text-[var(--danger-strong)]"
: banner.tone === "accent"
? "text-[var(--accent-strong)]"
: "text-[var(--ink-soft)]",
)}
>
{banner.message}
</p>
</div>
<main className="grid min-h-0 flex-1 gap-4 xl:grid-cols-[310px_minmax(360px,1.1fr)_minmax(420px,1fr)]">
<aside className="rounded-[28px] border border-[var(--line-soft)] bg-[var(--surface-raised)] p-4 shadow-[var(--shadow-panel)]">
<div className="space-y-5">
<section>
<SectionEyebrow icon={<PlugZap className="h-4 w-4" />} label="Connection library" />
<div className="mt-3 space-y-2">
{connections.map((connection) => {
const active = connection.id === activeConnection.id;
return (
<button
key={connection.id}
type="button"
onClick={() => handleSelectConnection(connection)}
className={cn(
"w-full rounded-[22px] border px-4 py-3 text-left transition",
active
? "border-[var(--accent-strong)] bg-[var(--accent-faint)]"
: "border-[var(--line-soft)] bg-[var(--surface-base)] hover:border-[var(--line-strong)]",
)}
>
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--ink-0)]">{connection.name}</p>
<p className="mt-1 text-sm text-[var(--ink-soft)]">{formatEndpoint(connection.target)}</p>
<p className="mt-2 font-mono text-xs uppercase tracking-[0.14em] text-[var(--ink-muted)]">
{formatConnectionCardDetail(connection)}
</p>
</div>
<Badge variant={active ? "accent" : "neutral"}>{connection.environment}</Badge>
</div>
</button>
);
})}
</div>
<div className="mt-3 grid grid-cols-2 gap-2">
<Dialog open={connectionDialogOpen} onOpenChange={setConnectionDialogOpen}>
<DialogTrigger asChild>
<Button variant="default" size="sm">
New
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<p className="font-mono text-xs uppercase tracking-[0.22em] text-[var(--ink-muted)]">
Connection draft
</p>
<DialogTitle>Create a visible shell profile</DialogTitle>
<DialogDescription>
This form demonstrates the entry point and keeps credentials out of persistent storage in the current foundation scope.
</DialogDescription>
</DialogHeader>
<div className="grid gap-3 sm:grid-cols-2">
<div className="grid gap-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-name">
Profile name
</label>
<Input
id="draft-name"
value={connectionDraft.name}
onChange={handleConnectionDraftChange("name")}
/>
</div>
<div className="grid gap-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-user">
User
</label>
<Input
id="draft-user"
value={connectionDraft.user}
onChange={handleConnectionDraftChange("user")}
/>
</div>
<div className="grid gap-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-host">
Host
</label>
<Input
id="draft-host"
value={connectionDraft.host}
onChange={handleConnectionDraftChange("host")}
/>
</div>
<div className="grid gap-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-port">
Port
</label>
<Input
id="draft-port"
value={connectionDraft.port}
onChange={handleConnectionDraftChange("port")}
/>
</div>
<div className="grid gap-2 sm:col-span-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-db">
Default DB
</label>
<Input
id="draft-db"
value={connectionDraft.database}
onChange={handleConnectionDraftChange("database")}
/>
</div>
<div className="grid gap-2 sm:col-span-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="draft-password">
Password
</label>
<Input
id="draft-password"
type="password"
value={connectionDraft.password}
onChange={handleConnectionDraftChange("password")}
placeholder="Optional, memory-only"
/>
</div>
</div>
<div className="rounded-[22px] border border-[var(--line-soft)] bg-[var(--surface-base)] px-4 py-3 text-sm text-[var(--ink-soft)]">
Secrets stay memory-only in the foundation phase. This dialog is intentionally limited to a visible draft flow, and TLS remains fixed to disabled until the product scope asks for transport controls.
</div>
{draftErrorList.length > 0 ? (
<div className="rounded-[22px] border border-[var(--danger-soft)] bg-[var(--danger-faint)] px-4 py-3 text-sm text-[var(--danger-strong)]">
{draftErrorList[0]}
</div>
) : null}
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button onClick={handleCreateDraftConnection} disabled={draftErrorList.length > 0}>
Create draft
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Button
variant="outline"
size="sm"
onClick={handleTestConnection}
disabled={isConnectionTestRunning}
>
{isConnectionTestRunning ? "Testing..." : tauriAvailable ? "Test live" : "Test demo"}
</Button>
</div>
</section>
<section>
<SectionEyebrow icon={<Database className="h-4 w-4" />} label="Database switcher" />
<div className="mt-3 grid gap-2">
{databases.map((database) => {
const active = database === activeDb;
return (
<button
key={database}
type="button"
onClick={() => handleSelectDatabase(database)}
className={cn(
"flex items-center justify-between rounded-2xl border px-3 py-2 text-left transition",
active
? "border-[var(--accent-strong)] bg-[var(--accent-faint)] text-[var(--ink-0)]"
: "border-[var(--line-soft)] bg-[var(--surface-base)] text-[var(--ink-soft)] hover:border-[var(--line-strong)] hover:text-[var(--ink-0)]",
)}
>
<span className="font-medium">{database}</span>
<span className="font-mono text-xs">{active ? "active" : "idle"}</span>
</button>
);
})}
</div>
</section>
<section>
<SectionEyebrow icon={<ShieldAlert className="h-4 w-4" />} label="Safety posture" />
<div className="mt-3 space-y-3 rounded-[22px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4 text-sm text-[var(--ink-soft)]">
<InfoLine label="Surface" value={bootstrap.surface} />
<InfoLine label="Connection catalog" value={bootstrap.persistence.connection_catalog} />
<InfoLine label="Secrets" value={bootstrap.persistence.secrets} />
<InfoLine label="Command execution" value={bootstrap.command_execution} />
<InfoLine label="Key browsing" value={bootstrap.key_browsing} />
<InfoLine label="Next backend milestone" value={bootstrap.next_backend_milestone} />
<p>Delete and rename stay behind an explicit confirmation step.</p>
<p>Administrative commands stay blocked in both the UI and backend scope.</p>
</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">
<p className="text-sm text-[var(--ink-soft)]">
Copy this summary into a smoke log when screenshots are unavailable.
</p>
<Textarea
readOnly
value={qaSnapshot}
className="mt-3 h-40 resize-none border-[var(--line-soft)] bg-[var(--surface-muted)] font-mono text-xs text-[var(--ink-soft)]"
/>
<div className="mt-3 flex items-center justify-between gap-3">
<p className="text-xs text-[var(--ink-muted)]">
{copyFeedback === "copied"
? "Snapshot copied."
: copyFeedback === "unavailable"
? "Clipboard unavailable in this runtime."
: "Includes version, runtime, context, and latest command state."}
</p>
<Button variant="outline" size="sm" onClick={handleCopyQaSnapshot}>
<Copy className="h-4 w-4" />
Copy snapshot
</Button>
</div>
</div>
</section>
</div>
</aside>
<section className="flex min-h-0 flex-col rounded-[28px] border border-[var(--line-soft)] bg-[var(--surface-raised)] p-4 shadow-[var(--shadow-panel)]">
<div className="flex flex-col gap-4 border-b border-[var(--line-soft)] pb-4">
<div className="flex items-start justify-between gap-3">
<div>
<SectionEyebrow icon={<KeyRound className="h-4 w-4" />} label="Key browser" />
<h2 className="mt-2 text-xl font-semibold tracking-[-0.03em]">Incremental browse with fast pattern search</h2>
</div>
<Badge variant="neutral">
{tauriAvailable
? `${filteredKeys.length}${liveHasMore ? "+" : ""} visible in ${activeDb}`
: `${filteredKeys.length} of ${demoKeys.length} keys in ${activeDb}`}
</Badge>
</div>
<label className="flex items-center gap-3 rounded-2xl border border-[var(--line-soft)] bg-[var(--surface-base)] px-3 py-3">
<Search className="h-4 w-4 text-[var(--ink-muted)]" />
<input
value={search}
onChange={(event) => setSearch(event.target.value)}
className="w-full bg-transparent text-sm text-[var(--ink-0)] outline-none placeholder:text-[var(--ink-muted)]"
placeholder="Search key name or pattern"
/>
{searchState.mode === "all" ? null : (
<Button
variant="ghost"
size="sm"
onClick={() => setSearch("")}
className="shrink-0 px-2 py-1 text-xs"
>
Clear
</Button>
)}
</label>
<div className="flex items-center justify-between gap-3 text-xs text-[var(--ink-muted)]">
<div className="flex items-center gap-2">
<Badge variant="neutral" className="normal-case tracking-normal">
{deferredSearchState.mode === "all" ? "all keys" : deferredSearchState.mode === "pattern" ? "pattern" : "contains"}
</Badge>
<p>{keyBrowserStatusMessage}</p>
</div>
</div>
</div>
<div className="mt-4 min-h-0 flex-1 space-y-2 overflow-auto pr-1">
{filteredKeys.map((item) => {
const active = item.id === selectedKey?.id;
return (
<button
key={item.id}
type="button"
onClick={() => {
setSelectedKeyId(item.id);
setDraftValue(item.value);
}}
className={cn(
"w-full rounded-[24px] border px-4 py-4 text-left transition",
active
? "border-[var(--accent-strong)] bg-[var(--accent-faint)]"
: "border-[var(--line-soft)] bg-[var(--surface-base)] hover:border-[var(--line-strong)]",
)}
>
<div className="flex items-start justify-between gap-3">
<div>
<p className="font-mono text-sm text-[var(--ink-0)]">{item.name}</p>
<p className="mt-1 text-sm text-[var(--ink-soft)]">{item.preview}</p>
</div>
<div className="flex flex-col items-end gap-2">
<Badge variant={active ? "accent" : "neutral"}>{item.type}</Badge>
<span className="font-mono text-xs text-[var(--ink-muted)]">{item.ttl}</span>
</div>
</div>
</button>
);
})}
{filteredKeys.length === 0 ? (
<div className="rounded-[24px] border border-dashed border-[var(--line-strong)] bg-[var(--surface-base)] px-4 py-8 text-center text-sm text-[var(--ink-soft)]">
{keyBrowserEmptyStateMessage}
</div>
) : null}
{tauriAvailable && liveHasMore ? (
<Button
variant="outline"
onClick={handleLoadMoreKeys}
disabled={isBrowseLoading}
className="w-full"
>
{isBrowseLoading ? "Loading..." : "Load more keys"}
</Button>
) : null}
</div>
</section>
<section className="flex min-h-0 flex-col gap-4">
<div className="flex min-h-0 flex-1 flex-col rounded-[28px] border border-[var(--line-soft)] bg-[var(--surface-raised)] p-4 shadow-[var(--shadow-panel)]">
<div className="flex items-start justify-between gap-3 border-b border-[var(--line-soft)] pb-4">
<div>
<SectionEyebrow icon={<Database className="h-4 w-4" />} label="Inspector" />
<h2 className="mt-2 text-xl font-semibold tracking-[-0.03em]">
{selectedKey?.name ?? "No key selected"}
</h2>
</div>
{selectedKey ? (
<div className="flex items-center gap-2">
<Badge variant="neutral">{selectedKey.type}</Badge>
<Badge variant="neutral">TTL {selectedKey.ttl}</Badge>
</div>
) : null}
</div>
<div className="grid gap-4 py-4 lg:grid-cols-[1.05fr_0.95fr]">
<div className="rounded-[24px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4">
<div className="flex items-center justify-between gap-3">
<p className="text-sm font-medium text-[var(--ink-0)]">Value surface</p>
<span className="font-mono text-xs text-[var(--ink-muted)]">
{selectedKey
? isInspectorLoading
? "loading"
: isEditable
? "editable"
: "read only"
: "awaiting selection"}
</span>
</div>
{selectedKey ? (
showStructuredInspector ? (
<StructuredInspectorPanels sections={structuredInspectorSections} />
) : (
<Textarea
value={isEditable ? draftValue : inspectorText}
onChange={(event) => setDraftValue(event.target.value)}
readOnly={!isEditable}
className={cn(
"mt-3 h-64 resize-none font-mono",
!isEditable &&
"border-[var(--line-soft)] bg-[var(--surface-muted)] text-[var(--ink-soft)]",
)}
/>
)
) : (
<div className="mt-3 flex h-64 items-center justify-center rounded-[22px] border border-dashed border-[var(--line-strong)] bg-[var(--surface-muted)] px-6 text-center text-sm text-[var(--ink-soft)]">
{inspectorEmptyStateMessage}
</div>
)}
<div className="mt-3 flex flex-wrap gap-2">
<Button
onClick={handleSaveString}
disabled={!selectedKey || !isEditable || isSaveRunning || isInspectorLoading}
>
{isSaveRunning ? "Saving..." : "Save string value"}
</Button>
<Button
variant="outline"
onClick={handleRefreshInspector}
disabled={!selectedKey || isInspectorLoading}
>
{isInspectorLoading ? "Refreshing..." : "Refresh metadata"}
</Button>
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<DialogTrigger asChild>
<Button
variant="subtle"
disabled={!selectedKey}
className="border-[var(--danger-soft)] bg-[var(--danger-faint)] text-[var(--danger-strong)] hover:border-[var(--danger-soft)]"
>
Delete key
</Button>
</DialogTrigger>
<DialogContent className="border-[var(--danger-soft)]">
<DialogHeader>
<div className="flex items-start gap-3">
<div className="rounded-2xl border border-[var(--danger-soft)] bg-[var(--danger-faint)] p-3 text-[var(--danger-strong)]">
<Trash2 className="h-5 w-5" />
</div>
<div>
<p className="font-mono text-xs uppercase tracking-[0.22em] text-[var(--ink-muted)]">
Confirm delete
</p>
<DialogTitle className="mt-2">
Remove <span className="font-mono">{selectedKey?.name ?? "the selected key"}</span> from {activeDb}?
</DialogTitle>
</div>
</div>
<DialogDescription>
This step stays separate from the browser and repeats both key name and DB context before mutation.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button variant="danger" onClick={handleDeleteKey}>
Delete key
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
<div className="space-y-4">
<InfoCard
title="Key metadata"
icon={<TimerReset className="h-4 w-4" />}
lines={[
`Active DB: ${activeDb}`,
`TTL: ${selectedKey?.ttl ?? "no active key"}`,
`Editor support: ${selectedKey ? (isEditable ? "string write enabled" : "inspection only") : "awaiting matching selection"}`,
]}
/>
<div className="rounded-[24px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4">
<div className="flex items-center gap-2">
<span className="text-[var(--accent-strong)]">
<TimerReset className="h-4 w-4" />
</span>
<p className="text-sm font-medium text-[var(--ink-0)]">TTL control</p>
</div>
<div className="mt-3 space-y-3">
<div>
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
Current state
</p>
<p className="mt-1 text-sm text-[var(--ink-0)]">
{selectedKey ? `TTL ${selectedKey.ttl}` : "Select a key to manage TTL."}
</p>
</div>
<div className="grid gap-2">
<label className="text-sm font-medium text-[var(--ink-0)]" htmlFor="ttl-millis">
Expire in milliseconds
</label>
<Input
id="ttl-millis"
inputMode="numeric"
placeholder="60000"
value={ttlDraft}
onChange={(event) => setTtlDraft(event.target.value)}
disabled={!selectedKey || isTtlUpdateRunning}
/>
</div>
<p
className={cn(
"text-sm",
ttlDraftError ? "text-[var(--danger-strong)]" : "text-[var(--ink-soft)]",
)}
>
{ttlDraftError ??
(tauriAvailable
? "Uses the existing desktop TTL contract. String saves preserve the active TTL."
: "Browser dev mode keeps TTL controls in explicit demo fallback.")}
</p>
<div className="flex flex-wrap gap-2">
<Button
size="sm"
onClick={handleApplyTtl}
disabled={!canSubmitTtlUpdate}
>
{isTtlUpdateRunning ? "Updating..." : "Set TTL"}
</Button>
<Button
variant="outline"
size="sm"
onClick={handlePersistTtl}
disabled={!canPersistTtl}
>
Remove TTL
</Button>
</div>
</div>
</div>
<InfoCard
title="Support note"
icon={<CircleAlert className="h-4 w-4" />}
lines={[supportNote]}
/>
</div>
</div>
</div>
<section className="rounded-[28px] border border-[var(--line-soft)] bg-[var(--surface-raised)] p-4 shadow-[var(--shadow-panel)]">
<div className="flex items-start justify-between gap-3 border-b border-[var(--line-soft)] pb-4">
<div>
<SectionEyebrow icon={<TerminalSquare className="h-4 w-4" />} label="Command tray" />
<h2 className="mt-2 text-xl font-semibold tracking-[-0.03em]">
Scoped console with visible safety boundary
</h2>
</div>
<Badge variant="neutral">
{activeConnection.name} / {activeDb}
</Badge>
</div>
<div className="mt-4 flex flex-col gap-3">
<form className="flex flex-col gap-3 sm:flex-row" onSubmit={handleConsoleSubmit}>
<Input
value={consoleInput}
onChange={(event) => setConsoleInput(event.target.value)}
className="font-mono"
/>
<Button type="submit" className="sm:self-start" disabled={isCommandRunning}>
{isCommandRunning ? "Running..." : tauriAvailable ? "Run live command" : "Run demo command"}
</Button>
</form>
<div className="grid gap-2">
{consoleHistory.map((entry) => (
<div
key={`${entry.command}-${entry.output}`}
className={cn(
"rounded-[22px] border px-4 py-3",
entry.status === "blocked"
? "border-[var(--danger-soft)] bg-[var(--danger-faint)]"
: "border-[var(--line-soft)] bg-[var(--surface-base)]",
)}
>
<div className="flex items-start justify-between gap-3">
<p className="font-mono text-sm text-[var(--ink-0)]">{entry.command}</p>
<div className="flex items-center gap-2">
<Badge variant={entry.source === "live" ? "accent" : "neutral"}>
{entry.source}
</Badge>
<Badge
variant={entry.status === "ok" ? "neutral" : "danger"}
>
{entry.status}
</Badge>
</div>
</div>
<p className="mt-1 text-sm text-[var(--ink-soft)]">{entry.output}</p>
</div>
))}
</div>
</div>
</section>
</section>
</main>
</div>
</div>
);
}
function SectionEyebrow({ icon, label }: { icon: ReactElement; label: string }) {
return (
<div className="flex items-center gap-2 font-mono text-xs uppercase tracking-[0.24em] text-[var(--ink-muted)]">
<span className="text-[var(--accent-strong)]">{icon}</span>
<span>{label}</span>
</div>
);
}
function StatusPill({
icon,
label,
value,
}: {
icon: ReactElement;
label: string;
value: string;
}) {
return (
<div className="flex items-center gap-3 rounded-full border border-[var(--line-soft)] bg-[var(--surface-base)] px-3 py-2">
<span className="text-[var(--accent-strong)]">{icon}</span>
<div>
<p className="text-[11px] uppercase tracking-[0.2em] text-[var(--ink-muted)]">{label}</p>
<p className="font-medium text-[var(--ink-0)]">{value}</p>
</div>
</div>
);
}
function InfoLine({ label, value }: { label: string; value: string }) {
return (
<div className="flex flex-col gap-1">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
{label}
</p>
<p className="text-sm text-[var(--ink-0)]">{value}</p>
</div>
);
}
function InfoCard({
title,
icon,
lines,
}: {
title: string;
icon: ReactElement;
lines: string[];
}) {
return (
<div className="rounded-[24px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-4">
<div className="flex items-center gap-2">
<span className="text-[var(--accent-strong)]">{icon}</span>
<p className="text-sm font-medium text-[var(--ink-0)]">{title}</p>
</div>
<div className="mt-3 space-y-2 text-sm text-[var(--ink-soft)]">
{lines.map((line) => (
<p key={line}>{line}</p>
))}
</div>
</div>
);
}
function StructuredInspectorPanels({
sections,
}: {
sections: StructuredInspectorSection[];
}) {
return (
<div className="mt-3 h-64 space-y-3 overflow-auto pr-1">
{sections.map((section) => (
<section
key={section.id}
className="rounded-[22px] border border-[var(--line-soft)] bg-[var(--surface-muted)] p-4"
>
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<p className="text-sm font-medium text-[var(--ink-0)]">{section.title}</p>
<p className="mt-1 text-sm text-[var(--ink-soft)]">{section.summary}</p>
</div>
<Badge variant="neutral">{section.rows.length} rows</Badge>
</div>
<div className="mt-4 hidden grid-cols-[minmax(0,0.38fr)_minmax(0,0.62fr)] gap-3 px-1 sm:grid">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
{section.primaryLabel}
</p>
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
{section.secondaryLabel}
</p>
</div>
<div className="mt-3 space-y-2">
{section.rows.map((row) => (
<div
key={row.id}
className="grid gap-3 rounded-[18px] border border-[var(--line-soft)] bg-[var(--surface-base)] p-3 sm:grid-cols-[minmax(0,0.38fr)_minmax(0,0.62fr)]"
>
<div>
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)] sm:hidden">
{section.primaryLabel}
</p>
<p className="mt-1 break-all font-mono text-sm text-[var(--ink-0)] sm:mt-0">
{row.primary}
</p>
</div>
<div>
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)] sm:hidden">
{section.secondaryLabel}
</p>
<p className="mt-1 break-all font-mono text-sm text-[var(--ink-0)] sm:mt-0">
{row.secondary}
</p>
</div>
{row.detailLines && row.detailLines.length > 0 ? (
<div className="sm:col-span-2">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
Payload
</p>
<div className="mt-2 space-y-2">
{row.detailLines.map((detail) => (
<div
key={`${row.id}-${detail}`}
className="rounded-2xl border border-[var(--line-soft)] bg-[var(--surface-muted)] px-3 py-2 font-mono text-xs text-[var(--ink-soft)]"
>
{detail}
</div>
))}
</div>
</div>
) : null}
</div>
))}
</div>
</section>
))}
</div>
);
}
function isTauriRuntimeAvailable() {
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
}
function normalizeOptionalString(value: string) {
const normalized = value.trim();
return normalized ? normalized : null;
}
function parseDatabaseLabel(value: string) {
const parsed = Number.parseInt(value.replace(/^db/i, ""), 10);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
}
function parseDatabaseInput(value: string) {
const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
}
function parsePortInput(value: string) {
const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : 6379;
}
function formatDatabaseLabel(database: number) {
return `db${database}`;
}
function formatEndpoint(target: ConnectionProfile["target"]) {
return `${target.host}:${target.port}`;
}
function formatConnectionStatus(connection: ConnectionProfile) {
if (connection.status === "healthy") {
return `Healthy · ${connection.detail}`;
}
if (connection.status === "checking") {
return "Testing live connection";
}
if (connection.status === "error") {
return `Error · ${connection.detail}`;
}
return connection.detail;
}
function formatConnectionCardDetail(connection: ConnectionProfile) {
if (connection.status === "checking") {
return "live test in progress";
}
if (connection.status === "healthy") {
return `checked ${connection.detail}`;
}
if (connection.status === "error") {
return `failed ${connection.detail}`;
}
return connection.detail;
}
function formatBackendErrorCodeLabel(code: string) {
return code.replace(/_/g, " ");
}
function toRedisConnectionRequest(
connection: ConnectionProfile,
activeDb: string,
): RedisConnectionRequest {
return {
target: {
...connection.target,
database: parseDatabaseLabel(activeDb),
username: connection.target.username,
},
password: normalizeOptionalString(connection.password),
};
}
function tokenizeCommand(value: string) {
return (value.match(/"[^"]*"|'[^']*'|\S+/g) ?? []).map((part) => {
if (
(part.startsWith("\"") && part.endsWith("\"")) ||
(part.startsWith("'") && part.endsWith("'"))
) {
return part.slice(1, -1);
}
return part;
});
}
function buildMockCommandOutput(command: string, selectedKey: KeyRecord | null) {
if (!selectedKey) {
return "No key is selected in the visible shell.";
}
const upper = command.toUpperCase();
if (upper === "GET") {
return selectedKey.value;
}
if (upper === "TTL") {
return `(integer) ${parseDemoTtlToSeconds(selectedKey.ttl)}`;
}
if (upper === "TYPE") {
return selectedKey.type;
}
return "OK (demo result)";
}
function validateConnectionDraft(
draft: ConnectionDraft,
connections: ConnectionProfile[],
): ConnectionDraftErrors {
const errors: ConnectionDraftErrors = {};
const trimmedName = draft.name.trim();
const trimmedHost = draft.host.trim();
const port = Number.parseInt(draft.port, 10);
const database = Number.parseInt(draft.database, 10);
if (!trimmedName) {
errors.name = "Profile name is required.";
} else if (
connections.some((connection) => connection.name.toLowerCase() === trimmedName.toLowerCase())
) {
errors.name = "Profile name must stay unique inside the visible shell.";
}
if (!trimmedHost) {
errors.host = "Host is required for a connection draft.";
}
if (!Number.isFinite(port) || port < 1 || port > 65535) {
errors.port = "Port must be a number between 1 and 65535.";
}
if (!Number.isFinite(database) || database < 0) {
errors.database = "Default DB must be a non-negative integer.";
}
return errors;
}
function formatRedisResponse(response: RedisResponse) {
const normalized = normalizeRedisResponse(response);
return typeof normalized === "string"
? normalized
: JSON.stringify(normalized, null, 2);
}
function normalizeRedisResponse(response: RedisResponse): unknown {
switch (response.kind) {
case "null":
return null;
case "integer":
return response.value;
case "string":
return response.value;
case "binary":
return `[binary ${response.bytes.length} bytes]`;
case "array":
return response.items.map(normalizeRedisResponse);
default:
return response;
}
}
function toRedisSearchPattern(value: string) {
const trimmed = value.trim();
if (!trimmed) {
return null;
}
if (/[*?\[\]]/.test(trimmed)) {
return trimmed;
}
return `*${trimmed}*`;
}
function formatRedisKeyTtl(ttl: RedisKeyTtl) {
switch (ttl.kind) {
case "persistent":
return "persistent";
case "missing":
return "missing";
case "expires_in_millis":
return formatDurationFromMillis(ttl.value);
default:
return "unknown";
}
}
function formatDurationFromMillis(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`;
}
function toLiveKeyRecordFromMetadata(metadata: RedisKeyMetadata): KeyRecord {
return {
id: metadata.name,
name: metadata.name,
type: metadata.key_type,
ttl: formatRedisKeyTtl(metadata.ttl),
preview: buildMetadataPreview(metadata),
editable: false,
value: "",
summary: buildMetadataSummary(metadata),
};
}
function toLiveKeyRecordFromValue(record: RedisValueRecord): KeyRecord {
return {
id: record.metadata.name,
name: record.metadata.name,
type: record.metadata.key_type,
ttl: formatRedisKeyTtl(record.metadata.ttl),
preview: buildValuePreview(record),
editable: record.write_capability === "replace_string",
value: formatRedisValueData(record.data),
summary: buildLiveSupportNote(record, null),
};
}
function buildMetadataPreview(metadata: RedisKeyMetadata) {
return `${formatRedisKeyTypeLabel(metadata.key_type)} key · TTL ${formatRedisKeyTtl(metadata.ttl)}`;
}
function buildMetadataSummary(metadata: RedisKeyMetadata) {
if (!metadata.exists) {
return "The selected key disappeared before the inspector could read its value.";
}
return `${formatRedisKeyTypeLabel(metadata.key_type)} metadata is available. Open the inspector to load the live value surface.`;
}
function buildValuePreview(record: RedisValueRecord) {
switch (record.data.kind) {
case "missing":
return "key disappeared before value read";
case "string":
return truncateText(formatRedisValueContent(record.data.value), 48);
case "hash":
return `${record.data.entries.length} field${record.data.entries.length === 1 ? "" : "s"}`;
case "list":
return `${record.data.items.length} list item${record.data.items.length === 1 ? "" : "s"}`;
case "set":
return `${record.data.members.length} set member${record.data.members.length === 1 ? "" : "s"}`;
case "sorted_set":
return `${record.data.entries.length} scored member${record.data.entries.length === 1 ? "" : "s"}`;
case "stream":
return `${record.data.entries.length} stream entr${record.data.entries.length === 1 ? "y" : "ies"}`;
default:
return "live value loaded";
}
}
function buildLiveSupportNote(record: RedisValueRecord | null, selectedKey: KeyRecord | null) {
if (!selectedKey) {
return "Search currently returns zero keys in the active DB. This is an intentional empty state, not a backend failure.";
}
if (!record) {
return "Loading live metadata and value surface for the current key.";
}
switch (record.data.kind) {
case "missing":
return "The key disappeared between browse and read. Refresh the browser list to clear stale state safely.";
case "string":
return record.write_capability === "replace_string"
? "String values are editable through the current desktop contract and preserve their existing TTL on save."
: "String value is currently inspection only in the desktop contract.";
case "hash":
case "list":
case "set":
case "sorted_set":
case "stream":
return `${formatRedisKeyTypeLabel(record.metadata.key_type)} values are loaded live into structured read-only panels in the current desktop contract.`;
default:
return "The current key is available for live inspection through the desktop backend contract.";
}
}
function formatInspectorValue(
record: RedisValueRecord | null,
selectedKey: KeyRecord | null,
isLoading: boolean,
) {
if (!selectedKey) {
return "";
}
if (isLoading || !record) {
return "Loading live value...";
}
return formatRedisValueData(record.data);
}
function formatRedisValueData(data: RedisValueData) {
switch (data.kind) {
case "missing":
return "Key no longer exists.";
case "string":
return formatRedisValueContent(data.value);
case "hash":
return JSON.stringify(
data.entries.map((entry) => ({
field: normalizeRedisValueContent(entry.field),
value: normalizeRedisValueContent(entry.value),
})),
null,
2,
);
case "list":
return JSON.stringify(data.items.map(normalizeRedisValueContent), null, 2);
case "set":
return JSON.stringify(data.members.map(normalizeRedisValueContent), null, 2);
case "sorted_set":
return JSON.stringify(
data.entries.map((entry) => ({
member: normalizeRedisValueContent(entry.member),
score: entry.score,
})),
null,
2,
);
case "stream":
return JSON.stringify(
data.entries.map((entry) => ({
id: entry.id,
fields: entry.fields.map((field) => ({
field: normalizeRedisValueContent(field.field),
value: normalizeRedisValueContent(field.value),
})),
})),
null,
2,
);
default:
return "Unsupported value payload.";
}
}
function formatRedisValueContent(content: RedisValueContent) {
switch (content.kind) {
case "string":
return content.value;
case "binary":
return `[binary ${content.bytes.length} bytes]`;
default:
return "unsupported";
}
}
function normalizeRedisValueContent(content: RedisValueContent) {
switch (content.kind) {
case "string":
return content.value;
case "binary":
return `[binary ${content.bytes.length} bytes]`;
default:
return content;
}
}
function formatRedisKeyTypeLabel(value: string) {
switch (value) {
case "zset":
return "sorted set";
default:
return value || "unknown";
}
}
function truncateText(value: string, maxLength: number) {
if (value.length <= maxLength) {
return value;
}
return `${value.slice(0, maxLength - 1)}`;
}
function validateTtlDraft(value: string) {
const trimmed = value.trim();
const parsed = Number.parseInt(trimmed, 10);
if (!trimmed) {
return "TTL must be provided in milliseconds.";
}
if (!Number.isFinite(parsed) || parsed <= 0) {
return "TTL must be a positive integer number of milliseconds.";
}
return null;
}
export default App;