mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
feat(theme-registry): bootstrap with 25 existing + 12 new themes
Registers all 37 editor themes at startup via ThemeRegistry.bootstrap.js. Side-effect module: requiring it populates the registry from a static THEMES array (25 existing menu themes refactored into the registry + 12 new: Catppuccin x4, one-light, tokyo-night-storm, synthwave-84, outrun, winter-is-coming x2, solarized-dark-hc, spring-light). Categories split: 13 light / 22 dark / 1 high-contrast / 1 seasonal (spring-light per spec). Snapshot test asserts exact id order, shape validity, and category counts. Tests use jest.resetModules() + per-test requires so the bootstrap module re-evaluates its registration loop each run. Amit Haridas
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Editor Theme Registry bootstrap — registers all shipped themes at startup.
|
||||||
|
*
|
||||||
|
* Importing this module has the side effect of populating the registry.
|
||||||
|
* `src/main.js` requires this once during startup. Test files require it
|
||||||
|
* after `ThemeRegistry.clear()` to assert the snapshot.
|
||||||
|
*
|
||||||
|
* Theme ids are kebab-case. Categories: 'light' | 'dark' | 'high-contrast'
|
||||||
|
* | 'seasonal'. `isDark` is independent of category — see spec note on
|
||||||
|
* winter-is-coming-light (light-category, dark-not-required) and the future
|
||||||
|
* seasonal-with-dark possibility.
|
||||||
|
*
|
||||||
|
* @module ThemeRegistry.bootstrap
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ThemeRegistry = require('./ThemeRegistry');
|
||||||
|
|
||||||
|
const THEMES = [
|
||||||
|
// ---- 25 existing menu themes (refactor — no behavioural change) ──
|
||||||
|
// Light
|
||||||
|
{ id: 'atomonelight', label: 'Atom One Light (Default)', category: 'light', isDark: false },
|
||||||
|
{ id: 'github', label: 'GitHub Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'light', label: 'Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'solarized', label: 'Solarized Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'gruvbox-light', label: 'Gruvbox Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'ayu-light', label: 'Ayu Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'sepia', label: 'Sepia', category: 'light', isDark: false },
|
||||||
|
{ id: 'paper', label: 'Paper', category: 'light', isDark: false },
|
||||||
|
{ id: 'rosepine-dawn', label: 'Rose Pine Dawn', category: 'light', isDark: false },
|
||||||
|
{ id: 'concrete-light', label: 'Concrete Light', category: 'light', isDark: false },
|
||||||
|
// Dark
|
||||||
|
{ id: 'dark', label: 'Dark', category: 'dark', isDark: true },
|
||||||
|
{ id: 'onedark', label: 'One Dark', category: 'dark', isDark: true },
|
||||||
|
{ id: 'dracula', label: 'Dracula', category: 'dark', isDark: true },
|
||||||
|
{ id: 'nord', label: 'Nord', category: 'dark', isDark: true },
|
||||||
|
{ id: 'monokai', label: 'Monokai', category: 'dark', isDark: true },
|
||||||
|
{ id: 'material', label: 'Material', category: 'dark', isDark: true },
|
||||||
|
{ id: 'gruvbox-dark', label: 'Gruvbox Dark', category: 'dark', isDark: true },
|
||||||
|
{ id: 'tokyonight', label: 'Tokyo Night', category: 'dark', isDark: true },
|
||||||
|
{ id: 'palenight', label: 'Palenight', category: 'dark', isDark: true },
|
||||||
|
{ id: 'ayu-dark', label: 'Ayu Dark', category: 'dark', isDark: true },
|
||||||
|
{ id: 'ayu-mirage', label: 'Ayu Mirage', category: 'dark', isDark: true },
|
||||||
|
{ id: 'oceanic-next', label: 'Oceanic Next', category: 'dark', isDark: true },
|
||||||
|
{ id: 'cobalt2', label: 'Cobalt2', category: 'dark', isDark: true },
|
||||||
|
{ id: 'concrete-dark', label: 'Concrete Dark', category: 'dark', isDark: true },
|
||||||
|
{ id: 'concrete-warm', label: 'Concrete Warm', category: 'dark', isDark: true },
|
||||||
|
// ---- 12 new themes ──
|
||||||
|
// Catppuccin (4)
|
||||||
|
{ id: 'catppuccin-latte', label: 'Catppuccin Latte', category: 'light', isDark: false },
|
||||||
|
{ id: 'catppuccin-frappe', label: 'Catppuccin Frappé', category: 'dark', isDark: true },
|
||||||
|
{ id: 'catppuccin-macchiato', label: 'Catppuccin Macchiato', category: 'dark', isDark: true },
|
||||||
|
{ id: 'catppuccin-mocha', label: 'Catppuccin Mocha', category: 'dark', isDark: true },
|
||||||
|
// One Light + Tokyo Night Storm
|
||||||
|
{ id: 'one-light', label: 'One Light', category: 'light', isDark: false },
|
||||||
|
{ id: 'tokyo-night-storm', label: 'Tokyo Night Storm', category: 'dark', isDark: true },
|
||||||
|
// Synthwave / Outrun
|
||||||
|
{ id: 'synthwave-84', label: "Synthwave '84", category: 'dark', isDark: true },
|
||||||
|
{ id: 'outrun', label: 'Outrun', category: 'dark', isDark: true },
|
||||||
|
// Winter is Coming (light + dark)
|
||||||
|
{
|
||||||
|
id: 'winter-is-coming-light',
|
||||||
|
label: 'Winter is Coming (Light)',
|
||||||
|
category: 'light',
|
||||||
|
isDark: false,
|
||||||
|
},
|
||||||
|
{ id: 'winter-is-coming-dark', label: 'Winter is Coming (Dark)', category: 'dark', isDark: true },
|
||||||
|
// Solarized HC + Spring seasonal
|
||||||
|
{
|
||||||
|
id: 'solarized-dark-hc',
|
||||||
|
label: 'Solarized Dark (High Contrast)',
|
||||||
|
category: 'high-contrast',
|
||||||
|
isDark: true,
|
||||||
|
},
|
||||||
|
{ id: 'spring-light', label: 'Spring Light', category: 'seasonal', isDark: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const t of THEMES) ThemeRegistry.register(t);
|
||||||
|
|
||||||
|
module.exports = { THEMES };
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment node
|
||||||
|
*
|
||||||
|
* Bootstrap snapshot — at startup, ThemeRegistry.list() returns exactly 37
|
||||||
|
* themes (25 existing menu themes + 12 new) in the expected order.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EXPECTED_IDS = [
|
||||||
|
// Existing — light
|
||||||
|
'atomonelight',
|
||||||
|
'github',
|
||||||
|
'light',
|
||||||
|
'solarized',
|
||||||
|
'gruvbox-light',
|
||||||
|
'ayu-light',
|
||||||
|
'sepia',
|
||||||
|
'paper',
|
||||||
|
'rosepine-dawn',
|
||||||
|
'concrete-light',
|
||||||
|
// Existing — dark
|
||||||
|
'dark',
|
||||||
|
'onedark',
|
||||||
|
'dracula',
|
||||||
|
'nord',
|
||||||
|
'monokai',
|
||||||
|
'material',
|
||||||
|
'gruvbox-dark',
|
||||||
|
'tokyonight',
|
||||||
|
'palenight',
|
||||||
|
'ayu-dark',
|
||||||
|
'ayu-mirage',
|
||||||
|
'oceanic-next',
|
||||||
|
'cobalt2',
|
||||||
|
'concrete-dark',
|
||||||
|
'concrete-warm',
|
||||||
|
// New — Catppuccin
|
||||||
|
'catppuccin-latte',
|
||||||
|
'catppuccin-frappe',
|
||||||
|
'catppuccin-macchiato',
|
||||||
|
'catppuccin-mocha',
|
||||||
|
// New — One Light + Tokyo Night Storm
|
||||||
|
'one-light',
|
||||||
|
'tokyo-night-storm',
|
||||||
|
// New — Synthwave / Outrun
|
||||||
|
'synthwave-84',
|
||||||
|
'outrun',
|
||||||
|
// New — Winter is Coming
|
||||||
|
'winter-is-coming-light',
|
||||||
|
'winter-is-coming-dark',
|
||||||
|
// New — Solarized HC + Spring seasonal
|
||||||
|
'solarized-dark-hc',
|
||||||
|
'spring-light',
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('ThemeRegistry.bootstrap', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset module registry so both ThemeRegistry and bootstrap re-load fresh.
|
||||||
|
// Otherwise Jest's module cache returns the already-evaluated bootstrap
|
||||||
|
// (whose registration loop has already run once), leaving list() empty
|
||||||
|
// across subsequent tests.
|
||||||
|
jest.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('registers exactly 37 themes in the expected order', () => {
|
||||||
|
const ThemeRegistry = require('../../src/main/ThemeRegistry');
|
||||||
|
ThemeRegistry.clear();
|
||||||
|
require('../../src/main/ThemeRegistry.bootstrap');
|
||||||
|
expect(ThemeRegistry.list().map((t) => t.id)).toEqual(EXPECTED_IDS);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('every theme has a valid shape', () => {
|
||||||
|
const ThemeRegistry = require('../../src/main/ThemeRegistry');
|
||||||
|
ThemeRegistry.clear();
|
||||||
|
require('../../src/main/ThemeRegistry.bootstrap');
|
||||||
|
for (const t of ThemeRegistry.list()) {
|
||||||
|
expect(typeof t.id).toBe('string');
|
||||||
|
expect(typeof t.label).toBe('string');
|
||||||
|
expect(['light', 'dark', 'high-contrast', 'seasonal']).toContain(t.category);
|
||||||
|
expect(typeof t.isDark).toBe('boolean');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('expected category counts', () => {
|
||||||
|
const ThemeRegistry = require('../../src/main/ThemeRegistry');
|
||||||
|
ThemeRegistry.clear();
|
||||||
|
require('../../src/main/ThemeRegistry.bootstrap');
|
||||||
|
const counts = ThemeRegistry.list().reduce((acc, t) => {
|
||||||
|
acc[t.category] = (acc[t.category] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
// 10 existing light + one-light + winter-is-coming-light + catppuccin-latte = 13 light (spring-light is category=seasonal)
|
||||||
|
expect(counts.light).toBe(13);
|
||||||
|
// 15 existing dark + 3 catppuccin dark + tokyo-night-storm + synthwave-84 + outrun + winter-is-coming-dark = 22 dark
|
||||||
|
expect(counts.dark).toBe(22);
|
||||||
|
expect(counts['high-contrast']).toBe(1);
|
||||||
|
// 1: spring-light (the only seasonal theme in v1)
|
||||||
|
expect(counts.seasonal).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user