Files
yaak-mountain-loop/apps/yaak-proxy/lib/atoms.ts
T
Gregory SchierandClaude Opus 5.5 c2d4c39c62 Apply Yaak Proxy's filter on Enter, with places, Back and Forward, and filter history
The search box now composes a filter and Enter applies it. The main window's
view (filter, selected flow, card tabs) is a place: a session stack of them
backs Back and Forward (⌘[ ⌘], mouse buttons, a Go menu), the current one
survives a reload, and the last filter comes back at launch. Filters applied
with Enter go into a new filter_history table, listed from the magnifier and
recalled with Up in the empty box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:33:18 -07:00

66 lines
2.9 KiB
TypeScript

import type { Flow } from "@yaakapp-internal/proxy-lib";
import type { SplitLayoutLayout } from "@yaakapp-internal/ui";
import { atom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { currentPlace, EMPTY_PLACE, type PlaceStack, stackOf, updatePlace } from "./places";
/** The sidebar's tree, whose selection narrows the list */
export const SIDEBAR_TREE_ID = "proxy-sidebar";
/** The selected flow as last seen, kept once the window has dropped it so the detail stays */
export const selectedFlowAtom = atom<Flow | null>(null);
export const selectedFlowIdAtom = atom((get) => get(selectedFlowAtom)?.id ?? null);
/** The places Back and Forward walk, the current one what the window shows */
export const placeStackAtom = atom<PlaceStack>(stackOf(EMPTY_PLACE));
export const placeAtom = atom((get) => currentPlace(get(placeStackAtom)));
/** The filter the list, its counts and the sidebar follow, changed only by applying one */
export const appliedFilterAtom = atom((get) => get(placeAtom).filter);
/** The flows selected at the current place */
export const placeSelectionAtom = atom((get) => get(placeAtom).selection);
/** The tab each half of the card has open at the current place */
export const placeTabsAtom = atom((get) => get(placeAtom).tabs);
export const canGoBackAtom = atom((get) => get(placeStackAtom).index > 0);
export const canGoForwardAtom = atom((get) => {
const stack = get(placeStackAtom);
return stack.index < stack.places.length - 1;
});
/**
* Lets go of flows that are gone, deleted or cleared: the detail empties and the current place
* no longer selects them, without a place of its own. All of them without `ids`.
*/
export const dropSelectedAtom = atom(null, (get, set, ids?: ReadonlySet<string>) => {
const selected = get(selectedFlowAtom);
if (selected != null && (ids == null || ids.has(selected.id))) set(selectedFlowAtom, null);
set(placeStackAtom, (stack) => {
const place = currentPlace(stack);
const kept = [...place.selection].filter((id) => ids != null && !ids.has(id));
if (kept.length === place.selection.size) return stack;
const tabs = kept.length === 0 ? {} : place.tabs;
return updatePlace(stack, { ...place, selection: new Set(kept), tabs });
});
});
/** A flow to scroll into view once the list has it, as going back to a place asks */
export const revealFlowAtom = atom<{ id: string } | null>(null);
/** The list follows new traffic, rather than being held where it was scrolled or selected */
export const followingAtom = atom(true);
/** When the helper was last registered, so its state is read until approval shows */
export const helperRegisteredAtAtom = atom<number | null>(null);
/** The dialog that walks through allowing the helper in System Settings */
export const helperDialogOpenAtom = atom(false);
/** Stacked by default; the header's toggle switches to side by side */
export const flowLayoutAtom = atomWithStorage<SplitLayoutLayout>("flow_layout", "vertical");