Files
orca/src/shared/plugins/plugin-list-entry.ts
T
Neil 51f3c956ed feat(plugins): let plugins ship web fonts for the terminal, editor and interface (#39416)
* feat(plugins): let plugins contribute web fonts to the font pickers

Plugins declare font families under contributes.fonts (WOFF/WOFF2 only).
Install checks each file's signature against its extension and caps file
size, family count and plugin total; main never parses a font. Files are
served over the plugin image scheme only while the plugin is enabled,
typed and nosniff. The renderer registers each family under a name that
carries the plugin key, so a plugin font cannot shadow an installed or
built-in font, and it applies only once the user picks it. Disabling or
uninstalling removes the faces; uninstalling also resets font settings
that named them.

Refs #38607

* fix(plugins): harden plugin fonts and re-measure editors when they load

- Refuse WOFF/WOFF2-wrapped font collections by checking the flavor bytes.
- Check dev folders' font files at discovery, not only at install.
- Re-check family, weight and style from the plugin list in the renderer.
- Track loaded families (case-insensitively) from document font loads, so
  the code editor re-measures and terminals, previews, the SSH terminal and
  the dashboard popout re-apply only when their own font changes.
- Register plugin fonts in the dashboard popout window.
- Give dev-folder font URLs their change count so edits reload.
- Keep monospace fallbacks unless the chosen family is exactly that font.

* fix(terminal): keep every fallback only for plugin font families

System font choices keep their existing fallback list unchanged.

* fix(plugins): leave the dashboard popout's plugin list alone; add the font row string

Registering fonts there started the plugin auto-update watcher in a window with no toaster, which could consume update notices. Adds the new contribution-row string to the English catalog and the fonts list to the RPC test manifest.
2026-11-07 02:08:35 -08:00

139 lines
4.9 KiB
TypeScript

import type { PluginCatalogType } from './catalog/plugin-catalog-types'
import type { PluginExecutionProfile } from './contributions/plugin-execution-profile'
import type { PluginHostImages } from './images/plugin-image-url'
import type { PluginStatusBarItemContribution } from './contributions/plugin-status-bar-item-contribution'
/** One installed plugin as main projects it for every client (preload IPC and the `plugins.list`
* RPC). Optional fields are absent on older hosts, so paired clients must tolerate them. */
export type PluginHostPanel = {
id: string
title: string
/** Lucide icon name declared in the plugin manifest. */
icon?: string
tabKey: `plugin:${string}`
}
/** pending awaits consent; idle defers worker startup; errored exhausted retries or failed startup. */
export type PluginHostStatus =
| 'running'
| 'restarting'
| 'idle'
| 'pending'
| 'disabled'
| 'errored'
| 'invalid'
export type PluginHostCustomEditor = {
id: string
title: string
extensions: string[]
filenames: string[]
priority: 'default' | 'option'
/** Absent on hosts that predate byte editors: text. */
contentKind?: 'text' | 'bytes'
/** Absent on hosts that predate the field: `any`. */
frame?: 'any' | 'isolated'
/** orca-plugin-image URL of the editor's tab icon; absent when it declares none. */
icon?: string
}
export type PluginHostNewFile = {
id: string
title: string
/** Suffix, with the dot, of the untitled file it creates. */
extension: string
/** orca-plugin-image URL; absent when it declares none. */
icon?: string
}
export type PluginHostFont = {
/** The manifest's family name; clients register it as `pluginFontFamilyName`. */
family: string
faces: {
/** orca-plugin-image URL main serves while the plugin is enabled. */
url: string
weight: string
style: 'normal' | 'italic'
}[]
}
export type PluginHostListEntry = {
pluginKey: string
consentFingerprint: string | null
name: string
version: string
publisher: string
description?: string
/** The verified execution profile from the installed manifest; absent for invalid entries and
* on hosts that predate it. Never infer it from the plugin's contents. */
execution?: PluginExecutionProfile
/** What the installed manifest contributes; absent on hosts that predate it. */
types?: readonly PluginCatalogType[]
/** Declared images (format 2 manifests), as orca-plugin-image URLs main serves. */
images?: PluginHostImages
/** SPDX license expression from the manifest. */
license?: string
homepage?: string
repository?: string
status: PluginHostStatus
needsReconsent: boolean
error?: string
isDev: boolean
/** Dev folders: file changes the host had seen when it last loaded the folder, so a client can
* tell a reload happened. Absent on older hosts and installed plugins. */
devChangeCount?: number
official: boolean
bundled: boolean
capabilities: { kind: string; description: string }[]
panels: PluginHostPanel[]
/** Absent on hosts that predate custom editors. */
customEditors?: PluginHostCustomEditor[]
/** Absent on hosts that predate new-file templates. */
newFiles?: PluginHostNewFile[]
/** Absent on hosts that predate plugin fonts. */
fonts?: PluginHostFont[]
commands: {
id: string
title: string
// Why `worktree`: hosts from before the rename to `workspace` still send it.
context: 'global' | 'workspace' | 'worktree'
handler: { type: 'built-in'; action: string } | { type: 'worker' }
keybindings: { key: string; when: 'global' | 'workspace' | 'worktree' }[]
/** The built-in action it runs in place of; absent on older hosts. */
replaces?: string
/** Another plugin, installed earlier, replaces the same action, so this one does not. */
replacedBy?: string
}[]
/** The manifest's status bar items; a worker's changes arrive separately. Absent on older hosts. */
statusBarItems?: PluginStatusBarItemContribution[]
/** The hooks its manifest lists, so a window can skip asking when none applies. Absent on older
* hosts. */
hooks?: string[]
hasWorker: boolean
vmRecipes?: {
id: string
name: string
description?: string
commands: {
phase: 'create' | 'suspend' | 'resume' | 'destroy'
command: string
}[]
}[]
restarts: number
blockedByKillList?: { reason: string; advisoryUrl?: string }
source?: {
kind: 'local-path' | 'git' | 'marketplace' | 'bundled'
reference: string
/** Structured install location; absent on older hosts and non-Git installs. */
url?: string
path?: string
/** The catalog's repository claim for this release; not verified authorship. */
authorRepository?: string
resolvedCommit: string | null
contentHash: string
/** `ref` is the catalog's ref, so a client can join to the exact catalog source. */
marketplace?: { reference: string; ref?: string; resolvedCommit: string }
}
}