mirror of
https://github.com/Xevion/dotfiles.git
synced 2026-01-31 00:24:06 -06:00
feat: introduce meta-configs pattern and relocate fonts config
- Move fonts.toml from deployed location to meta/ directory - Update install-fonts.ts to read from meta/ and support extras array - Add comprehensive documentation explaining meta-configs pattern - Add ZedMono NF font and update Zed editor keybindings/settings
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Meta-Configs Pattern
|
||||
|
||||
This repository uses a **meta-config pattern** for configuration files that drive imperative actions during `chezmoi apply`, rather than being deployed directly to the filesystem.
|
||||
|
||||
## What are Meta-Configs?
|
||||
|
||||
Meta-configs are configuration files that:
|
||||
|
||||
1. **Are NOT deployed** to the target system
|
||||
2. **Drive scripts** that perform imperative actions (downloads, installations, modifications)
|
||||
3. **Live in `meta/`** at the repository root (outside `home/`)
|
||||
4. **Trigger `run_onchange_*` scripts** when their content changes
|
||||
|
||||
This pattern separates "what to configure" from "configuration files that get deployed."
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
chezmoi/
|
||||
├── meta/ # Meta-configs (NOT deployed)
|
||||
│ ├── fonts.toml # Drives font installation
|
||||
│ └── ... # Future meta-configs
|
||||
├── home/ # Deployed to $HOME
|
||||
│ ├── .config/
|
||||
│ ├── .local/
|
||||
│ └── ...
|
||||
└── docs/
|
||||
└── meta-configs.md # This file
|
||||
```
|
||||
|
||||
## Current Meta-Configs
|
||||
|
||||
### `meta/fonts.toml`
|
||||
|
||||
Drives the `install-fonts.ts` script to download and install fonts.
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
primary = "Inter"
|
||||
fallback = "Noto Sans"
|
||||
|
||||
[mono]
|
||||
primary = "Geist Mono"
|
||||
fallback = "JetBrains Mono"
|
||||
|
||||
[extras]
|
||||
# Fonts to install without category assignment
|
||||
fonts = ["ZedMono NF"]
|
||||
```
|
||||
|
||||
**Triggered by:** `run_onchange_after_install-fonts.sh.tmpl`
|
||||
|
||||
**Script:** `~/.local/bin/install-fonts.ts`
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Edit a meta-config in `meta/` (e.g., `meta/fonts.toml`)
|
||||
2. Run `chezmoi apply`
|
||||
3. Chezmoi detects the file changed via `{{ include "../meta/fonts.toml" | sha256sum }}`
|
||||
4. The corresponding `run_onchange_*` script executes
|
||||
5. The script reads the meta-config and performs imperative actions
|
||||
|
||||
## Why This Pattern?
|
||||
|
||||
### Problem
|
||||
|
||||
Some configuration requires imperative actions:
|
||||
- Downloading files from the internet
|
||||
- Installing packages
|
||||
- Modifying system state
|
||||
|
||||
These don't fit the declarative "deploy this file" model of chezmoi.
|
||||
|
||||
### Solution
|
||||
|
||||
Meta-configs provide:
|
||||
- **Centralized configuration** - Easy to edit, version-controlled
|
||||
- **Imperative execution** - Scripts perform the actual work
|
||||
- **Change detection** - `run_onchange_*` only runs when config changes
|
||||
- **Clear separation** - Meta-configs are clearly not "files to deploy"
|
||||
|
||||
## Adding a New Meta-Config
|
||||
|
||||
1. Create `meta/<name>.toml` with your configuration schema
|
||||
2. Create a script in `home/dot_local/bin/` to process the config
|
||||
3. Create `home/run_onchange_after_<name>.sh.tmpl` that:
|
||||
- Includes a hash comment: `# hash: {{ include "../meta/<name>.toml" | sha256sum }}`
|
||||
- Calls your processing script
|
||||
4. Document the meta-config in this file
|
||||
|
||||
## Comparison with `.managed/`
|
||||
|
||||
| Aspect | `meta/` | `.managed/` |
|
||||
| ------------ | ---------------------------------- | --------------------------------------- |
|
||||
| Deployed | No | Yes (via symlinks) |
|
||||
| Purpose | Drive imperative scripts | Source of truth for app configs |
|
||||
| Consumed by | Chezmoi scripts | Applications (via symlinks) |
|
||||
| Location | Repo root | Inside `home/` |
|
||||
@@ -10,24 +10,38 @@
|
||||
"context": "Workspace",
|
||||
"bindings": {
|
||||
// "shift shift": "file_finder::Toggle"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_mode == insert",
|
||||
"bindings": {
|
||||
// "j k": "vim::NormalBefore"
|
||||
}
|
||||
"j k": "vim::NormalBefore",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"alt-pageup": "editor::HalfPageUp"
|
||||
}
|
||||
"alt-pageup": "editor::HalfPageUp",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"alt-pagedown": "editor::HalfPageDown"
|
||||
}
|
||||
}
|
||||
"alt-pagedown": "editor::HalfPageDown",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Workspace",
|
||||
"bindings": {
|
||||
"ctrl-tab": "pane::ActivateNextItem",
|
||||
"ctrl-shift-tab": "pane::ActivatePreviousItem",
|
||||
},
|
||||
},
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"ctrl-alt-left": "pane::SwapItemLeft",
|
||||
"ctrl-alt-right": "pane::SwapItemRight",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -7,12 +7,19 @@
|
||||
// custom settings, run `zed: open default settings` from the
|
||||
// command palette (cmd-shift-p / ctrl-shift-p)
|
||||
{
|
||||
"base_keymap": "Cursor",
|
||||
"vim_mode": false,
|
||||
"icon_theme": "Material Icon Theme",
|
||||
"use_system_path_prompts": false,
|
||||
"base_keymap": "VSCode",
|
||||
"ui_font_size": 16,
|
||||
"buffer_font_size": 15,
|
||||
"auto_signature_help": false,
|
||||
"theme": {
|
||||
"mode": "dark",
|
||||
"mode": "light",
|
||||
"light": "One Light",
|
||||
"dark": "Gruvbox Dark Hard"
|
||||
"dark": "Min Dark (Blurred)"
|
||||
},
|
||||
"terminal": {
|
||||
"font_family": "ZedMono Nerd Font"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# Font Configuration for Chezmoi
|
||||
# This file defines which fonts to install and configure.
|
||||
# Fonts are sourced from Google Fonts automatically.
|
||||
#
|
||||
# To add a font: Just type its name - fuzzy matching will help if you misspell.
|
||||
# To swap fonts: Change the primary, run `chezmoi apply`, done!
|
||||
#
|
||||
# Run `install-fonts.ts` manually to see available fonts or troubleshoot.
|
||||
|
||||
[ui]
|
||||
# Sans-serif fonts for user interface elements
|
||||
primary = "Inter"
|
||||
fallback = "Noto Sans"
|
||||
|
||||
[serif]
|
||||
# Serif fonts for documents and reading
|
||||
primary = "Source Serif 4"
|
||||
fallback = "Noto Serif"
|
||||
|
||||
[mono]
|
||||
# Monospace fonts for code and terminals
|
||||
primary = "Geist Mono"
|
||||
fallback = "JetBrains Mono"
|
||||
|
||||
[emoji]
|
||||
# Emoji font for unicode emoji support
|
||||
primary = "Noto Color Emoji"
|
||||
|
||||
# Optional: Uncomment to install accessibility-focused fonts
|
||||
# [accessibility]
|
||||
# primary = "Atkinson Hyperlegible"
|
||||
@@ -49,11 +49,14 @@ interface FontDetails extends GoogleFont {
|
||||
variants: FontVariant[];
|
||||
}
|
||||
|
||||
interface FontCategoryConfig {
|
||||
primary: string;
|
||||
fallback?: string;
|
||||
}
|
||||
|
||||
interface FontConfig {
|
||||
[category: string]: {
|
||||
primary: string;
|
||||
fallback?: string;
|
||||
};
|
||||
categories: Record<string, FontCategoryConfig>;
|
||||
extras: string[];
|
||||
}
|
||||
|
||||
interface FuseResult<T> {
|
||||
@@ -67,7 +70,8 @@ interface FuseResult<T> {
|
||||
// ============================================================================
|
||||
|
||||
const FONTS_DIR = join(homedir(), ".local", "share", "fonts");
|
||||
const CONFIG_PATH = join(homedir(), ".config", "fontconfig", "fonts.toml");
|
||||
// Meta-config location: chezmoi source dir, not deployed to filesystem
|
||||
const CONFIG_PATH = join(homedir(), ".local", "share", "chezmoi", "meta", "fonts.toml");
|
||||
const API_BASE = "https://gwfh.mranftl.com/api";
|
||||
const CACHE_FILE = join(homedir(), ".cache", "font-catalog.json");
|
||||
const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
@@ -81,6 +85,10 @@ interface GitHubFontConfig {
|
||||
}
|
||||
|
||||
const GITHUB_FONTS: Record<string, GitHubFontConfig> = {
|
||||
"ZedMono NF": {
|
||||
repo: "ryanoasis/nerd-fonts",
|
||||
assetPattern: /^ZedMono\.zip$/,
|
||||
},
|
||||
Iosevka: {
|
||||
repo: "be5invis/Iosevka",
|
||||
assetPattern: /^PkgTTF-Iosevka-[\d.]+\.zip$/,
|
||||
@@ -517,7 +525,7 @@ async function loadConfig(): Promise<FontConfig> {
|
||||
const content = await Bun.file(CONFIG_PATH).text();
|
||||
|
||||
// Simple TOML parser for our specific format
|
||||
const config: FontConfig = {};
|
||||
const config: FontConfig = { categories: {}, extras: [] };
|
||||
let currentSection = "";
|
||||
|
||||
for (const line of content.split("\n")) {
|
||||
@@ -530,16 +538,33 @@ async function loadConfig(): Promise<FontConfig> {
|
||||
const sectionMatch = trimmed.match(/^\[(\w+)\]$/);
|
||||
if (sectionMatch) {
|
||||
currentSection = sectionMatch[1];
|
||||
config[currentSection] = { primary: "" };
|
||||
if (currentSection !== "extras") {
|
||||
config.categories[currentSection] = { primary: "" };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key-value pair
|
||||
// Handle [extras] section - array of fonts
|
||||
if (currentSection === "extras") {
|
||||
// Match: fonts = ["Font1", "Font2"]
|
||||
const arrayMatch = trimmed.match(/^fonts\s*=\s*\[(.*)\]$/);
|
||||
if (arrayMatch) {
|
||||
const arrayContent = arrayMatch[1];
|
||||
// Parse quoted strings from array
|
||||
const fontMatches = arrayContent.matchAll(/"([^"]+)"/g);
|
||||
for (const match of fontMatches) {
|
||||
config.extras.push(match[1]);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key-value pair for category sections
|
||||
const kvMatch = trimmed.match(/^(\w+)\s*=\s*"([^"]+)"$/);
|
||||
if (kvMatch && currentSection) {
|
||||
if (kvMatch && currentSection && currentSection !== "extras") {
|
||||
const [, key, value] = kvMatch;
|
||||
if (key === "primary" || key === "fallback") {
|
||||
config[currentSection][key] = value;
|
||||
config.categories[currentSection][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,13 +655,16 @@ async function installFromConfig(): Promise<void> {
|
||||
mkdirSync(FONTS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Collect all fonts to install
|
||||
// Collect all fonts to install from categories
|
||||
const fontsToInstall: string[] = [];
|
||||
for (const category of Object.values(config)) {
|
||||
for (const category of Object.values(config.categories)) {
|
||||
if (category.primary) fontsToInstall.push(category.primary);
|
||||
if (category.fallback) fontsToInstall.push(category.fallback);
|
||||
}
|
||||
|
||||
// Add extras
|
||||
fontsToInstall.push(...config.extras);
|
||||
|
||||
// Remove duplicates
|
||||
const uniqueFonts = [...new Set(fontsToInstall)];
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{{ if eq .chezmoi.os "linux" -}}
|
||||
#!/bin/bash
|
||||
# Font Installer Hook
|
||||
# Runs automatically when fonts.toml changes
|
||||
# Runs automatically when meta/fonts.toml changes
|
||||
#
|
||||
# fonts.toml hash: {{ include "dot_config/fontconfig/fonts.toml" | sha256sum }}
|
||||
# fonts.toml hash: {{ include "../meta/fonts.toml" | sha256sum }}
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Font Installation Meta-Config
|
||||
#
|
||||
# This file drives the install-fonts.ts script.
|
||||
# It is NOT deployed to the filesystem.
|
||||
#
|
||||
# Fonts are sourced from:
|
||||
# - Google Fonts (via google-webfonts-helper API)
|
||||
# - GitHub releases (for fonts like Iosevka, ZedMono NF)
|
||||
#
|
||||
# Run `install-fonts.ts --help` for manual usage.
|
||||
# Run `install-fonts.ts --search <query>` to find fonts.
|
||||
|
||||
[ui]
|
||||
# Sans-serif fonts for user interface elements
|
||||
primary = "Inter"
|
||||
fallback = "Noto Sans"
|
||||
|
||||
[serif]
|
||||
# Serif fonts for documents and reading
|
||||
primary = "Source Serif 4"
|
||||
fallback = "Noto Serif"
|
||||
|
||||
[mono]
|
||||
# Monospace fonts for code and terminals
|
||||
primary = "Geist Mono"
|
||||
fallback = "JetBrains Mono"
|
||||
|
||||
[emoji]
|
||||
# Emoji font for unicode emoji support
|
||||
primary = "Noto Color Emoji"
|
||||
|
||||
[extras]
|
||||
# Additional fonts to install without category assignment
|
||||
# Useful for fonts needed by specific applications
|
||||
fonts = ["ZedMono NF"]
|
||||
Reference in New Issue
Block a user