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]); }, [path]);
return url == null ? ( 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" ? ( typeof part === "string" ? (
// oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line // oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line
<span key={i}>{part}</span> <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 // oxlint-disable-next-line react/no-array-index-key -- the parts of one fixed line
<InlineCode key={i}>{part.code}</InlineCode> <InlineCode key={i}>{part.code}</InlineCode>
@@ -222,7 +227,7 @@ function ActionButton({
flow.clientPath == null ? ( flow.clientPath == null ? (
<Icon icon="box" size="sm" /> <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" /> <Icon icon={ICONS[action.icon]} size="sm" />
@@ -241,7 +246,7 @@ function ActionButton({
action.hint == null ? undefined : ( action.hint == null ? undefined : (
<span <span
className={classNames( 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", action.removes && "line-through",
)} )}
> >
+3 -3
View File
@@ -77,9 +77,9 @@ export function HttpsStrip() {
app.path == null ? ( app.path == null ? (
app.name app.name
) : ( ) : (
<span className="flex items-center gap-2"> <span className="flex items-center gap-2 min-w-0">
<AppIcon path={app.path} className="size-4" /> <AppIcon path={app.path} className="size-4 min-w-4" />
{app.name} <span className="truncate">{app.name}</span>
</span> </span>
), ),
keepOpenOnSelect: true, 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 === "encrypted" && <Icon icon="lock" size="sm" className="text-text-subtle" />}
{item.kind === "saved" && <Icon icon="bookmark" 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.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 ? ( {item.kind === "folder" && item.path != null ? (
// Named by its last part; the whole path on hover // Named by its last part; the whole path on hover
<Tooltip wrap content={item.path} delay={CONTROL_TOOLTIP_DELAY}> <Tooltip wrap content={item.path} delay={CONTROL_TOOLTIP_DELAY}>
+7
View File
@@ -40,6 +40,12 @@ const apps = {
clientBundleId: "com.1password.1password", clientBundleId: "com.1password.1password",
clientPath: "/Applications/1Password.app", 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: { acme: {
clientName: "Acme Sync", clientName: "Acme Sync",
clientBundleId: "dev.acme.sync", clientBundleId: "dev.acme.sync",
@@ -124,6 +130,7 @@ function encryptedScene(): Flow[] {
tunnel(10, "fonts.gstatic.com", apps.dia), tunnel(10, "fonts.gstatic.com", apps.dia),
tunnel(11, "clients2.google.com", apps.chrome), tunnel(11, "clients2.google.com", apps.chrome),
tunnel(12, "home.taila714c.ts.net:8317", apps.dia, { url: "home.taila714c.ts.net:8317" }), 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", () => { test("something else listed: not on the list, with the same choices", () => {
const c = card({ list: [...user("host:api.stripe.com"), ...BUILT_INS] }); 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([ expect(c.actions.map((a) => a.label)).toEqual([
"Decrypt www.google.com", "Decrypt www.google.com",
"Decrypt Dia", "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", () => { test("names the host nothing on the list covers, its port only when not 443", () => {
const list = [...user("host:api.stripe.com"), ...BUILT_INS]; const list = [...user("host:api.stripe.com"), ...BUILT_INS];
expect(lineText(card({ list, flow: { ...tunnel, url: "t.yaak.app:443" } }).why)).toBe( 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( 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"; 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 { 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 { export interface EncryptedCardModel {
@@ -265,7 +272,11 @@ function whyLine(
? ["Nothing is decrypted yet"] ? ["Nothing is decrypted yet"]
: decryptsNow : decryptsNow
? ["Not on the decrypt list"] ? ["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, whySub: null,
}; };
} }