Files
Netcatty/docs/plugin-platform/theme-packs.md
T

8.5 KiB

Host color theme packs

Theme packs contribute validated light and dark host colors. They cannot inject CSS, access host DOM, change layout, load remote resources, replace security controls, or apply themselves. The feature is appearance.theme-packs.

Install and select

Use normal Settings > Plugins to install and enable a package. In Appearance, choose a plugin theme for the current light or dark mode, then select Preview or Apply. Preview changes only the current window and writes no preference. Cancel, closing Appearance, changing mode, package withdrawal, or a newer committed revision ends that preview. Apply saves a host-owned, device-local choice and refreshes other application windows. Restore default clears the plugin choice for that mode and selects Snow or Midnight. Selecting a built-in swatch also clears that mode's plugin choice. Failures remain visible and are not reported as successful applications.

The current view environment is globally broadcast. To keep temporary previews local, it publishes committed theme tokens only. Plugin pages follow an applied theme through their existing environment subscription; they do not receive another window's temporary preview. This does not add an owner-local view environment API.

Data-only package

A pure theme uses main: { "data": "theme" }, contributes only themes, and omits permissions, activation events and companions. Its code runtime is never started, including by the runtime supervisor's startup or explicit-start path. Browser code plugins may also declare themes; choosing a theme does not start a runtime or grant their functional permissions. Only enabled packages appear.

{
  "features": { "required": ["appearance.theme-packs"] },
  "main": { "data": "theme" },
  "contributes": {
    "themes": [{
      "id": "com.example.colors.forest",
      "label": "Forest",
      "variant": "light",
      "entry": "forest.json"
    }]
  }
}

This fragment belongs in an otherwise complete manifest. Contribution IDs use the package namespace. Palette paths are package-relative JSON files, included and checked by the normal CLI/package store. Hosts lacking the required feature reject compatibility deterministically.

{ "formatVersion": 1, "base": "light", "colors": { "accent": "#166534" } }

Colors merge onto a fixed accessible light/dark host base. Only opaque #RRGGBB values and canonical semantic keys are accepted. The 15 editable keys cover background/foreground, card and popover pairs, secondary and muted pairs, accent, destructive and its foreground, border and input. Primary, ring and the accent foregrounds are derived together, matching the existing theme conversion. Nineteen output fields are not nineteen independent controls.

Each package has at most 32 themes; each palette is at most 64 KiB; the catalog and validated cache contain at most 256 entries. Schema and contrast checks run before catalog publication. Text pairs and state colors require at least 4.5:1; control borders and focus information require at least 3:1 against their surfaces. Invalid palettes produce bounded, content-free diagnostics and cannot be previewed or applied. Files are read from verified immutable package roots, with bounded reads and a no-follow final file open.

Ownership, recovery and security appearance

The host stores selections separately from plugin KV, settings sidecars and cloud sync. A reference binds plugin ID, theme ID, exact version, archive digest and variant. Application uses a revision check plus an active-package check just before the atomic preference/audit transaction. An audit write failure rolls back the selection. Audit records contain revision/action metadata, not palette or user content.

Disable, uninstall, update, missing entries and integrity failures withdraw the palette. A saved missing reference remains visible as unavailable while the same-mode safe host palette is used; a changed package is not automatically applied. Preview owner and committed revision checks prevent a late cancellation from replacing another preview or a newer application.

Host dialogs, App Lock, grant/revoke, host details, keychain panels, host-key prompts and the credential picker use fixed host security variables while a plugin theme is active. The pack controls no font, icon, opacity, CSS selector, button text or ordering. System forced-colors/high-contrast preferences use a fixed host high-contrast fallback. Personal accent, fonts, terminal choices and accessibility preferences retain their authority. Existing personal custom CSS remains a user setting and is not exposed to plugins.

This first layer changes host semantic colors. Static terminal palette linkage, custom high-contrast variants, fonts, density, icons and backgrounds remain separate bounded follow-ups; their fields are not silently accepted here.

Example and validation

examples/plugins/forest-theme-pack is a four-file data package with light and dark Forest accents. It requests no permissions and contains no JavaScript. The starter, Vault Scratchpad and Session Scratchpad pages now subscribe to the existing seven public HSL tokens through theme.js, dispose on page hide, and use responsive host-colored controls. No new styling RPC is needed.

Local evidence includes normal CLI validation/packing, schema/admission and no-runtime checks; host catalog/identity/size/contrast/CAS/migration/atomic-audit checks; picker preview/apply/cancel/reset and stale-owner/withdrawal/high-contrast checks; and view-template initial/live environment/disposal checks. Exact-SHA CI, independent review and parent native multi-window/security-surface acceptance remain separate delivery gates. Local production builds do not establish them.

First review / CI delta

The first review found that muted text was validated only against its namesake muted surface, although host descriptions and statuses also use it on background, card and popover surfaces. The validator now checks generic and muted text on all neutral surfaces that reuse those tokens. The white-on-white counterexample is rejected both by the pure validator and the host catalog. Forest light/dark palettes remain valid.

The first exact-head test run 40317690002 failed with 15187 passing tests, one failure and 50 skips. Its single failure was the unchanged TERM-SSHD concurrent failure-release test expecting a particular invocation to reach OPEN first. Both calls perform asynchronous local-file preparation. The test now requires exactly one explicit Permission denied result, one successful result with matching bytes, and no rejected concurrent handle; it retains concurrency without assuming filesystem completion order. No transfer implementation changed. The test file's original SHA256 was identical in e5bd294c, 733f44c and fdeeaf74: 8b200d9f6a05ac5cd91fe4e0a95ea36a770d22267a6a0162b4c9fc927beb52a7. This is source identity and a source-based ordering diagnosis, not a claim that a baseline runtime failure was reproduced locally. The updated test needs the new exact-head CI result. That same first run passed the added Vite browser-import smoke, resolving the earlier local temp-directory limitation for that head.

Native security-color delta

Parent native acceptance at f868 verified zero-permission installation/enabling, light/dark Preview and Cancel, Apply across main/Settings windows, Restore default, restart persistence, and Disable fallback with the unavailable notice. The isolated schema-3 profile was not touched. These core results remain valid.

Native QA also found Forest-colored inner New Host cards/inputs/Save and some grant-panel text inside an otherwise fixed security container. The cause was Tailwind's derived --color-* aliases: changing only raw semantic tokens at the boundary does not recompute aliases already resolved on an ancestor. The same existing security rule now fixes both raw tokens and those color aliases. The selector and protected regions are unchanged; ordinary application cards remain theme-controlled. No host, credential or grant operation changed.

The regression renders the actual HostDetailsPanel (including General, Address, Credentials cards, real Inputs and Save), plus the real grant-panel root. It compiles the production Tailwind source, verifies which aliases those utilities consume, checks the fixed declarations, and verifies an ordinary card is outside the security selector. This is component/compiled-CSS coverage, not a claim of native computed-color acceptance. The parent's native color delta remains the final visual check. A local Chromium probe aborted because its SUID sandbox was not configured; sandboxing was not disabled to retry.