Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 25x 25x 25x 25x 600x 600x 25x 25x 225x 25x 25x 225x 225x 225x 600x 25x 600x 25x 610x 610x 601x 601x 600x 600x 600x 50x 50x 550x 550x 550x 550x 550x 550x 550x 600x | // SPDX-License-Identifier: MIT
/**
* Consolidated theme metadata derived from tokens.json.
*
* All display metadata (names, appearances, vendor groups) is computed from
* the canonical token data at module evaluation time. Consumers should import
* from here instead of maintaining their own hardcoded copies.
*/
import type { ThemeFlavor } from '../themes/types.js';
import { flavors, themeIds, packages } from '../tokens/index.js';
/** Default theme applied when no preference is stored. */
export const DEFAULT_THEME = 'catppuccin-mocha' as const;
/** All valid theme IDs (re-export for convenience). */
export const VALID_THEMES: readonly string[] = themeIds;
/** Set of valid theme IDs for O(1) lookup. */
export const VALID_THEME_SET: ReadonlySet<string> = /*#__PURE__*/ new Set(themeIds);
/** Full human-readable label for each theme (e.g., "Catppuccin Mocha"). */
export const THEME_NAMES: Readonly<Record<string, string>> = /*#__PURE__*/ Object.fromEntries(
flavors.map((f) => [f.id, f.label]),
);
/** Appearance (light/dark) for each theme. */
export const THEME_APPEARANCES: Readonly<Record<string, 'light' | 'dark'>> =
/*#__PURE__*/ Object.fromEntries(flavors.map((f) => [f.id, f.appearance]));
/**
* Ordered list of vendor IDs controlling display order in dropdowns.
* New vendors should be appended here.
*/
export const VENDOR_ORDER: readonly string[] = [
'catppuccin',
'dracula',
'gruvbox',
'github',
'bulma',
'nord',
'solarized',
'rose-pine',
'tokyo-night',
];
/** A vendor group with its display name and ordered theme IDs. */
export interface VendorGroup {
id: string;
displayName: string;
themeIds: string[];
}
/**
* Ordered array of vendor groups derived from packages + VENDOR_ORDER.
* The "(synced)" suffix is stripped from display names.
*/
// Dev-time validation: warn if packages has vendors not listed in VENDOR_ORDER
const _packageKeys = Object.keys(packages);
const _missingFromOrder = _packageKeys.filter((id) => !VENDOR_ORDER.includes(id));
Iif (_missingFromOrder.length > 0) {
console.warn(
`[metadata] VENDOR_ORDER is missing vendor IDs present in packages: ${_missingFromOrder.join(', ')}. ` +
'Append them to VENDOR_ORDER so their themes are included in VENDOR_GROUPS.',
);
}
export const VENDOR_GROUPS: readonly VendorGroup[] = /*#__PURE__*/ VENDOR_ORDER.filter(
(id) => id in packages,
).map((id) => {
const pkg = packages[id]!;
return {
id,
displayName: pkg.name.replace(/\s*\(synced\)\s*/i, ''),
themeIds: pkg.flavors.flatMap((f) => (f ? [f.id] : [])),
};
});
/** O(1) flavor lookup by ID, built once at module evaluation time. */
const flavorById: ReadonlyMap<string, ThemeFlavor> = /*#__PURE__*/ new Map(
flavors.map((f) => [f.id, f]),
);
/** Cache for computed short labels. */
const shortLabelCache = new Map<string, string>();
/**
* Strip the vendor prefix from a theme label to produce a short label.
*
* For single-flavor vendors (e.g., "Dracula", "Nord") the full label is returned.
* For multi-flavor vendors the vendor name prefix is stripped:
* "Catppuccin Mocha" -> "Mocha"
* "Gruvbox Dark Hard" -> "Dark Hard"
*
* @param themeId - A valid theme ID
* @returns The short display label, or the full label if the theme is not found
*/
export function getShortLabel(themeId: string): string {
const cached = shortLabelCache.get(themeId);
if (cached !== undefined) return cached;
const flavor = flavorById.get(themeId);
if (!flavor) return themeId;
const vendorPkg = packages[flavor.vendor];
Iif (!vendorPkg) {
shortLabelCache.set(themeId, flavor.label);
return flavor.label;
}
// Single-flavor vendors keep their full label
if (vendorPkg.flavors.length === 1) {
shortLabelCache.set(themeId, flavor.label);
return flavor.label;
}
// Strip vendor display name prefix (case-insensitive, NFC-normalized for
// consistent handling of composed/decomposed Unicode accents).
const displayName = vendorPkg.name.replace(/\s*\(synced\)\s*/i, '').normalize('NFC');
const label = flavor.label;
let result: string;
if (label.normalize('NFC').toLowerCase().startsWith(displayName.toLowerCase())) {
const stripped = label.slice(displayName.length).trim();
result = stripped || label;
} else E{
result = label;
}
shortLabelCache.set(themeId, result);
return result;
}
/** Pre-computed short labels for all themes. */
export const THEME_SHORT_LABELS: Readonly<Record<string, string>> =
/*#__PURE__*/ Object.fromEntries(flavors.map((f) => [f.id, getShortLabel(f.id)]));
|