add colors.toml and 0x-hex support, rescuing 44 more community themes
Poked at the 62 "no parseable source" skips from the first pass and found two things the pipeline didn't handle: several community repos genuinely ship colors.toml too — some in Omarchy 4's own semantic schema (best case, no derivation needed at all, same quality as the official 22), others reusing the filename for an unrelated flat color0..color15 scheme — and a handful of alacritty.toml files use 0xrrggbb instead of #rrggbb. palette.ts's loadPalette is now a thin wrapper over two exported pieces (parseTomlText, paletteFromRecord) so the community pipeline can attempt the real schema first without duplicating the reader; falls through to the color16 or ANSI-derived paths otherwise. generate-themes.ts --check still reports 0 changes for the official set — the refactor is behavior-preserving there, the lowercase-hex normalization it picked up along the way touched 7 official theme files cosmetically (their source colors.toml has uppercase hex), regenerated for real rather than left inconsistent with their own source. 147 -> 191 community themes ported. Of the original 62 unparseable repos, most turned out to be simply gone (404 on GitHub — repos deleted or renamed since the site listed them); the rest needed one of the two format additions above. 20 themes remain stuck: 16 dead repos, 2 with no repo link at all, and the same handful of genuinely low-contrast source palettes (mostly Solarized variants) where fixing contrast would mean changing the author's actual colours rather than porting them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,10 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
import { mapPalette } from './themes/map';
|
||||
import type { Palette } from './themes/palette';
|
||||
import { paletteFromRecord, parseTomlText } from './themes/palette';
|
||||
import { deriveOmarchyPalette } from './themes/from-ansi';
|
||||
import { parseAlacrittyToml, parseGhosttyConf } from './themes/ansi-source';
|
||||
import { parseAlacrittyToml, parseColor16Toml, parseGhosttyConf } from './themes/ansi-source';
|
||||
import { contrast, ensureContrast, TEXT, UI } from './themes/contrast';
|
||||
|
||||
// Port community Omarchy themes from omarchythemes.com into the same 84-token shape the official
|
||||
@@ -36,7 +38,7 @@ type ManifestEntry = {
|
||||
id: string;
|
||||
name: string;
|
||||
repo: string;
|
||||
kind: 'alacritty.toml' | 'ghostty.conf';
|
||||
kind: string;
|
||||
dark: boolean;
|
||||
};
|
||||
|
||||
@@ -105,12 +107,29 @@ async function discoverRepo(slug: string): Promise<string | undefined> {
|
||||
return m ? `${m[1]}/${m[2]}` : undefined;
|
||||
}
|
||||
|
||||
async function sourceFor(
|
||||
repo: string,
|
||||
): Promise<{ ansi: NonNullable<ReturnType<typeof parseAlacrittyToml>>; kind: ManifestEntry['kind'] } | undefined> {
|
||||
async function sourceFor(repo: string): Promise<{ palette: Palette; kind: string; derived: boolean } | undefined> {
|
||||
const branch = ghApiDefaultBranch(repo);
|
||||
const raw = (p: string) => `https://raw.githubusercontent.com/${repo}/${branch}/${p}`;
|
||||
|
||||
// `colors.toml` is ambiguous across community repos: it might genuinely be Omarchy 4's own semantic
|
||||
// schema (in which case no derivation is needed at all — same quality as the official 22), or it might
|
||||
// reuse the filename for an unrelated flat `color0..color15` ANSI scheme. Try the real schema first;
|
||||
// only fall through to the ANSI reading if the required keys aren't there.
|
||||
const tomlText = await fetchText(raw('colors.toml'));
|
||||
if (tomlText) {
|
||||
const kv = parseTomlText(tomlText);
|
||||
try {
|
||||
return {
|
||||
palette: paletteFromRecord(kv, `${repo}/colors.toml`),
|
||||
kind: 'colors.toml (Omarchy schema)',
|
||||
derived: false,
|
||||
};
|
||||
} catch {
|
||||
const ansi = parseColor16Toml(kv);
|
||||
if (ansi) return { palette: deriveOmarchyPalette(ansi), kind: 'colors.toml (16-colour ANSI)', derived: true };
|
||||
}
|
||||
}
|
||||
|
||||
for (const path of [
|
||||
'alacritty.toml',
|
||||
'.config/alacritty.toml',
|
||||
@@ -120,13 +139,13 @@ async function sourceFor(
|
||||
const text = await fetchText(raw(path));
|
||||
if (!text) continue;
|
||||
const ansi = parseAlacrittyToml(text);
|
||||
if (ansi) return { ansi, kind: 'alacritty.toml' };
|
||||
if (ansi) return { palette: deriveOmarchyPalette(ansi), kind: 'alacritty.toml', derived: true };
|
||||
}
|
||||
for (const path of ['ghostty.conf', 'ghostty-theme', '.config/ghostty/config']) {
|
||||
const text = await fetchText(raw(path));
|
||||
if (!text) continue;
|
||||
const ansi = parseGhosttyConf(text);
|
||||
if (ansi) return { ansi, kind: 'ghostty.conf' };
|
||||
if (ansi) return { palette: deriveOmarchyPalette(ansi), kind: 'ghostty.conf', derived: true };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -136,9 +155,9 @@ async function portOne(slug: string): Promise<{ entry: ManifestEntry; css: strin
|
||||
if (!repo) return { skip: 'no github repo link found on its omarchythemes.com page' };
|
||||
|
||||
const source = await sourceFor(repo);
|
||||
if (!source) return { skip: `no parseable alacritty.toml/ghostty.conf in ${repo}` };
|
||||
if (!source) return { skip: `no parseable colors.toml/alacritty.toml/ghostty.conf in ${repo}` };
|
||||
|
||||
const palette = deriveOmarchyPalette(source.ansi);
|
||||
const palette = source.palette;
|
||||
const tokens = mapPalette(palette);
|
||||
|
||||
// `--input` carries a real accessibility requirement (a field boundary you can't see) where `--border`
|
||||
@@ -161,16 +180,19 @@ async function portOne(slug: string): Promise<{ entry: ManifestEntry; css: strin
|
||||
const body = Object.entries(tokens)
|
||||
.map(([k, v]) => ` ${k}: ${v};`)
|
||||
.join('\n');
|
||||
const modeLine = source.kind.startsWith('colors.toml (Omarchy')
|
||||
? `${palette.mode === 'dark' ? 'Dark theme' : 'Light theme'} (${palette.mode}) — the source repo's own colors.toml says so directly.`
|
||||
: `${palette.mode === 'dark' ? 'Dark theme' : 'Light theme'} (${palette.mode}), by luminance of its own background — the source repo carries no mode flag of its own to read.`;
|
||||
const provenanceLine = source.derived
|
||||
? `went through deriveOmarchyPalette (scripts/themes/from-ansi.ts) because the source has no Omarchy 4\n colors.toml — some tokens here are reconstructed, not authored.`
|
||||
: `came straight from the repo's own colors.toml — the same schema and reader the official 22 themes\n use, so nothing here is reconstructed.`;
|
||||
const css = `/*
|
||||
${name} — ported from a community Omarchy theme, not from basecamp/omarchy's own set.
|
||||
|
||||
${palette.mode === 'dark' ? 'Dark theme' : 'Light theme'} (${palette.mode}), by luminance of its own
|
||||
background — the source repo carries no mode flag of its own to read.
|
||||
${modeLine}
|
||||
|
||||
Source: https://github.com/${repo} (${source.kind}), read by scripts/port-community-themes.ts. Values
|
||||
went through deriveOmarchyPalette (scripts/themes/from-ansi.ts) because a single-theme repo ships
|
||||
per-app configs, not Omarchy 4's own colors.toml shape — some tokens here are reconstructed, not
|
||||
authored. Every text/surface pair is contrast-checked the same way the official themes are.
|
||||
${provenanceLine} Every text/surface pair is contrast-checked the same way the official themes are.
|
||||
|
||||
DO NOT EDIT BY HAND. Re-run the script to refresh from the source repo.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user