Files
yaak-mountain-loop/apps/yaak-proxy/components/AppIcon.tsx
T
Gregory SchierandClaude Opus 5.5 c0f75bcfc2 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>
2026-11-10 22:30:40 -07:00

55 lines
1.6 KiB
TypeScript

import classNames from "classnames";
import { useEffect, useState } from "react";
import { rpc } from "../lib/rpc";
/**
* Icons by app path, as data URLs once loaded, for the whole session. Plain rather than a query
* per icon, since every new row in the list shows one.
*/
const icons = new Map<string, string | null>();
const loading = new Map<string, Promise<string | null>>();
function loadIcon(path: string): Promise<string | null> {
let promise = loading.get(path);
if (promise == null) {
promise = rpc("app_icon", { path })
.then((png) => (png == null ? null : `data:image/png;base64,${png}`))
.catch(() => null)
.then((url) => {
icons.set(path, url);
return url;
});
loading.set(path, promise);
}
return promise;
}
/** An app's icon, read once per path for the whole session; `className` sets its size */
export function AppIcon({ path, className = "size-4" }: { path: string; className?: string }) {
const [url, setUrl] = useState(() => icons.get(path));
useEffect(() => {
if (icons.has(path)) {
setUrl(icons.get(path));
return;
}
let current = true;
void loadIcon(path).then((loaded) => {
if (current) setUrl(loaded);
});
return () => {
current = false;
};
}, [path]);
return url == null ? (
<span aria-hidden className={classNames(className, "inline-block shrink-0 aspect-square")} />
) : (
// Square whatever squeezes it, beside text that truncates
<img
alt=""
src={url}
className={classNames(className, "shrink-0 aspect-square object-contain")}
/>
);
}