@deneb-ui/core 2.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/engine/cssVariables.d.ts +8 -0
- package/dist/engine/cssVariables.js +186 -0
- package/dist/engine/deepMerge.d.ts +2 -0
- package/dist/engine/deepMerge.js +19 -0
- package/dist/engine/domPatcher.d.ts +6 -0
- package/dist/engine/domPatcher.js +54 -0
- package/dist/engine/tokenResolver.d.ts +3 -0
- package/dist/engine/tokenResolver.js +35 -0
- package/dist/fonts/clamp.d.ts +8 -0
- package/dist/fonts/clamp.js +47 -0
- package/dist/fonts/google-url.d.ts +1 -0
- package/dist/fonts/google-url.js +41 -0
- package/dist/fonts/index.d.ts +5 -0
- package/dist/fonts/index.js +21 -0
- package/dist/fonts/installProject.d.ts +25 -0
- package/dist/fonts/installProject.js +273 -0
- package/dist/fonts/registry.d.ts +7 -0
- package/dist/fonts/registry.js +138 -0
- package/dist/fonts/resolve.d.ts +7 -0
- package/dist/fonts/resolve.js +117 -0
- package/dist/fonts/types.d.ts +36 -0
- package/dist/fonts/types.js +2 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +25 -0
- package/dist/types/markers.d.ts +7 -0
- package/dist/types/markers.js +31 -0
- package/dist/types/protocol.d.ts +10 -0
- package/dist/types/protocol.js +19 -0
- package/dist/types/styles.d.ts +90 -0
- package/dist/types/styles.js +2 -0
- package/dist/validation/styleSchemaValidator.d.ts +8 -0
- package/dist/validation/styleSchemaValidator.js +44 -0
- package/package.json +52 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FONTS_CSS_IMPORT = exports.FONTS_CSS_REL = exports.FONTS_MANIFEST_REL = void 0;
|
|
7
|
+
exports.findSiteDataPath = findSiteDataPath;
|
|
8
|
+
exports.findHostProject = findHostProject;
|
|
9
|
+
exports.generateFontsCss = generateFontsCss;
|
|
10
|
+
exports.installProjectFonts = installProjectFonts;
|
|
11
|
+
exports.installFontsForHostOf = installFontsForHostOf;
|
|
12
|
+
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const registry_1 = require("./registry");
|
|
16
|
+
const resolve_1 = require("./resolve");
|
|
17
|
+
exports.FONTS_MANIFEST_REL = '.deneb/fonts.json';
|
|
18
|
+
exports.FONTS_CSS_REL = 'src/fonts/deneb-fonts.css';
|
|
19
|
+
exports.FONTS_CSS_IMPORT = './fonts/deneb-fonts.css';
|
|
20
|
+
const SKIP_HOST_NAMES = new Set([
|
|
21
|
+
'@deneb-ui/ui',
|
|
22
|
+
'@deneb-ui/core',
|
|
23
|
+
'@deneb-ui/cli',
|
|
24
|
+
'@deneb-ui/create-template',
|
|
25
|
+
'deneb-ui-framework-monorepo',
|
|
26
|
+
]);
|
|
27
|
+
function log(quiet, message) {
|
|
28
|
+
if (!quiet)
|
|
29
|
+
console.log(message);
|
|
30
|
+
}
|
|
31
|
+
function readJsonSafe(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(node_fs_1.default.readFileSync(filePath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function findSiteDataPath(projectDir) {
|
|
40
|
+
const candidates = [
|
|
41
|
+
node_path_1.default.join(projectDir, 'src', 'data', 'site-data.json'),
|
|
42
|
+
node_path_1.default.join(projectDir, 'site-data.json'),
|
|
43
|
+
node_path_1.default.join(projectDir, 'data', 'site-data.json'),
|
|
44
|
+
];
|
|
45
|
+
return candidates.find((candidate) => node_fs_1.default.existsSync(candidate)) ?? null;
|
|
46
|
+
}
|
|
47
|
+
function findHostProject(startDir) {
|
|
48
|
+
let dir = node_path_1.default.resolve(startDir);
|
|
49
|
+
for (let i = 0; i < 12; i++) {
|
|
50
|
+
const pkgPath = node_path_1.default.join(dir, 'package.json');
|
|
51
|
+
if (node_fs_1.default.existsSync(pkgPath)) {
|
|
52
|
+
const pkg = readJsonSafe(pkgPath);
|
|
53
|
+
const name = typeof pkg?.name === 'string' ? pkg.name : '';
|
|
54
|
+
if (!SKIP_HOST_NAMES.has(name)) {
|
|
55
|
+
const deps = {
|
|
56
|
+
...(pkg?.dependencies ?? {}),
|
|
57
|
+
...(pkg?.devDependencies ?? {}),
|
|
58
|
+
};
|
|
59
|
+
if (deps['@deneb-ui/ui'] || deps.next || deps.react) {
|
|
60
|
+
return dir;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const parent = node_path_1.default.dirname(dir);
|
|
65
|
+
if (parent === dir)
|
|
66
|
+
break;
|
|
67
|
+
dir = parent;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function detectPackageManager(projectDir) {
|
|
72
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(projectDir, 'pnpm-lock.yaml')))
|
|
73
|
+
return 'pnpm';
|
|
74
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(projectDir, 'yarn.lock')))
|
|
75
|
+
return 'yarn';
|
|
76
|
+
return 'npm';
|
|
77
|
+
}
|
|
78
|
+
function buildFontsourceCssEntries(font) {
|
|
79
|
+
const pkg = font.fontsourcePackage;
|
|
80
|
+
if (!pkg)
|
|
81
|
+
return [];
|
|
82
|
+
if (font.variable) {
|
|
83
|
+
return [`@import '@fontsource/${pkg}/index.css';`];
|
|
84
|
+
}
|
|
85
|
+
const weights = font.weights.length > 0 ? font.weights : [400];
|
|
86
|
+
const lines = [];
|
|
87
|
+
for (const weight of weights) {
|
|
88
|
+
lines.push(`@import '@fontsource/${pkg}/${weight}.css';`);
|
|
89
|
+
if (font.italic) {
|
|
90
|
+
lines.push(`@import '@fontsource/${pkg}/${weight}-italic.css';`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return lines;
|
|
94
|
+
}
|
|
95
|
+
function generateFontsCss(fonts) {
|
|
96
|
+
const lines = [
|
|
97
|
+
'/*',
|
|
98
|
+
' * DENEB Fonts — self-hosted via @fontsource',
|
|
99
|
+
' * Generated by: deneb fonts install / @deneb-ui/ui postinstall',
|
|
100
|
+
' */',
|
|
101
|
+
'',
|
|
102
|
+
':root { --deneb-fonts-selfhosted: 1; }',
|
|
103
|
+
'',
|
|
104
|
+
];
|
|
105
|
+
for (const font of fonts) {
|
|
106
|
+
lines.push(`/* ${font.label} (${font.id}) */`);
|
|
107
|
+
lines.push(...buildFontsourceCssEntries(font));
|
|
108
|
+
lines.push('');
|
|
109
|
+
}
|
|
110
|
+
return `${lines.join('\n').trim()}\n`;
|
|
111
|
+
}
|
|
112
|
+
function patchLayoutImport(projectDir) {
|
|
113
|
+
const layoutCandidates = [
|
|
114
|
+
node_path_1.default.join(projectDir, 'src', 'app', 'layout.tsx'),
|
|
115
|
+
node_path_1.default.join(projectDir, 'src', 'app', 'layout.jsx'),
|
|
116
|
+
node_path_1.default.join(projectDir, 'app', 'layout.tsx'),
|
|
117
|
+
node_path_1.default.join(projectDir, 'app', 'layout.jsx'),
|
|
118
|
+
];
|
|
119
|
+
const layoutPath = layoutCandidates.find((candidate) => node_fs_1.default.existsSync(candidate));
|
|
120
|
+
if (!layoutPath)
|
|
121
|
+
return { patched: false, reason: 'layout.tsx not found' };
|
|
122
|
+
let source = node_fs_1.default.readFileSync(layoutPath, 'utf8');
|
|
123
|
+
if (source.includes(exports.FONTS_CSS_IMPORT) || source.includes('deneb-fonts.css')) {
|
|
124
|
+
return { patched: false, reason: 'already imported', layoutPath };
|
|
125
|
+
}
|
|
126
|
+
const importLine = `import '${exports.FONTS_CSS_IMPORT}';`;
|
|
127
|
+
if (source.includes("import './globals.css'")) {
|
|
128
|
+
source = source.replace("import './globals.css';", `import './globals.css';\n${importLine}`);
|
|
129
|
+
}
|
|
130
|
+
else if (source.includes('import "./globals.css"')) {
|
|
131
|
+
source = source.replace('import "./globals.css";', `import "./globals.css";\n${importLine}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
source = `${importLine}\n${source}`;
|
|
135
|
+
}
|
|
136
|
+
node_fs_1.default.writeFileSync(layoutPath, source, 'utf8');
|
|
137
|
+
return { patched: true, reason: 'patched', layoutPath };
|
|
138
|
+
}
|
|
139
|
+
function resolveInstallableList(projectDir, options) {
|
|
140
|
+
const warnings = [];
|
|
141
|
+
const ids = new Set();
|
|
142
|
+
if (options.all) {
|
|
143
|
+
for (const font of registry_1.DENEB_FONT_REGISTRY) {
|
|
144
|
+
if (font.fontsourcePackage)
|
|
145
|
+
ids.add(font.id);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (options.fontIds && options.fontIds.length > 0) {
|
|
149
|
+
for (const id of options.fontIds)
|
|
150
|
+
ids.add((0, resolve_1.normalizeFontId)(id));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const siteDataPath = findSiteDataPath(projectDir);
|
|
154
|
+
if (siteDataPath) {
|
|
155
|
+
const siteData = readJsonSafe(siteDataPath) ?? {};
|
|
156
|
+
for (const id of (0, resolve_1.collectFontIdsFromSiteData)(siteData))
|
|
157
|
+
ids.add(id);
|
|
158
|
+
}
|
|
159
|
+
if (ids.size === 0) {
|
|
160
|
+
for (const id of registry_1.DEFAULT_PROJECT_FONT_IDS)
|
|
161
|
+
ids.add(id);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const fonts = [];
|
|
165
|
+
for (const id of ids) {
|
|
166
|
+
const font = (0, resolve_1.resolveInstallableFont)(id) ?? (0, resolve_1.lookupFontDefinition)(id);
|
|
167
|
+
if (!font?.fontsourcePackage) {
|
|
168
|
+
warnings.push(`Skipped "${id}" — not available on Google Fonts / @fontsource`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (!fonts.find((entry) => entry.id === font.id))
|
|
172
|
+
fonts.push(font);
|
|
173
|
+
}
|
|
174
|
+
return { fonts, warnings };
|
|
175
|
+
}
|
|
176
|
+
function packagesAlreadyPresent(projectDir, packages) {
|
|
177
|
+
const pkg = readJsonSafe(node_path_1.default.join(projectDir, 'package.json'));
|
|
178
|
+
const deps = {
|
|
179
|
+
...(pkg?.dependencies ?? {}),
|
|
180
|
+
...(pkg?.devDependencies ?? {}),
|
|
181
|
+
};
|
|
182
|
+
return packages.every((name) => Boolean(deps[name]));
|
|
183
|
+
}
|
|
184
|
+
function installPackages(projectDir, packages, quiet) {
|
|
185
|
+
if (packages.length === 0)
|
|
186
|
+
return;
|
|
187
|
+
if (packagesAlreadyPresent(projectDir, packages))
|
|
188
|
+
return;
|
|
189
|
+
const pm = detectPackageManager(projectDir);
|
|
190
|
+
const args = pm === 'pnpm'
|
|
191
|
+
? ['add', ...packages]
|
|
192
|
+
: pm === 'yarn'
|
|
193
|
+
? ['add', ...packages]
|
|
194
|
+
: ['install', ...packages, '--save'];
|
|
195
|
+
const res = (0, node_child_process_1.spawnSync)(pm, args, {
|
|
196
|
+
cwd: projectDir,
|
|
197
|
+
stdio: quiet ? 'pipe' : 'inherit',
|
|
198
|
+
shell: true,
|
|
199
|
+
env: { ...process.env, DENEB_SKIP_FONTS: '1' },
|
|
200
|
+
});
|
|
201
|
+
if (res.status !== 0) {
|
|
202
|
+
throw new Error(`${pm} install failed for font packages`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function installProjectFonts(options) {
|
|
206
|
+
const projectDir = node_path_1.default.resolve(options.projectDir);
|
|
207
|
+
const warnings = [];
|
|
208
|
+
if (process.env.DENEB_SKIP_FONTS === '1') {
|
|
209
|
+
return { ok: true, skipped: 'DENEB_SKIP_FONTS=1', installed: [], packages: [], warnings };
|
|
210
|
+
}
|
|
211
|
+
if (!node_fs_1.default.existsSync(projectDir)) {
|
|
212
|
+
return { ok: false, skipped: 'directory not found', installed: [], packages: [], warnings };
|
|
213
|
+
}
|
|
214
|
+
const { fonts, warnings: resolveWarnings } = resolveInstallableList(projectDir, options);
|
|
215
|
+
warnings.push(...resolveWarnings);
|
|
216
|
+
if (fonts.length === 0) {
|
|
217
|
+
return { ok: false, installed: [], packages: [], warnings };
|
|
218
|
+
}
|
|
219
|
+
const packages = fonts.map((font) => `@fontsource/${font.fontsourcePackage}`);
|
|
220
|
+
log(options.quiet, `\nInstalling ${fonts.length} DENEB font package(s) into ${projectDir}\n`);
|
|
221
|
+
installPackages(projectDir, packages, options.quiet);
|
|
222
|
+
const cssPath = node_path_1.default.join(projectDir, exports.FONTS_CSS_REL);
|
|
223
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(cssPath), { recursive: true });
|
|
224
|
+
node_fs_1.default.writeFileSync(cssPath, generateFontsCss(fonts), 'utf8');
|
|
225
|
+
const manifestPath = node_path_1.default.join(projectDir, exports.FONTS_MANIFEST_REL);
|
|
226
|
+
const manifest = {
|
|
227
|
+
version: 1,
|
|
228
|
+
installedAt: new Date().toISOString(),
|
|
229
|
+
fonts: fonts.map((font) => ({
|
|
230
|
+
id: font.id,
|
|
231
|
+
fontsourcePackage: font.fontsourcePackage,
|
|
232
|
+
weights: font.weights,
|
|
233
|
+
variable: font.variable,
|
|
234
|
+
})),
|
|
235
|
+
};
|
|
236
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(manifestPath), { recursive: true });
|
|
237
|
+
node_fs_1.default.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
238
|
+
const layout = patchLayoutImport(projectDir);
|
|
239
|
+
if (!options.quiet) {
|
|
240
|
+
for (const warning of warnings)
|
|
241
|
+
console.warn(`⚠ ${warning}`);
|
|
242
|
+
console.log(`✔ Wrote ${exports.FONTS_CSS_REL}`);
|
|
243
|
+
console.log(`✔ Wrote ${exports.FONTS_MANIFEST_REL}`);
|
|
244
|
+
if (layout.patched) {
|
|
245
|
+
console.log(`✔ Patched layout font import`);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
console.log(`ℹ Add to layout: import '${exports.FONTS_CSS_IMPORT}'; (${layout.reason})`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
ok: true,
|
|
253
|
+
installed: fonts.map((font) => font.id),
|
|
254
|
+
packages,
|
|
255
|
+
cssPath,
|
|
256
|
+
manifestPath,
|
|
257
|
+
layoutPatched: layout.patched,
|
|
258
|
+
warnings,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function installFontsForHostOf(packageDir) {
|
|
262
|
+
const host = findHostProject(packageDir);
|
|
263
|
+
if (!host) {
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
skipped: 'no host Next/React app found',
|
|
267
|
+
installed: [],
|
|
268
|
+
packages: [],
|
|
269
|
+
warnings: [],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return installProjectFonts({ projectDir: host, quiet: true });
|
|
273
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DenebFontDefinition } from './types';
|
|
2
|
+
/** Curated catalog — 54 web-friendly faces from [Google Fonts](https://fonts.google.com/) */
|
|
3
|
+
export declare const DENEB_FONT_REGISTRY: DenebFontDefinition[];
|
|
4
|
+
export declare const DENEB_FONT_BY_ID: Map<string, DenebFontDefinition>;
|
|
5
|
+
export declare const DENEB_GOOGLE_FONT_COUNT: number;
|
|
6
|
+
/** Default self-hosted set when a project has no site-data font choices yet. */
|
|
7
|
+
export declare const DEFAULT_PROJECT_FONT_IDS: readonly ["inter", "plus-jakarta-sans", "playfair-display"];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PROJECT_FONT_IDS = exports.DENEB_GOOGLE_FONT_COUNT = exports.DENEB_FONT_BY_ID = exports.DENEB_FONT_REGISTRY = void 0;
|
|
4
|
+
const W = [400, 500, 600, 700];
|
|
5
|
+
const W_DISPLAY = [400, 700];
|
|
6
|
+
const W_MONO = [400, 500, 600, 700];
|
|
7
|
+
function googleFont(id, label, category, fontsourcePackage, googleQuery, options) {
|
|
8
|
+
const family = label;
|
|
9
|
+
return {
|
|
10
|
+
id,
|
|
11
|
+
label,
|
|
12
|
+
category,
|
|
13
|
+
family,
|
|
14
|
+
cssStack: options?.cssStack ?? `'${family}', ${category === 'serif' || category === 'slab-serif' ? 'Georgia, serif' : category === 'monospace' ? 'ui-monospace, monospace' : 'system-ui, sans-serif'}`,
|
|
15
|
+
fontsourcePackage,
|
|
16
|
+
googleQuery,
|
|
17
|
+
googleFonts: true,
|
|
18
|
+
weights: options?.weights ?? W,
|
|
19
|
+
italic: options?.italic,
|
|
20
|
+
variable: options?.variable,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Curated catalog — 54 web-friendly faces from [Google Fonts](https://fonts.google.com/) */
|
|
24
|
+
exports.DENEB_FONT_REGISTRY = [
|
|
25
|
+
// ── Sans-serif ──
|
|
26
|
+
googleFont('inter', 'Inter', 'sans-serif', 'inter', 'Inter', { variable: true }),
|
|
27
|
+
googleFont('roboto', 'Roboto', 'sans-serif', 'roboto', 'Roboto'),
|
|
28
|
+
googleFont('open-sans', 'Open Sans', 'sans-serif', 'open-sans', 'Open+Sans', { variable: true }),
|
|
29
|
+
googleFont('lato', 'Lato', 'sans-serif', 'lato', 'Lato', { italic: true }),
|
|
30
|
+
googleFont('montserrat', 'Montserrat', 'sans-serif', 'montserrat', 'Montserrat', { variable: true }),
|
|
31
|
+
googleFont('poppins', 'Poppins', 'sans-serif', 'poppins', 'Poppins'),
|
|
32
|
+
googleFont('nunito', 'Nunito', 'sans-serif', 'nunito', 'Nunito', { variable: true }),
|
|
33
|
+
googleFont('raleway', 'Raleway', 'sans-serif', 'raleway', 'Raleway', { variable: true }),
|
|
34
|
+
googleFont('dm-sans', 'DM Sans', 'sans-serif', 'dm-sans', 'DM+Sans', { variable: true }),
|
|
35
|
+
googleFont('work-sans', 'Work Sans', 'sans-serif', 'work-sans', 'Work+Sans', { variable: true }),
|
|
36
|
+
googleFont('fira-sans', 'Fira Sans', 'sans-serif', 'fira-sans', 'Fira+Sans'),
|
|
37
|
+
googleFont('manrope', 'Manrope', 'sans-serif', 'manrope', 'Manrope', { variable: true }),
|
|
38
|
+
googleFont('oswald', 'Oswald', 'sans-serif', 'oswald', 'Oswald', { weights: [400, 500, 600, 700] }),
|
|
39
|
+
googleFont('source-sans-3', 'Source Sans 3', 'sans-serif', 'source-sans-3', 'Source+Sans+3', { variable: true }),
|
|
40
|
+
googleFont('quicksand', 'Quicksand', 'sans-serif', 'quicksand', 'Quicksand', { variable: true }),
|
|
41
|
+
googleFont('plus-jakarta-sans', 'Plus Jakarta Sans', 'sans-serif', 'plus-jakarta-sans', 'Plus+Jakarta+Sans', { variable: true }),
|
|
42
|
+
{
|
|
43
|
+
id: 'satoshi',
|
|
44
|
+
label: 'Satoshi',
|
|
45
|
+
category: 'sans-serif',
|
|
46
|
+
family: 'Space Grotesk',
|
|
47
|
+
cssStack: "'Space Grotesk', system-ui, sans-serif",
|
|
48
|
+
fontsourcePackage: null,
|
|
49
|
+
googleQuery: null,
|
|
50
|
+
googleFonts: false,
|
|
51
|
+
weights: W,
|
|
52
|
+
substituteId: 'space-grotesk',
|
|
53
|
+
},
|
|
54
|
+
googleFont('space-grotesk', 'Space Grotesk', 'sans-serif', 'space-grotesk', 'Space+Grotesk', { variable: true }),
|
|
55
|
+
googleFont('figtree', 'Figtree', 'sans-serif', 'figtree', 'Figtree', { variable: true }),
|
|
56
|
+
googleFont('urbanist', 'Urbanist', 'sans-serif', 'urbanist', 'Urbanist', { variable: true }),
|
|
57
|
+
googleFont('outfit', 'Outfit', 'sans-serif', 'outfit', 'Outfit', { variable: true }),
|
|
58
|
+
{
|
|
59
|
+
id: 'general-sans',
|
|
60
|
+
label: 'General Sans',
|
|
61
|
+
category: 'sans-serif',
|
|
62
|
+
family: 'Figtree',
|
|
63
|
+
cssStack: "'Figtree', system-ui, sans-serif",
|
|
64
|
+
fontsourcePackage: null,
|
|
65
|
+
googleQuery: null,
|
|
66
|
+
googleFonts: false,
|
|
67
|
+
weights: W,
|
|
68
|
+
substituteId: 'figtree',
|
|
69
|
+
},
|
|
70
|
+
googleFont('mulish', 'Mulish', 'sans-serif', 'mulish', 'Mulish', { variable: true }),
|
|
71
|
+
googleFont('barlow', 'Barlow', 'sans-serif', 'barlow', 'Barlow'),
|
|
72
|
+
googleFont('rubik', 'Rubik', 'sans-serif', 'rubik', 'Rubik', { variable: true }),
|
|
73
|
+
googleFont('geist', 'Geist', 'sans-serif', 'geist-sans', 'Geist', { variable: true }),
|
|
74
|
+
// ── Serif ──
|
|
75
|
+
googleFont('merriweather', 'Merriweather', 'serif', 'merriweather', 'Merriweather'),
|
|
76
|
+
googleFont('playfair-display', 'Playfair Display', 'serif', 'playfair-display', 'Playfair+Display'),
|
|
77
|
+
googleFont('lora', 'Lora', 'serif', 'lora', 'Lora', { variable: true }),
|
|
78
|
+
googleFont('pt-serif', 'PT Serif', 'serif', 'pt-serif', 'PT+Serif'),
|
|
79
|
+
googleFont('eb-garamond', 'EB Garamond', 'serif', 'eb-garamond', 'EB+Garamond', { variable: true }),
|
|
80
|
+
googleFont('libre-baskerville', 'Libre Baskerville', 'serif', 'libre-baskerville', 'Libre+Baskerville'),
|
|
81
|
+
googleFont('crimson-text', 'Crimson Text', 'serif', 'crimson-text', 'Crimson+Text'),
|
|
82
|
+
googleFont('cormorant-garamond', 'Cormorant Garamond', 'serif', 'cormorant-garamond', 'Cormorant+Garamond'),
|
|
83
|
+
googleFont('frank-ruhl-libre', 'Frank Ruhl Libre', 'serif', 'frank-ruhl-libre', 'Frank+Ruhl+Libre'),
|
|
84
|
+
googleFont('taviraj', 'Taviraj', 'serif', 'taviraj', 'Taviraj'),
|
|
85
|
+
googleFont('arapey', 'Arapey', 'serif', 'arapey', 'Arapey', { weights: [400], italic: true }),
|
|
86
|
+
googleFont('caladea', 'Caladea', 'serif', 'caladea', 'Caladea'),
|
|
87
|
+
// ── Slab serif ──
|
|
88
|
+
googleFont('roboto-slab', 'Roboto Slab', 'slab-serif', 'roboto-slab', 'Roboto+Slab', { variable: true }),
|
|
89
|
+
googleFont('zilla-slab', 'Zilla Slab', 'slab-serif', 'zilla-slab', 'Zilla+Slab'),
|
|
90
|
+
googleFont('aleo', 'Aleo', 'slab-serif', 'aleo', 'Aleo', { variable: true }),
|
|
91
|
+
googleFont('arvo', 'Arvo', 'slab-serif', 'arvo', 'Arvo'),
|
|
92
|
+
googleFont('bitter', 'Bitter', 'slab-serif', 'bitter', 'Bitter', { variable: true }),
|
|
93
|
+
googleFont('enriqueta', 'Enriqueta', 'slab-serif', 'enriqueta', 'Enriqueta'),
|
|
94
|
+
// ── Monospace ──
|
|
95
|
+
googleFont('fira-code', 'Fira Code', 'monospace', 'fira-code', 'Fira+Code', { weights: W_MONO, variable: true }),
|
|
96
|
+
googleFont('roboto-mono', 'Roboto Mono', 'monospace', 'roboto-mono', 'Roboto+Mono', { weights: W_MONO, variable: true }),
|
|
97
|
+
googleFont('jetbrains-mono', 'JetBrains Mono', 'monospace', 'jetbrains-mono', 'JetBrains+Mono', { weights: W_MONO, variable: true }),
|
|
98
|
+
googleFont('space-mono', 'Space Mono', 'monospace', 'space-mono', 'Space+Mono', { weights: [400, 700], italic: true }),
|
|
99
|
+
// ── Display ──
|
|
100
|
+
googleFont('bebas-neue', 'Bebas Neue', 'display', 'bebas-neue', 'Bebas+Neue', { weights: [400] }),
|
|
101
|
+
googleFont('fraunces', 'Fraunces', 'display', 'fraunces', 'Fraunces', { variable: true }),
|
|
102
|
+
googleFont('cinzel', 'Cinzel', 'display', 'cinzel', 'Cinzel', { weights: W_DISPLAY }),
|
|
103
|
+
googleFont('paytone-one', 'Paytone One', 'display', 'paytone-one', 'Paytone+One', { weights: [400] }),
|
|
104
|
+
googleFont('righteous', 'Righteous', 'display', 'righteous', 'Righteous', { weights: [400] }),
|
|
105
|
+
googleFont('abril-fatface', 'Abril Fatface', 'display', 'abril-fatface', 'Abril+Fatface', { weights: [400] }),
|
|
106
|
+
// ── System / editorial aliases ──
|
|
107
|
+
{
|
|
108
|
+
id: 'system-sans',
|
|
109
|
+
label: 'System Sans',
|
|
110
|
+
category: 'system',
|
|
111
|
+
family: 'system-ui',
|
|
112
|
+
cssStack: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
113
|
+
fontsourcePackage: null,
|
|
114
|
+
googleQuery: null,
|
|
115
|
+
googleFonts: false,
|
|
116
|
+
weights: [],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'editorial-serif',
|
|
120
|
+
label: 'Editorial Serif',
|
|
121
|
+
category: 'serif',
|
|
122
|
+
family: 'Playfair Display',
|
|
123
|
+
cssStack: "'Playfair Display', Georgia, serif",
|
|
124
|
+
fontsourcePackage: 'playfair-display',
|
|
125
|
+
googleQuery: 'Playfair+Display',
|
|
126
|
+
googleFonts: true,
|
|
127
|
+
weights: W,
|
|
128
|
+
substituteId: 'playfair-display',
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
exports.DENEB_FONT_BY_ID = new Map(exports.DENEB_FONT_REGISTRY.map((font) => [font.id, font]));
|
|
132
|
+
exports.DENEB_GOOGLE_FONT_COUNT = exports.DENEB_FONT_REGISTRY.filter((f) => f.googleFonts && f.fontsourcePackage).length;
|
|
133
|
+
/** Default self-hosted set when a project has no site-data font choices yet. */
|
|
134
|
+
exports.DEFAULT_PROJECT_FONT_IDS = [
|
|
135
|
+
'inter',
|
|
136
|
+
'plus-jakarta-sans',
|
|
137
|
+
'playfair-display',
|
|
138
|
+
];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DenebFontDefinition } from './types';
|
|
2
|
+
export declare function normalizeFontId(value: string): string;
|
|
3
|
+
export declare function lookupFontDefinition(value: string | undefined): DenebFontDefinition | null;
|
|
4
|
+
export declare function resolveInstallableFont(value: string | undefined): DenebFontDefinition | null;
|
|
5
|
+
export declare function resolveFontFamily(value: string | undefined): string | undefined;
|
|
6
|
+
export declare function listFontsByCategory(): Record<string, DenebFontDefinition[]>;
|
|
7
|
+
export declare function collectFontIdsFromSiteData(siteData: unknown): string[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeFontId = normalizeFontId;
|
|
4
|
+
exports.lookupFontDefinition = lookupFontDefinition;
|
|
5
|
+
exports.resolveInstallableFont = resolveInstallableFont;
|
|
6
|
+
exports.resolveFontFamily = resolveFontFamily;
|
|
7
|
+
exports.listFontsByCategory = listFontsByCategory;
|
|
8
|
+
exports.collectFontIdsFromSiteData = collectFontIdsFromSiteData;
|
|
9
|
+
const registry_1 = require("./registry");
|
|
10
|
+
function normalizeFontId(value) {
|
|
11
|
+
return value
|
|
12
|
+
.trim()
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/['"]/g, '')
|
|
15
|
+
.replace(/\s+/g, '-')
|
|
16
|
+
.replace(/[^a-z0-9-]/g, '');
|
|
17
|
+
}
|
|
18
|
+
function lookupFontDefinition(value) {
|
|
19
|
+
if (!value || !value.trim())
|
|
20
|
+
return null;
|
|
21
|
+
const slug = normalizeFontId(value);
|
|
22
|
+
const direct = registry_1.DENEB_FONT_BY_ID.get(slug);
|
|
23
|
+
if (direct)
|
|
24
|
+
return direct;
|
|
25
|
+
for (const font of registry_1.DENEB_FONT_REGISTRY) {
|
|
26
|
+
if (normalizeFontId(font.label) === slug)
|
|
27
|
+
return font;
|
|
28
|
+
if (normalizeFontId(font.family) === slug)
|
|
29
|
+
return font;
|
|
30
|
+
}
|
|
31
|
+
// Match partial family from css stack input e.g. "Inter, sans-serif"
|
|
32
|
+
const primary = value.split(',')[0]?.trim().replace(/^['"]|['"]$/g, '');
|
|
33
|
+
if (primary) {
|
|
34
|
+
const primarySlug = normalizeFontId(primary);
|
|
35
|
+
return registry_1.DENEB_FONT_BY_ID.get(primarySlug) ?? lookupFontDefinition(primarySlug);
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function resolveInstallableFont(value) {
|
|
40
|
+
const font = lookupFontDefinition(value);
|
|
41
|
+
if (!font)
|
|
42
|
+
return null;
|
|
43
|
+
if (font.fontsourcePackage)
|
|
44
|
+
return font;
|
|
45
|
+
if (font.substituteId)
|
|
46
|
+
return registry_1.DENEB_FONT_BY_ID.get(font.substituteId) ?? null;
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function resolveFontFamily(value) {
|
|
50
|
+
if (!value || !value.trim())
|
|
51
|
+
return undefined;
|
|
52
|
+
const font = lookupFontDefinition(value);
|
|
53
|
+
if (font)
|
|
54
|
+
return font.cssStack;
|
|
55
|
+
// Pass through quoted stacks or raw family names
|
|
56
|
+
if (value.includes(','))
|
|
57
|
+
return value;
|
|
58
|
+
return `'${value.trim()}', system-ui, sans-serif`;
|
|
59
|
+
}
|
|
60
|
+
function listFontsByCategory() {
|
|
61
|
+
var _a;
|
|
62
|
+
const grouped = {};
|
|
63
|
+
for (const font of registry_1.DENEB_FONT_REGISTRY) {
|
|
64
|
+
grouped[_a = font.category] ?? (grouped[_a] = []);
|
|
65
|
+
grouped[font.category].push(font);
|
|
66
|
+
}
|
|
67
|
+
return grouped;
|
|
68
|
+
}
|
|
69
|
+
function collectFontIdsFromSiteData(siteData) {
|
|
70
|
+
const ids = new Set();
|
|
71
|
+
const add = (raw) => {
|
|
72
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
73
|
+
return;
|
|
74
|
+
const font = lookupFontDefinition(raw);
|
|
75
|
+
if (font) {
|
|
76
|
+
ids.add(font.id);
|
|
77
|
+
if (font.substituteId)
|
|
78
|
+
ids.add(font.substituteId);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const slug = normalizeFontId(raw);
|
|
82
|
+
if (slug)
|
|
83
|
+
ids.add(slug);
|
|
84
|
+
};
|
|
85
|
+
if (!siteData || typeof siteData !== 'object')
|
|
86
|
+
return [];
|
|
87
|
+
const data = siteData;
|
|
88
|
+
const theme = data.theme ??
|
|
89
|
+
data.template?.structure?.theme;
|
|
90
|
+
add(theme?.headingFont);
|
|
91
|
+
add(theme?.bodyFont);
|
|
92
|
+
const styles = data.styles;
|
|
93
|
+
if (styles && typeof styles === 'object' && !Array.isArray(styles)) {
|
|
94
|
+
for (const styleEntry of Object.values(styles)) {
|
|
95
|
+
if (styleEntry && typeof styleEntry === 'object' && !Array.isArray(styleEntry)) {
|
|
96
|
+
add(styleEntry.fontFamily);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const walkContent = (node) => {
|
|
101
|
+
if (!node || typeof node !== 'object')
|
|
102
|
+
return;
|
|
103
|
+
if (Array.isArray(node)) {
|
|
104
|
+
node.forEach(walkContent);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const [key, value] of Object.entries(node)) {
|
|
108
|
+
if (key.endsWith('Style') && value && typeof value === 'object') {
|
|
109
|
+
add(value.fontFamily);
|
|
110
|
+
}
|
|
111
|
+
walkContent(value);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
walkContent(data.content);
|
|
115
|
+
ids.delete('system-sans');
|
|
116
|
+
return Array.from(ids);
|
|
117
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type DenebFontCategory = 'sans-serif' | 'serif' | 'slab-serif' | 'monospace' | 'display' | 'system';
|
|
2
|
+
export interface DenebFontDefinition {
|
|
3
|
+
/** Stable slug for Fivora + site-data (e.g. `plus-jakarta-sans`) */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Human label shown in editors */
|
|
6
|
+
label: string;
|
|
7
|
+
category: DenebFontCategory;
|
|
8
|
+
/** Primary CSS family name */
|
|
9
|
+
family: string;
|
|
10
|
+
/** Full CSS font-family stack */
|
|
11
|
+
cssStack: string;
|
|
12
|
+
/** @fontsource package name (without scope), null when not installable */
|
|
13
|
+
fontsourcePackage: string | null;
|
|
14
|
+
/** Google Fonts API family query segment */
|
|
15
|
+
googleQuery: string | null;
|
|
16
|
+
/** Whether this font is served from Google Fonts */
|
|
17
|
+
googleFonts: boolean;
|
|
18
|
+
/** Default weights to install / load */
|
|
19
|
+
weights: number[];
|
|
20
|
+
/** Include italic axis in Google Fonts request */
|
|
21
|
+
italic?: boolean;
|
|
22
|
+
/** Use variable font bundle when installing via @fontsource */
|
|
23
|
+
variable?: boolean;
|
|
24
|
+
/** When not on Google Fonts, maps to this registry id for install/load */
|
|
25
|
+
substituteId?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface DenebFontManifest {
|
|
28
|
+
version: 1;
|
|
29
|
+
installedAt: string;
|
|
30
|
+
fonts: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
fontsourcePackage: string;
|
|
33
|
+
weights: number[];
|
|
34
|
+
variable?: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './fonts';
|
|
2
|
+
export * from './types/styles';
|
|
3
|
+
export * from './types/protocol';
|
|
4
|
+
export * from './types/markers';
|
|
5
|
+
export * from './engine/cssVariables';
|
|
6
|
+
export * from './engine/domPatcher';
|
|
7
|
+
export * from './engine/tokenResolver';
|
|
8
|
+
export * from './engine/deepMerge';
|
|
9
|
+
export * from './validation/styleSchemaValidator';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./fonts"), exports);
|
|
18
|
+
__exportStar(require("./types/styles"), exports);
|
|
19
|
+
__exportStar(require("./types/protocol"), exports);
|
|
20
|
+
__exportStar(require("./types/markers"), exports);
|
|
21
|
+
__exportStar(require("./engine/cssVariables"), exports);
|
|
22
|
+
__exportStar(require("./engine/domPatcher"), exports);
|
|
23
|
+
__exportStar(require("./engine/tokenResolver"), exports);
|
|
24
|
+
__exportStar(require("./engine/deepMerge"), exports);
|
|
25
|
+
__exportStar(require("./validation/styleSchemaValidator"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const STYLE_TARGET_ATTRIBUTE = "data-preview-style-target";
|
|
2
|
+
export declare const STYLE_TYPE_ATTRIBUTE = "data-preview-style-type";
|
|
3
|
+
export declare const PREVIEW_FIELD_ATTRIBUTE = "data-preview-field-path";
|
|
4
|
+
export declare const PREVIEW_ITEM_ATTRIBUTE = "data-preview-item-path";
|
|
5
|
+
export declare const PREVIEW_LIST_ATTRIBUTE = "data-preview-list-path";
|
|
6
|
+
export declare function buildStyleTargetSelector(targetPath: string): string;
|
|
7
|
+
export declare function buildStyleFallbackSelectors(targetPath: string, styleType?: string): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PREVIEW_LIST_ATTRIBUTE = exports.PREVIEW_ITEM_ATTRIBUTE = exports.PREVIEW_FIELD_ATTRIBUTE = exports.STYLE_TYPE_ATTRIBUTE = exports.STYLE_TARGET_ATTRIBUTE = void 0;
|
|
4
|
+
exports.buildStyleTargetSelector = buildStyleTargetSelector;
|
|
5
|
+
exports.buildStyleFallbackSelectors = buildStyleFallbackSelectors;
|
|
6
|
+
exports.STYLE_TARGET_ATTRIBUTE = 'data-preview-style-target';
|
|
7
|
+
exports.STYLE_TYPE_ATTRIBUTE = 'data-preview-style-type';
|
|
8
|
+
exports.PREVIEW_FIELD_ATTRIBUTE = 'data-preview-field-path';
|
|
9
|
+
exports.PREVIEW_ITEM_ATTRIBUTE = 'data-preview-item-path';
|
|
10
|
+
exports.PREVIEW_LIST_ATTRIBUTE = 'data-preview-list-path';
|
|
11
|
+
function escapeAttributeValue(value) {
|
|
12
|
+
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
|
|
13
|
+
return CSS.escape(value);
|
|
14
|
+
}
|
|
15
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
16
|
+
}
|
|
17
|
+
function buildStyleTargetSelector(targetPath) {
|
|
18
|
+
return `[${exports.STYLE_TARGET_ATTRIBUTE}="${escapeAttributeValue(targetPath)}"]`;
|
|
19
|
+
}
|
|
20
|
+
function buildStyleFallbackSelectors(targetPath, styleType) {
|
|
21
|
+
const selectors = [
|
|
22
|
+
buildStyleTargetSelector(targetPath),
|
|
23
|
+
`[${exports.PREVIEW_FIELD_ATTRIBUTE}="${escapeAttributeValue(targetPath)}"]`,
|
|
24
|
+
`[${exports.PREVIEW_ITEM_ATTRIBUTE}="${escapeAttributeValue(targetPath)}"]`,
|
|
25
|
+
];
|
|
26
|
+
if (styleType === 'card' && targetPath.endsWith('.card')) {
|
|
27
|
+
const itemPath = targetPath.slice(0, -'.card'.length);
|
|
28
|
+
selectors.push(`[${exports.PREVIEW_ITEM_ATTRIBUTE}="${escapeAttributeValue(itemPath)}"]`);
|
|
29
|
+
}
|
|
30
|
+
return selectors;
|
|
31
|
+
}
|