targets: The Deploy-Target Registry#

The seam between the palette model and every artifact.

Each supported application is a Target: a small policy object declaring which palette hues anchor which of the app’s roles and which filenames each mode produces. The mechanism – iterating modes, writing out/, checking drift, deploying to local config directories – lives once, in irix.cli and irix.deploy (policy/mechanism separation; see Architecture: How Irix Is Put Together).

The render contract is the parity constraint: render() takes an explicit Mode, and the engine always invokes it for both modes – a dark-only target is unrepresentable rather than merely discouraged.

Concrete targets live in sibling modules, each registered here on import: targets.editors: VSCode, kimi, Sublime, and Vim Targets, targets.portable: Portable and Creative Deploy Targets, targets.shell: Shell-Tooling Targets, targets.terminals: Terminal-Emulator Targets.

irix.targets.Mode = Mode#

Type alias.

Type aliases are created through the type statement:

type Alias = int

In this example, Alias and int will be treated equivalently by static type checkers.

At runtime, Alias is an instance of TypeAliasType. The __name__ attribute holds the name of the type alias. The value of the type alias is stored in the __value__ attribute. It is evaluated lazily, so the value is computed only if the attribute is accessed.

Type aliases can also be generic:

type ListOrSet[T] = list[T] | set[T]

In this case, the type parameters of the alias are stored in the __type_params__ attribute.

See PEP 695 for more information.

Irix deploy-target registry: the seam between the palette model and every artifact.

Each supported application is a Target: a small policy object declaring which palette hues anchor which of the app’s roles and which filenames each mode produces. The mechanism — iterating modes, writing out/, checking drift, deploying to local config directories — lives once, in irix.cli and irix.deploy (policy/mechanism separation).

The render contract is the parity constraint: render() takes an explicit Mode, and the engine always invokes it for both modes — a dark-only target is unrepresentable rather than merely discouraged.

irix.targets.MODES: tuple[Mode, Mode] = ('dark', 'light')#

Every target renders exactly these modes; order is the canonical presentation order.

class irix.targets.Anchors(*, targets: dict[str, dict[str, str | dict[str, str]]])#

The central app→hue assignment table, loaded from irix/data/anchors/*.yaml.

Historically every artifact hand-picked its own neutral and accent hues (kitty=slate, alacritty=sage, firefox=sage+jade, …). This table records those choices as data so they are made once, centrally — and so regeneration preserves each app’s established look instead of re-deciding it. One file per target family keeps the table reviewable beside the family’s renderers.

targets#

Mapping of target name to that target’s role→address table, where an address is a Palette.resolve-compatible 'hue-shade' (or bare hue) string, or a per-mode {dark: ..., light: ...} override of the same shape irix.semantics.Semantics roles support (see that module for the rationale).

Type:

dict[str, dict[str, str | dict[str, str]]]

classmethod load() → Anchors#

Load and cache the packaged anchor table, merged across family files.

Returns:

The union of every anchors/*.yaml file’s targets tables; target names are globally unique, so files never collide.

address(target: str, role: str, mode: Mode = 'dark') → str#

Return the palette address anchored to role for target.

Parameters:
  • target – Registered target name, e.g. 'kitty'.

  • role – Target-local role key, e.g. 'neutral' or 'ansi_red'.

  • mode – Which mode’s address to return when the anchor carries a per-mode {dark: ..., light: ...} override; plain-string anchors ignore this (the same address serves both modes). Defaults to 'dark' so every pre-existing call site keeps today’s behavior unchanged.

Raises:

KeyError – If the target or role has no anchor entry — anchoring is explicit, never defaulted, so a missing entry must fail loudly at render time.

model_config = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

irix.targets.palette_for(mode: Mode, *, full: bool = False) → Palette#

Return the palette for mode.

Parameters:
  • mode – Which scheme to load.

  • full – Load the full Radix superset (full/full_light) instead of the curated Irix subset.

irix.targets.resolve_strict(palette: Palette, address: str) → str#

Resolve address through palette, raising on any miss.

Palette.resolve returns '' on a miss; artifact generation must never embed an empty color, so the engine funnels every lookup through this guard.

Parameters:
  • palette – The palette to resolve against.

  • address – A '#hex', 'hue-shade', or bare-hue address.

Returns:

The resolved '#RRGGBB' value.

Raises:

KeyError – If the address does not resolve to a non-empty value.

class irix.targets.Target(*, name: str, family: str)#

One deployable application target.

Subclasses implement render for a single, explicit mode and are registered into the module-level REGISTRY via register. Reference implementation: irix.targets.terminals.KittyTarget.

name#

Unique registry key, e.g. 'kitty'.

Type:

str

family#

The out/ subdirectory family: 'terminals', 'shell', 'editors', 'browsers', 'inkscape', 'python', 'web'.

Type:

str

render(mode: Mode) → dict[str, str]#

Render every artifact this target produces for mode.

Parameters:

mode – The scheme to render; the engine calls this once per member of MODES.

Returns:

Mapping of artifact path (relative to out/) to full file content. Must be non-empty for both modes — the parity suite enforces this.

deploy_dest(relpath: str) → Path | None#

Return the local config path where the relpath artifact installs.

Parameters:

relpath – One key of render’s result.

Returns:

The absolute destination path, or None when the artifact is generate-only (no local application consumes it directly).

anchor(role: str, mode: Mode) → str#

Resolve this target’s anchored role to a hex value under mode.

Parameters:
  • role – Target-local role key from anchors.yaml.

  • mode – The scheme to resolve under.

model_config = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

irix.targets.REGISTRY: dict[str, Target] = {}#

The live registry; populated by register at import of each family module.

irix.targets.register(target: T) → T#

Register target under its name; duplicate names are a programming error.

irix.targets.all_targets() → Iterator[Target]#

Iterate registered targets in name order (import family modules first).

irix.targets.load_all_targets() → None#

Import every family module in this package, registering its targets.

Family modules (irix.targets.terminals, …) call register as an import-time side effect; nothing else triggers that import. cli and deploy call this once before consulting REGISTRY/all_targets, so a new family module is picked up automatically – no per-module import list to maintain here as squads add targets.

irix.targets.source_out_root() → Path#

Return the repo checkout’s out/ directory (the generation destination).

Generation writes to the source tree so artifacts are reviewed and shipped as package data; it is a development-time operation and errors in wheel installs.

Raises:

FileNotFoundError – When no source checkout is present (e.g. wheel install).

irix.targets.packaged_out_root() → Path#

Return the readable root of packaged artifacts (deployment source).

Resolves irix.out (the built-wheel package-dir alias of out/) and falls back to the source checkout for editable installs, where the alias never materializes.