diff --git a/AGENTS.md b/AGENTS.md index 05238338a22cf..f71d37cf2be39 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -276,6 +276,7 @@ NeoCode is a distinct product built from upstream T3 Code (`pingdotgg/t3code`), | `apps/web/src/components/{desktop/DesktopAppActivationCoordinator,desktop/SshPasswordPromptDialog,onboarding/FirstRunGate,onboarding/WelcomeWizard,preview/PreviewPanel,cloud/RelayClientInstallDialog}.tsx`, `apps/web/src/components/{desktopUpdate.logic,desktopUpdate.logic.test}.ts` | NeoCode onboarding, connection, preview and update guidance; show NeoPi readiness first without changing enabled choices or initiating authentication; retain activation, recovery and update behavior | | `apps/web/src/components/clerk/{MobileClientsUserProfilePage.logic,MobileClientsUserProfilePage.logic.test}.ts`, `apps/web/src/components/clerk/MobileClientsUserProfilePage.tsx`, `apps/web/src/connection/{clientMetadata,clientMetadata.test}.ts`, `apps/web/src/{cloud/linkEnvironment,desktopAppActivation}.ts` | NeoCode client and authorization display metadata; retain protocol fields, connection behavior and metadata assertions | | `apps/web/src/components/settings/{BrowserImportWizard,CaptureShortcutConfig,ConnectionsSettings,IntegrationsSettings,KeybindingsSettings,LocalEnvironmentSetting,NotificationSettings,SettingsPanels,SnapShotSettings,SnapShotSetupDialog,ThemeImportDialog,ThemePreviewCircles}.tsx`, `apps/web/src/components/settings/providerStatus.ts`, `apps/web/src/routes/{_chat.index,_chat.pull-requests,_chat}.tsx` | Reuse web branding in settings, navigation, capture and provider guidance without changing entry points or saved choices | +| `apps/web/src/components/settings/FontFamilyPicker.tsx` | Classify installed monospace fonts only for an opened picker, in cancellable batches, instead of blocking Appearance on mount; the cache and scheduler live in fork-owned `installedMonospaceFonts.ts` | | `packages/client-runtime/src/connection/compatibility.ts`, `packages/client-runtime/src/state/{pullRequestDiffHttp,pullRequestDiffHttp.test}.ts`, `packages/contracts/src/{browserImport,previewAutomation,pullRequest}.ts` | Neutral cross-client compatibility, rejected-session recovery and permission guidance; preserve schemas and protocol identities | | `README.md`, `docs/user/install.md` | Identify NeoCode and upstream attribution; distinguish fork releases from upstream installers; explain NeoPi executable selection, retained capability diagnostics, per-instance discovery failures and Linux URL registration | | `docs/operations/development.md` | Distinguish rebuilt source output from installed artifacts; document complete Linux source URL commands and their inherited-environment handling | diff --git a/apps/web/src/components/settings/FontFamilyPicker.tsx b/apps/web/src/components/settings/FontFamilyPicker.tsx index b7e43eef82092..344ba3b2647da 100644 --- a/apps/web/src/components/settings/FontFamilyPicker.tsx +++ b/apps/web/src/components/settings/FontFamilyPicker.tsx @@ -1,7 +1,8 @@ import { LegendList, type LegendListRef } from "@legendapp/list/react"; import { CheckIcon } from "lucide-react"; import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; -import { isMonospaceFamily, queryInstalledFontFamilies } from "../../appearanceFonts"; +import { queryInstalledFontFamilies } from "../../appearanceFonts"; +import { filterInstalledMonospaceFonts } from "../../installedMonospaceFonts"; import { Combobox, ComboboxEmpty, @@ -14,6 +15,7 @@ import { import { SelectButton } from "../ui/select"; const DEFAULT_FONT_VALUE = "__default__"; +const EMPTY_FONT_FAMILIES: readonly string[] = []; function supportsFontEnumeration(): boolean { return ( @@ -139,10 +141,35 @@ export function FontFamilyPicker({ if (nextOpen) setQuery(""); }; - const families = useMemo(() => { - if (enumeration.status !== "granted") return []; - return requireMonospace ? enumeration.families.filter(isMonospaceFamily) : enumeration.families; - }, [enumeration, requireMonospace]); + const [monospaceResult, setMonospaceResult] = useState<{ + source: readonly string[]; + families: readonly string[]; + } | null>(null); + useEffect(() => { + // A closed picker must not measure every installed face during the + // Appearance mount. Stop scanning when it closes or the page unmounts. + if (!open || !requireMonospace || enumeration.status !== "granted") return; + const source = enumeration.families; + if (monospaceResult?.source === source) return; + const controller = new AbortController(); + void filterInstalledMonospaceFonts(source, controller.signal).then((families) => { + if (!controller.signal.aborted) setMonospaceResult({ source, families }); + }); + return () => controller.abort(); + }, [open, requireMonospace, enumeration, monospaceResult]); + + const loadingMonospace = + requireMonospace && + enumeration.status === "granted" && + monospaceResult?.source !== enumeration.families; + const families = + enumeration.status !== "granted" + ? EMPTY_FONT_FAMILIES + : requireMonospace + ? monospaceResult?.source === enumeration.families + ? monospaceResult.families + : EMPTY_FONT_FAMILIES + : enumeration.families; const items = useMemo(() => { const trimmedQuery = query.trim().toLowerCase(); @@ -212,7 +239,13 @@ export function FontFamilyPicker({ onChange={(event) => setQuery(event.target.value)} />