Yaak Proxy: square app icons beside long names, and the final uncovered why line

App icons keep their square however a long app name squeezes them, in the
card's buttons, the strip's Choose menu and the sidebar, and a long entry
hint truncates instead of the button's label. A connection nothing on the
list covers reads "Encrypted Content: Enable decryption to see requests
to <host>", with the host as code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Gregory SchierandClaude Opus 5.5 authored and Stetta task tk_mwb56agvne2hw21h committed 2026-11-10 22:30:40 -07:00
1 parent e3ff29ee6d
commit c0f75bcfc2
7 files changed
+48 -16

No files matched your search

+7 -2
View File
@@ -42,8 +42,13 @@ export function AppIcon({ path, className = "size-4" }: { path: string; classNam
}, [path]);
return url == null ? (
<span aria-hidden className={classNames(className, "inline-block shrink-0")} />
<span aria-hidden className={classNames(className, "inline-block shrink-0 aspect-square")} />
) : (
<img alt="" src={url} className={classNames(className, "shrink-0")} />
// Square whatever squeezes it, beside text that truncates
<img
alt=""
src={url}
className={classNames(className, "shrink-0 aspect-square object-contain")}
/>
);
}
+7 -2
View File
@@ -185,6 +185,11 @@ function LineText({ line }: { line: Line }) {
typeof part === "string" ? (
// oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line
<span key={i}>{part}</span>
) : "bold" in part ? (
// oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line
<span key={i} className="font-semibold">
{part.bold}
</span>
) : (
// oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line
<InlineCode key={i}>{part.code}</InlineCode>
@@ -222,7 +227,7 @@ function ActionButton({
flow.clientPath == null ? (
<Icon icon="box" size="sm" />
) : (
<AppIcon path={flow.clientPath} className="size-4" />
<AppIcon path={flow.clientPath} className="size-4 min-w-4" />
)
) : (
<Icon icon={ICONS[action.icon]} size="sm" />
@@ -241,7 +246,7 @@ function ActionButton({
action.hint == null ? undefined : (
<span
className={classNames(
"ml-auto pl-3 font-mono text-xs text-text-subtlest truncate max-w-[50%]",
"block ml-auto pl-3 font-mono text-xs text-text-subtlest truncate max-w-[11rem]",
action.removes && "line-through",
)}
>
+3 -3
View File
@@ -77,9 +77,9 @@ export function HttpsStrip() {
app.path == null ? (
app.name
) : (
<span className="flex items-center gap-2">
<AppIcon path={app.path} className="size-4" />
{app.name}
<span className="flex items-center gap-2 min-w-0">
<AppIcon path={app.path} className="size-4 min-w-4" />
<span className="truncate">{app.name}</span>
</span>
),
keepOpenOnSelect: true,
+3 -1
View File
@@ -85,7 +85,9 @@ function ItemInner({ item }: { item: SidebarItem }) {
{item.kind === "encrypted" && <Icon icon="lock" size="sm" className="text-text-subtle" />}
{item.kind === "saved" && <Icon icon="bookmark" size="sm" className="text-text-subtle" />}
{item.kind === "folder" && <Icon icon="folder" size="sm" className="text-text-subtle" />}
{item.appPath != null && <AppIcon path={item.appPath} className="size-[1.125rem]" />}
{item.appPath != null && (
<AppIcon path={item.appPath} className="size-[1.125rem] min-w-[1.125rem]" />
)}
{item.kind === "folder" && item.path != null ? (
// Named by its last part; the whole path on hover
<Tooltip wrap content={item.path} delay={CONTROL_TOOLTIP_DELAY}>
+7
View File
@@ -40,6 +40,12 @@ const apps = {
clientBundleId: "com.1password.1password",
clientPath: "/Applications/1Password.app",
},
headless: {
clientName: "chrome-headless-shell",
clientBundleId: null,
clientPath:
"/Users/you/.cache/ms-playwright/chromium_headless_shell-1208/chrome-headless-shell",
},
acme: {
clientName: "Acme Sync",
clientBundleId: "dev.acme.sync",
@@ -124,6 +130,7 @@ function encryptedScene(): Flow[] {
tunnel(10, "fonts.gstatic.com", apps.dia),
tunnel(11, "clients2.google.com", apps.chrome),
tunnel(12, "home.taila714c.ts.net:8317", apps.dia, { url: "home.taila714c.ts.net:8317" }),
tunnel(13, "optimizationguide-pa.googleapis.com", apps.headless),
];
}
+5 -3
View File
@@ -90,7 +90,9 @@ describe("the card of an encrypted connection", () => {
test("something else listed: not on the list, with the same choices", () => {
const c = card({ list: [...user("host:api.stripe.com"), ...BUILT_INS] });
expect(lineText(c.why)).toBe("Decryption not configured for `www.google.com`");
expect(lineText(c.why)).toBe(
"**Encrypted Content:** Enable decryption to see requests to `www.google.com`",
);
expect(c.actions.map((a) => a.label)).toEqual([
"Decrypt www.google.com",
"Decrypt Dia",
@@ -108,10 +110,10 @@ describe("the card of an encrypted connection", () => {
test("names the host nothing on the list covers, its port only when not 443", () => {
const list = [...user("host:api.stripe.com"), ...BUILT_INS];
expect(lineText(card({ list, flow: { ...tunnel, url: "t.yaak.app:443" } }).why)).toBe(
"Decryption not configured for `t.yaak.app`",
"**Encrypted Content:** Enable decryption to see requests to `t.yaak.app`",
);
expect(lineText(card({ list, flow: { ...tunnel, url: "home.ts.net:8317" } }).why)).toBe(
"Decryption not configured for `home.ts.net:8317`",
"**Encrypted Content:** Enable decryption to see requests to `home.ts.net:8317`",
);
});
+16 -5
View File
@@ -78,12 +78,19 @@ export interface CardAction {
icon: "host" | "app" | "everything" | "lock" | "lock_open" | "refresh" | "shield" | "terminal";
}
/** A line of text, the hosts, apps and entries in it shown as code, as Yaak shows literal values */
export type Line = (string | { code: string })[];
/**
* A line of text, the hosts, apps and entries in it shown as code, as Yaak shows literal values,
* and a lead in bold
*/
export type Line = (string | { code: string } | { bold: string })[];
/** A line as plain text, its code in backticks */
/** A line as plain text, its code in backticks and its bold in asterisks */
export function lineText(line: Line): string {
return line.map((part) => (typeof part === "string" ? part : `\`${part.code}\``)).join("");
return line
.map((part) =>
typeof part === "string" ? part : "code" in part ? `\`${part.code}\`` : `**${part.bold}**`,
)
.join("");
}
export interface EncryptedCardModel {
@@ -265,7 +272,11 @@ function whyLine(
? ["Nothing is decrypted yet"]
: decryptsNow
? ["Not on the decrypt list"]
: ["Decryption not configured for ", { code: c.label }],
: [
{ bold: "Encrypted Content:" },
" Enable decryption to see requests to ",
{ code: c.label },
],
whySub: null,
};
}