docs/superpowers/plans/2026-04-09-font-config-implementation.md
Ref: Size: 7.6 KiB
# Font Config Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Move terminal font family and font size into an `st`-style config module and use `Monaspace Argon` as the explicit configured font.
**Architecture:** Add a small `src/config.zig` module with compile-time constants, then thread that configuration into the existing Fontconfig lookup path and the main terminal startup path. Keep runtime behavior unchanged apart from explicit font selection and size sourcing, with no fallback logic.
**Tech Stack:** Zig 0.15, Fontconfig, FreeType, existing `font.zig` and `main.zig` startup path
---
## File Structure
- Create: `src/config.zig`
- Holds user-editable font defaults in one place.
- Modify: `src/font.zig`
- Reads the configured family and resolves it through Fontconfig without fallback.
- Modify: `src/main.zig`
- Uses configured font size in the normal terminal path and helper/demo paths that currently hardcode `16`.
- Test: `src/font.zig`
- Keeps lookup tests aligned with explicit configured-family behavior.
- Test: `src/main.zig`
- Keeps any font-size-dependent helper tests aligned with config-backed size use if needed.
### Task 1: Add the ST-style config module
**Files:**
- Create: `src/config.zig`
- [ ] **Step 1: Add the config module**
```zig
pub const font_family = "Monaspace Argon";
pub const font_size_px: u32 = 16;
```
- [ ] **Step 2: Verify the file contents**
Run: `sed -n '1,40p' src/config.zig`
Expected:
- Shows only the two config constants.
- [ ] **Step 3: Commit**
```bash
git add src/config.zig
git commit -m "Add terminal font config module"
```
### Task 2: Wire configured font family into Fontconfig lookup
**Files:**
- Modify: `src/font.zig`
- Test: `src/font.zig`
- [ ] **Step 1: Add the failing test update**
Replace the old generic lookup test with a configured-family test:
```zig
test "lookupConfiguredFont returns a valid configured font path" {
var lookup = try lookupConfiguredFont(std.testing.allocator);
defer lookup.deinit(std.testing.allocator);
try std.testing.expect(lookup.path.len > 0);
}
```
- [ ] **Step 2: Run test to verify it fails**
Run: `rm -rf /tmp/zig-global-cache-font-plan && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-font-plan && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-font-plan zig build test --summary all`
Expected:
- FAIL because `lookupConfiguredFont` is undefined or call sites still reference `lookupMonospace`.
- [ ] **Step 3: Implement the configured-family lookup**
Update imports and lookup code in `src/font.zig`:
```zig
const config = @import("config");
```
```zig
pub fn lookupConfiguredFont(alloc: std.mem.Allocator) !FontLookup {
if (c.FcInit() == c.FcFalse) return error.FcInitFailed;
const pattern = c.FcPatternCreate() orelse return error.FcPatternCreate;
defer c.FcPatternDestroy(pattern);
_ = c.FcPatternAddString(pattern, c.FC_FAMILY, @ptrCast(config.font_family));
_ = c.FcPatternAddInteger(pattern, c.FC_WEIGHT, c.FC_WEIGHT_REGULAR);
_ = c.FcPatternAddInteger(pattern, c.FC_SLANT, c.FC_SLANT_ROMAN);
_ = c.FcConfigSubstitute(null, pattern, c.FcMatchPattern);
c.FcDefaultSubstitute(pattern);
var result: c.FcResult = undefined;
const matched = c.FcFontMatch(null, pattern, &result) orelse return error.FcFontMatchFailed;
defer c.FcPatternDestroy(matched);
var file_cstr: [*c]c.FcChar8 = null;
if (c.FcPatternGetString(matched, c.FC_FILE, 0, &file_cstr) != c.FcResultMatch) {
return error.FcGetFileFailed;
}
var index: c_int = 0;
_ = c.FcPatternGetInteger(matched, c.FC_INDEX, 0, &index);
const slice = std.mem.span(@as([*:0]const u8, @ptrCast(file_cstr)));
const dup = try alloc.dupeZ(u8, slice);
return .{ .path = dup, .index = index };
}
```
Update existing tests and helpers that call `lookupMonospace` to call `lookupConfiguredFont` instead.
- [ ] **Step 4: Run test to verify it passes**
Run: `rm -rf /tmp/zig-global-cache-font-plan && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-font-plan && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-font-plan zig build test --summary all`
Expected:
- PASS for the font module tests and any dependent tests.
- [ ] **Step 5: Commit**
```bash
git add src/font.zig src/main.zig build.zig
git commit -m "Resolve configured terminal font family"
```
### Task 3: Move font size to config-backed startup
**Files:**
- Modify: `src/main.zig`
- Modify: `src/font.zig`
- [ ] **Step 1: Add the failing test update**
Replace hardcoded `16` setup in the normal terminal path and demo helper path with config-backed size references:
```zig
const config = @import("config");
```
```zig
const font_size: u32 = config.font_size_px;
```
```zig
var face = try font.Face.init(alloc, font_lookup.path, font_lookup.index, config.font_size_px);
```
- [ ] **Step 2: Run test to verify it fails**
Run: `rm -rf /tmp/zig-global-cache-size-plan && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-size-plan && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-size-plan zig build test --summary all`
Expected:
- FAIL until all hardcoded call sites are updated and the config module is imported where needed.
- [ ] **Step 3: Implement config-backed font size usage**
Update `src/main.zig`:
```zig
const config = @import("config");
```
Replace the startup hardcode:
```zig
const font_size: u32 = config.font_size_px;
```
Replace helper/demo face creation that currently uses `16`:
```zig
var face = try font.Face.init(alloc, font_lookup.path, font_lookup.index, config.font_size_px);
```
Use `lookupConfiguredFont` in the same locations:
```zig
var font_lookup = try font.lookupConfiguredFont(alloc);
```
- [ ] **Step 4: Run test to verify it passes**
Run: `rm -rf /tmp/zig-global-cache-size-plan && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-size-plan && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-size-plan zig build test --summary all`
Expected:
- PASS with all tests green.
- [ ] **Step 5: Commit**
```bash
git add src/main.zig src/font.zig src/config.zig
git commit -m "Read terminal font size from config"
```
### Task 4: Full verification
**Files:**
- Modify: none
- Test: `src/font.zig`, `src/main.zig`
- [ ] **Step 1: Run the full test suite**
Run: `rm -rf /tmp/zig-global-cache-font-verify && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-font-verify && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-font-verify zig build test --summary all`
Expected:
- PASS
- [ ] **Step 2: Run a build verification**
Run: `rm -rf /tmp/zig-global-cache-font-build && cp -a /home/xanderle/.cache/zig /tmp/zig-global-cache-font-build && ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache-font-build zig build`
Expected:
- PASS
- [ ] **Step 3: Run a manual launch verification**
Run: `zig build run`
Expected:
- Terminal launches with `Monaspace Argon`.
- Terminal starts at configured font size.
- Startup fails loudly if the configured family is not available.
- [ ] **Step 4: Commit**
```bash
git add src/config.zig src/font.zig src/main.zig
git commit -m "Verify configured terminal font defaults"
```
## Self-Review
- Spec coverage:
- ST-style config module: Task 1
- Explicit `Monaspace Argon` family: Task 2
- No fallback behavior: Task 2 and Task 4
- Config-backed font size: Task 3
- Validation with tests and manual launch: Task 4
- Placeholder scan:
- No `TODO`, `TBD`, or deferred implementation markers remain.
- Type consistency:
- The plan consistently uses `lookupConfiguredFont`, `config.font_family`, and `config.font_size_px`.