mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
Columns keep fixed widths, Path included, and the list scrolls sideways with the header in step instead of dropping columns or capping Host. Resize handles sit on every column boundary, a double-click resets one, and a third click on a sorted header goes back to start time, with empty values sorting lowest. The search box takes up to 44rem and wraps over the list while focused, its completions as wide as it. The list/detail handle fills the gutter. Native title tooltips become Yaak's tooltip, shared buttons gain an opt-in for it, and folder and program rows in the sidebar get Reveal in Finder and Copy path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
import { formatSize } from "@yaakapp-internal/lib/formatSize";
|
|
import type { Flow } from "@yaakapp-internal/proxy-lib";
|
|
import { Button, CONTROL_TOOLTIP_DELAY, formatMillis, Icon, Tooltip } from "@yaakapp-internal/ui";
|
|
import { useDecryptList, useEditDecryptList } from "../hooks/useDecryptList";
|
|
import { type DecryptSubject, detailActions, listEdit, setDecrypted } from "../lib/decrypt";
|
|
import { sidebarPath } from "../lib/search";
|
|
import { EmptyState } from "./FlowBodyViewer";
|
|
|
|
/**
|
|
* The detail of HTTPS passed through undecrypted: its sizes and time, and buttons that add its
|
|
* host or its app to the decrypt list, from the next connection on.
|
|
*/
|
|
export function NotDecrypted({ flow }: { flow: Flow }) {
|
|
const list = useDecryptList();
|
|
const save = useEditDecryptList();
|
|
const host = sidebarPath(flow.url).split("/")[0] ?? flow.url;
|
|
const hostSubject: DecryptSubject = { kind: "host", host };
|
|
const appSubject: DecryptSubject | null =
|
|
flow.clientName == null
|
|
? null
|
|
: { kind: "app", name: flow.clientName, bundleId: flow.clientBundleId };
|
|
const app = flow.clientName;
|
|
const actions = detailActions(
|
|
list,
|
|
host,
|
|
app == null ? null : { name: app, bundleId: flow.clientBundleId },
|
|
);
|
|
const open = flow.elapsedMs == null && flow.error == null;
|
|
|
|
const decrypt = (subject: DecryptSubject) =>
|
|
save.mutate(listEdit(list, setDecrypted(list, [subject], true)));
|
|
|
|
const facts = [
|
|
`${formatSize(Number(flow.requestSize))} sent`,
|
|
`${formatSize(Number(flow.responseSize))} received`,
|
|
open ? "open" : flow.elapsedMs == null ? null : formatMillis(flow.elapsedMs),
|
|
].filter((f) => f != null);
|
|
|
|
return (
|
|
<EmptyState>
|
|
<div className="grid justify-items-center gap-2">
|
|
<div className="flex items-center gap-1.5 text-text-subtle">
|
|
<Icon icon="lock" size="sm" />
|
|
Not decrypted
|
|
</div>
|
|
<div className="font-mono text-xs">{facts.join(" · ")}</div>
|
|
{flow.error != null && <div className="text-danger">{flow.error}</div>}
|
|
{actions.listed ? (
|
|
<Tooltip
|
|
wrap
|
|
content="Still tunnelled? The app may pin its certificate"
|
|
delay={CONTROL_TOOLTIP_DELAY}
|
|
>
|
|
<div>Decrypting new connections</div>
|
|
</Tooltip>
|
|
) : actions.blocked != null ? (
|
|
<Tooltip
|
|
wrap
|
|
content="Remove it from the decrypt list to decrypt"
|
|
delay={CONTROL_TOOLTIP_DELAY}
|
|
>
|
|
<div>
|
|
Excluded by <span className="font-mono text-xs">{actions.blocked}</span>
|
|
</div>
|
|
</Tooltip>
|
|
) : (
|
|
<div className="flex gap-2 mt-1">
|
|
{actions.host && (
|
|
<Button
|
|
size="xs"
|
|
variant="border"
|
|
title={`Decrypt new connections to ${host}`}
|
|
tooltip
|
|
disabled={save.isPending}
|
|
onClick={() => decrypt(hostSubject)}
|
|
>
|
|
Decrypt Host
|
|
</Button>
|
|
)}
|
|
{actions.app && appSubject != null && (
|
|
<Button
|
|
size="xs"
|
|
variant="border"
|
|
title={`Decrypt new connections from ${app}`}
|
|
tooltip
|
|
disabled={save.isPending}
|
|
onClick={() => decrypt(appSubject)}
|
|
>
|
|
Decrypt App
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</EmptyState>
|
|
);
|
|
}
|