v1/v2-Merge — Build-Variants Refactor¶
Datum: 2026-05-26
Repos: kuro-theme-settings/ (Master, Git) + kuro2-theme-settings/ (wird absorbiert)
Strategie: Build-Variants per env-Var KURO_THEME=v1|v2
Was ist v1-spezifisch vs v2-spezifisch?¶
v2-only Feature: Aspects (Statusbar-Chip + Frontmatter-Override)
- main.ts: +44 Zeilen — aspectStatusEl, Click-Handler cycelt Aspect, renderAspectChip()
- legacy.ts: ASPECTS, ASPECT_LABELS, ASPECT_KANJI Exports + Aspect-Resolver-Logik (+349 Zeilen)
- settings/tab.ts: Aspect-Picker UI (+16 Zeilen)
- styles.css: .kuro2-aspect-chip, .kuro2-aspect-glyph (+116 Zeilen)
Theme-Identität (immer pro Variant unterschiedlich):
- manifest.json: id, name, description, version
- Plugin-Klasse intern: kein Unterschied beim Klassenname (KuroThemeSettingsPlugin), aber DEFAULT_SETTINGS-Inhalt
- sync.sh: Ziel-Vault-Pfad
- Color-Presets (v1 = Kuro-Style, v2 = Signal-Protocol)
Identisch v1+v2:
- Komplett: src/screensaver/ (Engine, Scenes, FX, Audio, Terminal, HUD, Data)
- src/settings/tab-screensaver.ts
- esbuild.config.mjs (50 Zeilen, identisch)
- tsconfig.json, lefthook.yml, docs
Refactor-Architektur¶
kuro-theme-settings/ (= Master)
├── manifest.v1.json ← _kuro-theme-settings, v0.4.0
├── manifest.v2.json ← _kuro2-theme-settings, v2.0.0
├── styles.v1.css ← v1-Styles
├── styles.v2.css ← v2-Styles + Aspect-Chip
├── esbuild.config.mjs ← liest env KURO_THEME, outdir = dist/<variant>/
├── sync.v1.sh ← Sync v1 ins Vault
├── sync.v2.sh ← Sync v2 ins Vault
├── src/
│ ├── main.ts ← THEME = import.meta.env.KURO_THEME | esbuild --define
│ ├── settings/
│ │ ├── tab.ts ← rendert variant-aware (Aspect-Picker nur bei v2)
│ │ └── tab-screensaver.ts
│ ├── screensaver/ (unverändert)
│ ├── legacy.ts ← gemeinsame Plugin-Klasse, DEFAULT_SETTINGS gemerged
│ └── themes/
│ ├── types.ts ← interface ThemeVariant { id, name, presets, aspects?, ... }
│ ├── variant.ts ← exports active variant (chosen by build-time define)
│ ├── v1.ts ← const V1: ThemeVariant = { ... Kuro v1 ... }
│ └── v2.ts ← const V2: ThemeVariant = { ... Signal Protocol mit aspects ... }
└── dist/
├── v1/main.js, manifest.json, styles.css
└── v2/main.js, manifest.json, styles.css
Build-Mechanik¶
// esbuild.config.mjs (Auszug)
const variant = process.env.KURO_THEME || 'v1';
await build({
entryPoints: ['src/main.ts'],
outdir: `dist/${variant}`,
define: { '__KURO_VARIANT__': JSON.stringify(variant) },
});
// + copy: manifest.${variant}.json → dist/${variant}/manifest.json
// + copy: styles.${variant}.css → dist/${variant}/styles.css
// src/themes/variant.ts
declare const __KURO_VARIANT__: 'v1' | 'v2';
import { V1 } from './v1';
import { V2 } from './v2';
export const THEME = __KURO_VARIANT__ === 'v2' ? V2 : V1;
// src/main.ts (Auszug)
import { THEME } from './themes/variant';
export default class Plugin extends Plugin {
async onload() {
// ...
if (THEME.hasAspects) {
this.setupAspectChip(); // v2-only path
}
}
}
Migrations-Reihenfolge¶
- Auf Feature-Branch
feat/v1-v2-mergearbeiten. - Theme-Variant Interface + V1/V2 Module anlegen.
- legacy.ts splitten: gemeinsamer Code bleibt, Theme-spezifisches in
themes/v1.tsundthemes/v2.ts. - main.ts: Aspect-Setup hinter
THEME.hasAspects-Guard. - settings/tab.ts: Aspect-Picker hinter Guard.
- styles.css → styles.v1.css + styles.v2.css.
- manifest.v1.json + manifest.v2.json anlegen.
- esbuild.config.mjs anpassen für Variant-Build.
- Beide Builds testen (Output ≡ aktuelle v1- und v2-Dist).
- Branch mergen.
kuro2-theme-settings/ins Attic.
Verifikations-Kriterien¶
KURO_THEME=v1 npm run buildproduziertdist/v1/main.js+manifest.json+styles.css— funktional identisch zu aktuellem v1-Output.KURO_THEME=v2 npm run buildproduziert dasselbe für v2 (inkl. Aspect-Chip).- Beide Plugins lassen sich parallel in Vaults installieren (unterschiedliche manifest-ids).
- Kein Code-Block in src/screensaver/ angefasst (Engine bleibt unverändert).
Out-of-Scope für diesen Refactor¶
- Engine als externes pnpm-Workspace-Package extrahieren (= übernächste Iteration).
- v1+v2 Theme-Files (
Kuro/) konsolidieren (= separater Refactor, Themes sind nicht Plugin). - kuro-gamification Source-Reconstruction (= separate Aufgabe).
Outcome (2026-05-26)¶
Umgesetzte Variante: Dual-Legacy statt unified ThemeVariant-Hooks.
Beim Reverse-Engineering der Plugin-Klassen stellte sich heraus, dass der Aspect-Code in v2 nicht als Block-Erweiterung lebt, sondern tief in KuroThemeSettingsPlugin verzahnt ist (~349 Zeilen verteilt über _resolveActiveAspect, _syncAspectBg, _isAspectPinnedByFrontmatter, Hanko-Renderer, DEFAULT_SETTINGS, …). Ein Hook-Refactor wäre stundenlang gewesen.
Pragmatische Lösung: getrennte File-Versionen, gemeinsames Repo + Build.
src/main.{v1,v2}.ts,src/legacy.{v1,v2}.ts,src/settings/tab.{v1,v2}.ts,styles.{v1,v2}.css,manifest.{v1,v2}.json— getrennte Quellfilessrc/themes/presets.ts— gemeinsame Color-Presets (war früher in beiden legacys dupliziert)src/screensaver/,src/settings/tab-screensaver.ts— komplett unverändert, geteilt- esbuild liest
process.env.KURO_THEME, wählt entry-point und kopiert variant-spezifische Assets - Scripts:
npm run build:v1,build:v2,build:all; analogsync:v1,sync:v2
Was damit gewonnen ist:
- 1 Repo statt 2 (kuro2-theme-settings → _attic/)
- Gemeinsame src/screensaver/-Engine — wenn dort etwas gefixt wird, profitieren beide Varianten
- Gemeinsame Presets
- Klare Build-Wege, keine versteckte Drift mehr
Was bewusst NICHT gemacht ist:
- legacy.v1.ts und legacy.v2.ts haben ~95% identischen Plugin-Klassen-Code. Ein vollständiger Merge mit ThemeVariant-Boundary bleibt als Follow-up. Realistisch ein 4-8h-Refactor wenn das Mal nötig wird.
Verifikation:
- KURO_THEME=v1 npm run build → _kuro-theme-settings/main.js (650 KB), enthält keine Aspect-Symbole.
- KURO_THEME=v2 npm run build → _kuro2-theme-settings/main.js (663 KB), Aspect-Code enthalten.
- Manifest-IDs korrekt getrennt.
- Branch feat/v1-v2-merge → main gemerged (commit 1777aac).
Outcome Phase 2 (2026-05-26 spätabends)¶
Der Follow-up wurde doch noch eingelöst: unified legacy.ts via Build-Time-Defines.
Statt Subclass-Pattern (initial geplant) wurde ein pragmatischer Build-Time-Replace eingesetzt:
// In legacy.ts:
declare const __STYLE_TAG_ID__: string; // ersetzt durch esbuild
declare const __BODY_CLASS__: string; // ersetzt durch esbuild
declare const __HAS_ASPECTS__: boolean; // ersetzt durch esbuild
const HAS_ASPECTS = __HAS_ASPECTS__;
Esbuild's define-Feature ersetzt die __XXX__-Identifier zur Build-Zeit pro Variant:
- v1: __HAS_ASPECTS__='false', __STYLE_TAG_ID__='_kuro-overrides', __BODY_CLASS__='_kuro-active'
- v2: __HAS_ASPECTS__='true', __STYLE_TAG_ID__='_kuro2-overrides', __BODY_CLASS__='_kuro2-active'
In apply() werden Aspect-bezogene Calls hinter if (HAS_ASPECTS) ... geguarded. Esbuild eliminiert die Branches als Dead Code im v1-Bundle.
Code-Reduktion: git diff --stat zeigt -1368 Zeilen netto (legacy.v1.ts gelöscht, legacy.v2.ts → legacy.ts renamed).
Was übrig bleibt unverändert:
- main.v1.ts und main.v2.ts bleiben getrennt (v2 hat den Statusbar-Chip-Setup), aber importieren aus dem unified legacy.ts.
- settings/tab.v1.ts und tab.v2.ts bleiben getrennt (v2 hat den Aspect-Picker), beide aus unified legacy.ts.
Bundle-Footprint: - v1: 650 → 661 KB (+11 KB für nicht-tree-shakebare Aspect-Methoden auf der Plugin-Klasse). Funktional korrekt, akzeptabel. - v2: 663 KB unverändert.
Verbessert: Wenn ein Bug in der Plugin-Klassen-Logik gefixt wird, profitieren ab sofort beide Varianten ohne Duplikat-Edit.
Merge-Commit: 1a11d5f.