From 77783126c8e35d5880a3e16a0014fc727f59b00a Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 10 Jun 2026 01:18:20 -0500 Subject: feat(theme-studio): group families by hue anchor with a lightness-scaled neutral cut Replace gap-based hue clustering and the flat neutral threshold. Chromatic colors now bucket by nearest perceptual hue anchor (red, orange, yellow, green, teal, blue, purple, pink), so adjacent categories stay separate by construction and there's no single-linkage chaining merging them through intermediate tones. The neutral cut is lightness-scaled rather than flat: a color reads as neutral below a chroma that's highest in the mid-tones and tapers toward the light end, so a faint mid gray goes neutral while an equally-faint pale tint keeps its hue. This fixes the two concrete problems: the grays and steels consolidate into one neutral column, and pale tints (light blues) stay with their hue instead of falling into the grays. What it doesn't fix is hue-adjacent warm colors: this palette's olive-greens sit on top of the golds in OKLCH hue, so they still group together, and a ramp that drifts in hue can split across an anchor boundary. That's a real property of the colors, not a bug, and it's filed for research (a writeup of the problem and the four approaches tried lives outside the repo; the task points to it). 20 family node tests including the yellow/green split and the no-chaining case; suite green. --- todo.org | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'todo.org') diff --git a/todo.org b/todo.org index ae6fa1a6..282ab967 100644 --- a/todo.org +++ b/todo.org @@ -94,12 +94,12 @@ Commit =23926837=. README documents the ramp controls and defaults, the worst-ca ** TODO [#B] theme-studio color families :feature:theme-studio: Show the palette as hue-grouped strips (dark→light) over the existing flat, individually-editable palette. Grouping is by OKLCH hue from the hex, so renaming a color never moves it. A per-strip count control generates a symmetric ramp (N → base ±N) from the strip's most-saturated color; regenerate is authoritative, repointing surviving-step references by lightness rank and leaving removed-step references a visible "(gone)". The ground strip is synthesized from the bg/fg assignments and pinned first; the standalone ramp panel is removed. Designed in [[file:docs/theme-studio-color-families-spec.org][docs/theme-studio-color-families-spec.org]]. Codex-reviewed Ready 2026-06-10 after response folded: pivoted from name-derived families to hex-derived families over a flat palette, which designs out the name-grammar/import-inference and chip-ownership blockers. All review findings dispositioned; both open decisions resolved. Builds on and supersedes the palette-ramps v1 ramp UI. Six phases below; manual aesthetic checks under the Manual testing parent. -*** TODO [#B] Family model core :solo: -Phase 1. In app-core.js, pure: =familiesFromPalette(palette, groundHexes)= → ground strip (from the two ground hexes, de-duped) + hue-clustered families (gap-split at 25°, neutrals at C<0.02 separated), each with a base (most-saturated, tie toward mid-lightness); =regenFamily(baseHex, n, opts)= → members (n=0 → base only, handled without =ramp()='s 1-4 clamp; n≥1 → ramp base±n); =stepRepointPlan(oldMembers, newMembers)= → {map: [[oldHex,newHex]], removed: [hex]} by signed lightness rank. Node tests: spectrum splits, near-pair stays, neutrals/ground-absent, n=0, repoint survivors + removed. Verify: =make theme-studio-test= green. -*** TODO [#B] Family sort core :solo: -Phase 2. =sortFamilies= orders ground-first, neutrals (C<0.02) next, chromatic by base hue (ties by base lightness then hex); within-strip by OKLCH lightness. Node tests: spectrum order, all-neutral, ties, the 25° gap boundary. Sorting is display-only; the stored palette order is untouched. -*** TODO [#B] Family-strip rendering :solo: -Phase 3. Render the palette panel as the pinned ground strip + hue-sorted family strips (base marked, dark→light), reusing chip styling; the existing per-chip rename/remove/edit keep working over the still-flat palette. No count control yet. Verify: headless screenshot + the panel still drives the existing flows. +*** 2026-06-10 Wed @ 01:17:45 -0500 Family model core landed +Phase 1 (commit =ebe18d51=, grouping reworked in ==). =familiesFromPalette=, =regenFamily=, =rankByLightness=, =stepRepointPlan= in app-core.js, pure and hex-derived. Grouping started as gap-clustering + flat neutral threshold; after the design discussion it became nearest-hue-anchor bucketing (no single-linkage chaining) + a lightness-scaled neutral threshold (pale tints keep their hue, mid grays go neutral). regenFamily handles n=0 without ramp()'s clamp; stepRepointPlan maps survivors / lists removed by signed lightness rank. 20 node tests including the green/yellow split and the no-chaining case. Open: hue-adjacent warm colors still merge — research task above (=~/color-sorting.org=). +*** 2026-06-10 Wed @ 01:17:45 -0500 Family sort core landed +Phase 2 (commit =74db9a52=). =sortFamilies=/=sortFamilyMembers=: neutrals first, then chromatic by base hue (rounded so a hue hair doesn't outrank lightness), ties by base lightness then hex; members dark→light. Display-only; stored palette order untouched. 4 node tests. +*** 2026-06-10 Wed @ 01:17:45 -0500 Family-strip rendering landed +Phase 3 (commit =111687b0=, columns =e7ae18c4=). renderPalette restructured into the pinned ground strip + hue-sorted family columns (top→bottom dark→light), chips keep per-chip rename/remove/select, move-arrows/drag dropped. #familytest gate locks the structure + rename-stays-in-strip. Existing palette flows stay green. *** TODO [#B] Count control + regenerate :solo: Phase 4. Per-strip count input (0-4). On change: =regenFamily=, apply =stepRepointPlan= (repoint survivors via =repointHex=, leave removed refs "(gone)"), update PALETTE, re-render. New browser gate: count up adds symmetric steps; count down drops extremes and a ref to a dropped step reads "(gone)" while a ref to a surviving step follows the new hex. Depends on Phase 1. *** TODO [#B] Ground strip + base edit + retire ramp panel :solo: @@ -107,6 +107,9 @@ Phase 5. Synthesize the ground strip from =MAP.bg=/=MAP.p= (editable, pinned, de *** TODO [#B] Warnings, seeding, export, README close-out :solo: Phase 6. Keep =paletteWarnings= on the flattened palette but exempt adjacent same-family ramp steps from the too-similar warning. Confirm =seedPkgmap= still reads the flat palette unchanged. Confirm export emits the flat palette unchanged and import needs no reconstruction; gate an import→render→export round-trip leaving the JSON identical. Update README (families, ground strip, regenerate, removed-step refs, ramp-panel removal). Closes the README + round-trip acceptance criteria. +** TODO [#B] Color-family grouping for hue-adjacent warm colors :feature:theme-studio:research: +The hue-anchor + lightness-scaled-threshold grouping (shipped in color families) fixed the neutrals and pale tints but can't cleanly separate hue-adjacent warm colors: this palette's olive-greens (~110-120° OKLCH) sit right on the golds (~85-95°), so by hue they merge, and a ramp that drifts in hue can split across an anchor boundary. The problem, the four approaches tried, why each failed, and directions to research (2D chromaticity clustering, lightness-aware hue grouping, ramp detection, perceptual color-naming models, an optional hex-derived family hint) are written up in =~/color-sorting.org=. Craig is finding someone to comment. Pick the work back up from that doc. + ** TODO [#C] Internet radio now-playing song :feature:music:emms: Show the currently-playing song while streaming an internet radio station. Lives in =modules/music-config.el= (EMMS + MPV backend, M3U radio stations). The track title comes from the stream's ICY metadata — EMMS exposes it via =emms-track-description= / =emms-playing-time= and updates it on the metadata-change hook; MPV reports the ICY title too. Add an option to show the song in the minibuffer (e.g. echo on track change, or an on-demand command). Consider also a mode-line indicator as a second surface. -- cgit v1.2.3