/** * Parse filters from URL search params. * * Returns two sets of filter criteria: * - `filters`: View modes (highlights) and message kinds (user_message, tool_call, etc.) * - `toolTypes`: Specific tool type sub-filters (read, edit, grep, etc.) */// types.tstype ViewMode = "all" | "highlights";
type MessageKind = | "user_message" | "agent_text" | "agent_thinking" | "tool_call";
type ToolType = | "read" | "edit" | "write" | "delete" | "grep" | "glob" | "terminal_command" // ... etc (could derive from MESSAGE_TYPES where category === "tool_type")parseFiltersFromUrl(searchParams): { viewMode: ViewMode; // single value, defaults to "all" messageKinds: MessageKind[]; // which kinds to show (empty = all) toolTypes: ToolType[]; // sub-filters under tool_call}parseFiltersFromUrl(searchParams): { viewMode: ViewMode; // replaces normalizeFilter messageKinds: MessageKind[]; toolTypes: ToolType[]; }This comparison appears to be unintentional because the types 'MessageKind' and '"tool_result"' have no overlap.// Multi-select: "Show user messages AND tool calls, filtered to read/edit"buildFilterUrl(["user_message", "tool_call"], ["read", "edit"], params)// → "?filter=user_message&filter=tool_call&toolType=read&toolType=edit"
// Single mode: "Switch to highlights view"createFilterModeUrl("highlights", "/s/123", params)// → "/s/123?filter=highlights"/** * Build a filter URL from the given state. * * @param state - Filter state (viewMode, messageKinds, toolTypes) * @param pathname - If provided, returns full path; otherwise just query string * @param baseParams - Existing URL params to preserve */export function buildFilterUrl( state: { viewMode?: "all" | "highlights"; messageKinds?: MessageKind[]; toolTypes?: string[]; }, baseParams: URLSearchParams, pathname?: string): string { const params = new URLSearchParams(baseParams.toString()); params.delete("filter"); params.delete("toolType");
const { viewMode, messageKinds = [], toolTypes = [] } = state;
// "all" = no filter param (default) // "highlights" = single filter param // messageKinds = multiple filter params if (viewMode === "highlights") { params.set("filter", "highlights"); } else if (messageKinds.length > 0) { messageKinds.forEach((kind) => params.append("filter", kind)); toolTypes.forEach((type) => params.append("toolType", type)); }
const query = params.toString(); const queryString = query ? `?${query}` : ""; return pathname ? `${pathname}${queryString}` : queryString;}// Switch to highlightsbuildFilterUrl({ viewMode: "highlights" }, params, pathname)
// Multi-select message kindsbuildFilterUrl({ messageKinds: ["user_message", "tool_call"], toolTypes: ["read"] }, params, pathname)
// Clear all (show everything)buildFilterUrl({ viewMode: "all" }, params, pathname)export function buildFilterUrl( filters: MessageFilter[], toolTypes: string[], baseParams: URLSearchParams, pathname?: string): stringexport function parseFiltersFromUrl(searchParams: URLSearchParams): { filters: MessageFilter[]; toolTypes: string[];} { const rawFilters = searchParams.getAll("filter"); const filters = rawFilters .map((f) => f.toLowerCase()) .filter( (f): f is MessageFilter => VALID_FILTERS.has(f as MessageFilter) && f !== "all" );
const toolTypes = searchParams.getAll("toolType");
return { filters, toolTypes };}export type MessageKind = Exclude<MessageFilter, "all" | "highlights">; const messageKinds = rawFilters .map((f) => f.toLowerCase()) .filter( (f): f is MessageFilter => VALID_FILTERS.has(f as MessageFilter) && f !== "all" );