// ---------------------------------------------------------------------------// Preset icon — a preset's `icon` is usually a plain image URL. A preset whose// icon can only come from the local server (e.g. a Codex plugin's own icon,// read off this machine at runtime) uses the `executor:` scheme instead: the// path after the scheme is fetched from the executor API with the server auth// header and must answer `{ icon: string | null }` (a data URI). The// indirection exists because local auth is deliberately bearer-header-only —// an <img> cannot authenticate itself.// ---------------------------------------------------------------------------import { useEffect, useState } from "react";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import * as Schema from "effect/Schema";import { getExecutorApiBaseUrl, getExecutorServerAuthorizationHeader,} from "../api/server-connection";export const EXECUTOR_ICON_SCHEME = "executor:";const IconResponse = Schema.Struct({ icon: Schema.NullOr(Schema.String) });const decodeIconResponse = Schema.decodeUnknownOption(IconResponse);const resolved = new Map<string, Promise<string | null>>();/** Resolve an `executor:`-scheme icon path to its data URI (or null). Shared * by every surface that renders preset icons, including IntegrationFavicon's * cascade. Results are memoized per path for the session; any failure is * "no icon", never an error. */export const resolveExecutorIcon = (path: string): Promise<string | null> => { const cached = resolved.get(path); if (cached) return cached; const authorization = getExecutorServerAuthorizationHeader(); const request = Effect.runPromise( Effect.tryPromise(async () => { const response = await fetch(`${getExecutorApiBaseUrl()}${path}`, { headers: authorization === null ? {} : { authorization }, }); if (!response.ok) return null; const body: unknown = await response.json(); return Option.match(decodeIconResponse(body), { onNone: () => null, onSome: ({ icon }) => icon, }); }).pipe(Effect.orElseSucceed(() => null)), ); resolved.set(path, request); return request;};/** Renders a preset icon, resolving `executor:` scheme icons through the * authenticated API. * * `fallbackSrc` is a plain image URL to use when the machine-local icon is * unavailable — a Codex plugin card still shows its provider's mark on a * machine where Codex is not installed, which is exactly when the card is * most in need of explaining itself. `fallback` is the last resort, for when * there is no image of any kind. */export function PresetIcon(props: { readonly icon?: string; readonly fallbackSrc?: string; readonly className?: string; readonly fallback?: React.ReactNode;}) { const isExecutorIcon = props.icon?.startsWith(EXECUTOR_ICON_SCHEME) ?? false; const [fetched, setFetched] = useState<string | null>(null); useEffect(() => { if (!isExecutorIcon || props.icon === undefined) return; let live = true; void resolveExecutorIcon(props.icon.slice(EXECUTOR_ICON_SCHEME.length)).then((icon) => { if (live) setFetched(icon); }); return () => { live = false; }; }, [isExecutorIcon, props.icon]); const src = (isExecutorIcon ? fetched : (props.icon ?? null)) ?? props.fallbackSrc ?? null; if (src === null) return <>{props.fallback ?? null}</>; return <img src={src} alt="" className={props.className} loading="lazy" />;}
// ---------------------------------------------------------------------------
// Preset icon — a preset's `icon` is usually a plain image URL. A preset whose
// icon can only come from the local server (e.g. a Codex plugin's own icon,
// read off this machine at runtime) uses the `executor:` scheme instead: the
// path after the scheme is fetched from the executor API with the server auth
// header and must answer `{ icon: string | null }` (a data URI). The
// indirection exists because local auth is deliberately bearer-header-only —
// an <img> cannot authenticate itself.
// ---------------------------------------------------------------------------
import { useEffect, useState } from "react";
import * as Effect from "effect/Effect";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import {
getExecutorApiBaseUrl,
getExecutorServerAuthorizationHeader,
} from "../api/server-connection";
export const EXECUTOR_ICON_SCHEME = "executor:";
const IconResponse = Schema.Struct({ icon: Schema.NullOr(Schema.String) });
const decodeIconResponse = Schema.decodeUnknownOption(IconResponse);
const resolved = new Map<string, Promise<string | null>>();
/** Resolve an `executor:`-scheme icon path to its data URI (or null). Shared
* by every surface that renders preset icons, including IntegrationFavicon's
* cascade. Results are memoized per path for the session; any failure is
* "no icon", never an error. */
export const resolveExecutorIcon = (path: string): Promise<string | null> => {
const cached = resolved.get(path);
if (cached) return cached;
const authorization = getExecutorServerAuthorizationHeader();
const request = Effect.runPromise(
Effect.tryPromise(async () => {
const response = await fetch(`${getExecutorApiBaseUrl()}${path}`, {
headers: authorization === null ? {} : { authorization },
});
if (!response.ok) return null;
const body: unknown = await response.json();
return Option.match(decodeIconResponse(body), {
onNone: () => null,
onSome: ({ icon }) => icon,
});
}).pipe(Effect.orElseSucceed(() => null)),
);
resolved.set(path, request);
return request;
};
/** Renders a preset icon, resolving `executor:` scheme icons through the
* authenticated API.
*
* `fallbackSrc` is a plain image URL to use when the machine-local icon is
* unavailable — a Codex plugin card still shows its provider's mark on a
* machine where Codex is not installed, which is exactly when the card is
* most in need of explaining itself. `fallback` is the last resort, for when
* there is no image of any kind. */
export function PresetIcon(props: {
readonly icon?: string;
readonly fallbackSrc?: string;
readonly className?: string;
readonly fallback?: React.ReactNode;
}) {
const isExecutorIcon = props.icon?.startsWith(EXECUTOR_ICON_SCHEME) ?? false;
const [fetched, setFetched] = useState<string | null>(null);
useEffect(() => {
if (!isExecutorIcon || props.icon === undefined) return;
let live = true;
void resolveExecutorIcon(props.icon.slice(EXECUTOR_ICON_SCHEME.length)).then((icon) => {
if (live) setFetched(icon);
});
return () => {
live = false;
};
}, [isExecutorIcon, props.icon]);
const src = (isExecutorIcon ? fetched : (props.icon ?? null)) ?? props.fallbackSrc ?? null;
if (src === null) return <>{props.fallback ?? null}</>;
return <img src={src} alt="" className={props.className} loading="lazy" />;
}