Files
yaak-mountain-loop/apps/yaak-proxy/lib/defaultTab.ts
T
Gregory SchierandClaude Opus 5.5 b0425069f4 Yaak Proxy: HTTPS decryption UI, and Info and Body in the card's tabs
The strip, quiet encrypted rows, warning rows for refused handshakes, the
encrypted card with named actions, trust at the first Decrypt, the toast
with Undo and Reconnect, the pill's Decrypt everything switch, Settings'
Decryption section, the Encrypted sidebar row, and two-step setup. Both
body tabs read Body with a type badge; Info leads the request half with
the timeline folded in.

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

99 lines
3.2 KiB
TypeScript

import type { Flow } from "@yaakapp-internal/proxy-lib";
/** The request half's tabs, named as the shared request pane names them, Info first */
export const REQUEST_TAB_INFO = "info";
export const REQUEST_TAB_BODY = "body";
export const REQUEST_TAB_PARAMS = "params";
export const REQUEST_TAB_HEADERS = "headers";
export const REQUEST_TAB_AUTH = "auth";
export const REQUEST_TAB_COOKIES = "cookies";
/** The response half's tabs, named as the shared response pane names them */
export const RESPONSE_TAB_BODY = "body";
export const RESPONSE_TAB_HEADERS = "headers";
export const RESPONSE_TAB_COOKIES = "cookies";
export const RESPONSE_TAB_MESSAGES = "messages";
export type FlowPane = "request" | "response";
/** What a flow has, which picks the tab each half opens on */
export type FlowShape = "response_body" | "request_body" | "neither" | "stream" | "no_response";
export function flowShape(
flow: Pick<
Flow,
"streamKind" | "status" | "error" | "cancelled" | "requestSize" | "responseSize"
>,
): FlowShape {
if (flow.streamKind != null) return "stream";
if (flow.status == null && (flow.error != null || flow.cancelled)) return "no_response";
if (flow.responseSize > 0) return "response_body";
if (flow.requestSize > 0) return "request_body";
return "neither";
}
/**
* The tab a half opens on for a shape, before anything was learned: the request half on its
* body when it has one, else on Info; the response half on what it has
*/
export function shapeDefaultTab(pane: FlowPane, shape: FlowShape, hasRequestBody: boolean): string {
if (pane === "request") {
return hasRequestBody ? REQUEST_TAB_BODY : REQUEST_TAB_INFO;
}
switch (shape) {
case "response_body":
return RESPONSE_TAB_BODY;
case "stream":
return RESPONSE_TAB_MESSAGES;
case "no_response":
case "request_body":
case "neither":
return RESPONSE_TAB_HEADERS;
}
}
/** Where the tab last chosen by hand for a shape is kept */
export function learnedTabKey(pane: FlowPane, shape: FlowShape): string {
return `proxy_learned_tab.${pane}.${shape}`;
}
/** Where learned tabs are read from and written to: local storage in the app */
export interface TabMemory {
getItem: (key: string) => string | null;
setItem: (key: string, value: string) => void;
}
/**
* The tab a half opens on for a flow with no tab of its own: the one last chosen by hand for
* flows of its shape, when this flow has that tab, else the shape's default.
*/
export function defaultTabFor(
pane: FlowPane,
flow: Parameters<typeof flowShape>[0],
available: string[],
memory: TabMemory | null,
): string {
const shape = flowShape(flow);
const learned = memory?.getItem(learnedTabKey(pane, shape)) ?? null;
if (learned != null && available.includes(learned)) return learned;
return shapeDefaultTab(pane, shape, flow.requestSize > 0);
}
/** A tab chosen by hand becomes the default for flows of the same shape */
export function learnTab(
pane: FlowPane,
flow: Parameters<typeof flowShape>[0],
tab: string,
memory: TabMemory | null,
) {
memory?.setItem(learnedTabKey(pane, flowShape(flow)), tab);
}
export function browserTabMemory(): TabMemory | null {
try {
return window.localStorage;
} catch {
return null;
}
}