stera-ui 0.1.3 → 0.2.0
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/dist/{chunk-57DNZ2YS.js → chunk-AXIACJ2C.js} +5 -1
- package/dist/{chunk-HCHFPFTS.js → chunk-MFUZBXO6.js} +16 -1
- package/dist/cli.js +503 -33
- package/dist/index.js +2 -2
- package/dist/schema/index.d.ts +36 -18
- package/dist/schema/index.js +1 -1
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +1 -1
- package/package.json +2 -1
|
@@ -34,7 +34,11 @@ var configSchema = z.object({
|
|
|
34
34
|
ui: z.string(),
|
|
35
35
|
lib: z.string(),
|
|
36
36
|
hooks: z.string()
|
|
37
|
-
})
|
|
37
|
+
}),
|
|
38
|
+
fonts: z.object({
|
|
39
|
+
strategy: z.enum(["stera-default", "keep-existing", "skip"]),
|
|
40
|
+
nextFont: z.boolean().optional()
|
|
41
|
+
}).optional()
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
export {
|
|
@@ -165,7 +165,22 @@ function detectProject(cwd) {
|
|
|
165
165
|
rsc = fs2.existsSync(path3.join(cwd, appDir));
|
|
166
166
|
}
|
|
167
167
|
const existingCssFile = findTailwindCssFile(cwd);
|
|
168
|
-
|
|
168
|
+
let layoutFile = null;
|
|
169
|
+
if (framework === "next") {
|
|
170
|
+
const appDir = hasSrc ? "src/app" : "app";
|
|
171
|
+
for (const ext of ["tsx", "jsx", "ts", "js"]) {
|
|
172
|
+
const candidate = `${appDir}/layout.${ext}`;
|
|
173
|
+
if (fs2.existsSync(path3.join(cwd, candidate))) {
|
|
174
|
+
layoutFile = candidate;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (framework === "vite") {
|
|
179
|
+
if (fs2.existsSync(path3.join(cwd, "index.html"))) {
|
|
180
|
+
layoutFile = "index.html";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { hasSrc, framework, rsc, existingCssFile, layoutFile };
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
// src/utils/detect-globals.ts
|
package/dist/cli.js
CHANGED
|
@@ -444,8 +444,9 @@ async function add(components, options) {
|
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
// src/commands/init.ts
|
|
447
|
-
import
|
|
448
|
-
import
|
|
447
|
+
import fs11 from "fs";
|
|
448
|
+
import path12 from "path";
|
|
449
|
+
import { select, confirm } from "@inquirer/prompts";
|
|
449
450
|
|
|
450
451
|
// src/utils/detect-project.ts
|
|
451
452
|
import fs5 from "fs";
|
|
@@ -486,11 +487,375 @@ function detectProject(cwd) {
|
|
|
486
487
|
rsc = fs5.existsSync(path7.join(cwd, appDir));
|
|
487
488
|
}
|
|
488
489
|
const existingCssFile = findTailwindCssFile(cwd);
|
|
489
|
-
|
|
490
|
+
let layoutFile = null;
|
|
491
|
+
if (framework === "next") {
|
|
492
|
+
const appDir = hasSrc ? "src/app" : "app";
|
|
493
|
+
for (const ext of ["tsx", "jsx", "ts", "js"]) {
|
|
494
|
+
const candidate = `${appDir}/layout.${ext}`;
|
|
495
|
+
if (fs5.existsSync(path7.join(cwd, candidate))) {
|
|
496
|
+
layoutFile = candidate;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} else if (framework === "vite") {
|
|
501
|
+
if (fs5.existsSync(path7.join(cwd, "index.html"))) {
|
|
502
|
+
layoutFile = "index.html";
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return { hasSrc, framework, rsc, existingCssFile, layoutFile };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/utils/detect-fonts.ts
|
|
509
|
+
import fs6 from "fs";
|
|
510
|
+
import path8 from "path";
|
|
511
|
+
function readIfExists(filePath) {
|
|
512
|
+
try {
|
|
513
|
+
return fs6.readFileSync(filePath, "utf-8");
|
|
514
|
+
} catch {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function detectNextFont(layoutContent) {
|
|
519
|
+
const hasNextFont = /import.*from\s+['"]next\/font/.test(layoutContent) || /import.*from\s+['"]@next\/font/.test(layoutContent) || /import.*from\s+['"]geist\/font/.test(layoutContent);
|
|
520
|
+
let nextFontVariable = null;
|
|
521
|
+
const varMatch = layoutContent.match(/variable:\s*['"](--.+?)['"]/);
|
|
522
|
+
if (varMatch) {
|
|
523
|
+
nextFontVariable = varMatch[1];
|
|
524
|
+
}
|
|
525
|
+
return { hasNextFont, nextFontVariable };
|
|
526
|
+
}
|
|
527
|
+
function detectFontFace(cssContent) {
|
|
528
|
+
const hasFontFace = /@font-face\s*\{/.test(cssContent);
|
|
529
|
+
const fontFamilies = [];
|
|
530
|
+
const regex = /font-family:\s*['"]?([^'";}\n]+?)['"]?\s*;/g;
|
|
531
|
+
let match;
|
|
532
|
+
while ((match = regex.exec(cssContent)) !== null) {
|
|
533
|
+
const family = match[1].trim();
|
|
534
|
+
if (family && !fontFamilies.includes(family)) {
|
|
535
|
+
fontFamilies.push(family);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return { hasFontFace, fontFamilies };
|
|
539
|
+
}
|
|
540
|
+
function detectGoogleFonts(content) {
|
|
541
|
+
return content.includes("fonts.googleapis.com") || content.includes("fonts.gstatic.com");
|
|
542
|
+
}
|
|
543
|
+
function detectFonts(cwd, project) {
|
|
544
|
+
const result = {
|
|
545
|
+
hasNextFont: false,
|
|
546
|
+
hasFontFace: false,
|
|
547
|
+
hasGoogleFontsLink: false,
|
|
548
|
+
fontFamilies: [],
|
|
549
|
+
nextFontVariable: null
|
|
550
|
+
};
|
|
551
|
+
if (project.layoutFile) {
|
|
552
|
+
const layoutContent = readIfExists(path8.join(cwd, project.layoutFile));
|
|
553
|
+
if (layoutContent) {
|
|
554
|
+
const nextFontResult = detectNextFont(layoutContent);
|
|
555
|
+
result.hasNextFont = nextFontResult.hasNextFont;
|
|
556
|
+
result.nextFontVariable = nextFontResult.nextFontVariable;
|
|
557
|
+
if (detectGoogleFonts(layoutContent)) {
|
|
558
|
+
result.hasGoogleFontsLink = true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (project.existingCssFile) {
|
|
563
|
+
const cssContent = readIfExists(path8.join(cwd, project.existingCssFile));
|
|
564
|
+
if (cssContent) {
|
|
565
|
+
const fontFaceResult = detectFontFace(cssContent);
|
|
566
|
+
result.hasFontFace = fontFaceResult.hasFontFace;
|
|
567
|
+
result.fontFamilies.push(...fontFaceResult.fontFamilies);
|
|
568
|
+
if (detectGoogleFonts(cssContent)) {
|
|
569
|
+
result.hasGoogleFontsLink = true;
|
|
570
|
+
}
|
|
571
|
+
const importRegex = /@import\s+['"](.+?)['"]/g;
|
|
572
|
+
let importMatch;
|
|
573
|
+
while ((importMatch = importRegex.exec(cssContent)) !== null) {
|
|
574
|
+
const importPath = importMatch[1];
|
|
575
|
+
const resolvedPath = path8.join(
|
|
576
|
+
cwd,
|
|
577
|
+
path8.dirname(project.existingCssFile),
|
|
578
|
+
importPath
|
|
579
|
+
);
|
|
580
|
+
const importedContent = readIfExists(resolvedPath);
|
|
581
|
+
if (importedContent) {
|
|
582
|
+
const imported = detectFontFace(importedContent);
|
|
583
|
+
if (imported.hasFontFace) result.hasFontFace = true;
|
|
584
|
+
for (const family of imported.fontFamilies) {
|
|
585
|
+
if (!result.fontFamilies.includes(family)) {
|
|
586
|
+
result.fontFamilies.push(family);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
for (const htmlFile of ["index.html", "src/index.html"]) {
|
|
594
|
+
const htmlContent = readIfExists(path8.join(cwd, htmlFile));
|
|
595
|
+
if (htmlContent && detectGoogleFonts(htmlContent)) {
|
|
596
|
+
result.hasGoogleFontsLink = true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return result;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/utils/patch-css-vars.ts
|
|
603
|
+
import fs7 from "fs";
|
|
604
|
+
function patchCssVariables(filePath, patches) {
|
|
605
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
606
|
+
const lines = content.split("\n");
|
|
607
|
+
const patched = [];
|
|
608
|
+
for (let i = 0; i < lines.length; i++) {
|
|
609
|
+
for (const [prop, newValue] of Object.entries(patches)) {
|
|
610
|
+
const regex = new RegExp(`(\\s*${escapeRegex(prop)}:\\s*).+?(;)`);
|
|
611
|
+
if (regex.test(lines[i])) {
|
|
612
|
+
lines[i] = lines[i].replace(regex, `$1${newValue}$2`);
|
|
613
|
+
if (!patched.includes(prop)) patched.push(prop);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (patched.length > 0) {
|
|
618
|
+
fs7.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
619
|
+
}
|
|
620
|
+
return patched;
|
|
621
|
+
}
|
|
622
|
+
function removeCssImport(filePath, importPath) {
|
|
623
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
624
|
+
const pattern = new RegExp(
|
|
625
|
+
`@import\\s+['"]${escapeRegex(importPath)}['"]\\s*;?\\n?`
|
|
626
|
+
);
|
|
627
|
+
if (!pattern.test(content)) return false;
|
|
628
|
+
fs7.writeFileSync(filePath, content.replace(pattern, ""), "utf-8");
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
function escapeRegex(str) {
|
|
632
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/utils/setup-fonts-generic.ts
|
|
636
|
+
import fs9 from "fs";
|
|
637
|
+
import path10 from "path";
|
|
638
|
+
|
|
639
|
+
// src/utils/copy-fonts.ts
|
|
640
|
+
import fs8 from "fs";
|
|
641
|
+
import path9 from "path";
|
|
642
|
+
import { createRequire } from "module";
|
|
643
|
+
var FONT_FILES = [
|
|
644
|
+
{
|
|
645
|
+
src: "geist-sans/Geist-Variable.woff2",
|
|
646
|
+
dest: "geist/Geist-Variable.woff2"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
src: "geist-sans/Geist-Italic[wght].woff2",
|
|
650
|
+
dest: "geist/Geist-Italic.woff2"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
src: "geist-mono/GeistMono-Variable.woff2",
|
|
654
|
+
dest: "geist-mono/GeistMono-Variable.woff2"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
src: "geist-mono/GeistMono-Italic[wght].woff2",
|
|
658
|
+
dest: "geist-mono/GeistMono-Italic.woff2"
|
|
659
|
+
}
|
|
660
|
+
];
|
|
661
|
+
function resolveGeistFontsDir(cwd) {
|
|
662
|
+
const require2 = createRequire(path9.join(cwd, "package.json"));
|
|
663
|
+
const geistEntry = require2.resolve("geist/font/sans");
|
|
664
|
+
const distDir = path9.dirname(geistEntry);
|
|
665
|
+
return path9.join(distDir, "fonts");
|
|
666
|
+
}
|
|
667
|
+
function copyFonts(cwd) {
|
|
668
|
+
const spinner = createSpinner("Copying font files");
|
|
669
|
+
const fontsDir = resolveGeistFontsDir(cwd);
|
|
670
|
+
const publicFontsDir = path9.join(cwd, "public", "fonts");
|
|
671
|
+
const written = [];
|
|
672
|
+
for (const file of FONT_FILES) {
|
|
673
|
+
const srcPath = path9.join(fontsDir, file.src);
|
|
674
|
+
const destPath = path9.join(publicFontsDir, file.dest);
|
|
675
|
+
if (fs8.existsSync(destPath)) continue;
|
|
676
|
+
if (!fs8.existsSync(srcPath)) {
|
|
677
|
+
spinner.fail(`Font file not found: ${file.src}`);
|
|
678
|
+
throw new Error(
|
|
679
|
+
`Could not find ${srcPath}. Is the geist package installed?`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
fs8.mkdirSync(path9.dirname(destPath), { recursive: true });
|
|
683
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
684
|
+
written.push(path9.relative(cwd, destPath));
|
|
685
|
+
}
|
|
686
|
+
if (written.length > 0) {
|
|
687
|
+
spinner.succeed(`Copied ${written.length} font files to public/fonts/`);
|
|
688
|
+
} else {
|
|
689
|
+
spinner.succeed("Font files already exist");
|
|
690
|
+
}
|
|
691
|
+
return written;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/utils/setup-fonts-generic.ts
|
|
695
|
+
var FONTS_CSS_CONTENT = `/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
696
|
+
Stera UI font registry
|
|
697
|
+
To add a font:
|
|
698
|
+
1. Drop .woff2 file(s) into public/fonts/{family-name}/
|
|
699
|
+
2. Add an @font-face block below
|
|
700
|
+
3. Add a --font-* variable in globals.css
|
|
701
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
702
|
+
|
|
703
|
+
/* \u2500\u2500\u2500 Geist Sans \u2500\u2500\u2500 */
|
|
704
|
+
@font-face {
|
|
705
|
+
font-family: 'Geist';
|
|
706
|
+
src: url('/fonts/geist/Geist-Variable.woff2') format('woff2');
|
|
707
|
+
font-weight: 100 900;
|
|
708
|
+
font-style: normal;
|
|
709
|
+
font-display: swap;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
@font-face {
|
|
713
|
+
font-family: 'Geist';
|
|
714
|
+
src: url('/fonts/geist/Geist-Italic.woff2') format('woff2');
|
|
715
|
+
font-weight: 100 900;
|
|
716
|
+
font-style: italic;
|
|
717
|
+
font-display: swap;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/* \u2500\u2500\u2500 Geist Mono \u2500\u2500\u2500 */
|
|
721
|
+
@font-face {
|
|
722
|
+
font-family: 'Geist Mono';
|
|
723
|
+
src: url('/fonts/geist-mono/GeistMono-Variable.woff2') format('woff2');
|
|
724
|
+
font-weight: 100 900;
|
|
725
|
+
font-style: normal;
|
|
726
|
+
font-display: swap;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
@font-face {
|
|
730
|
+
font-family: 'Geist Mono';
|
|
731
|
+
src: url('/fonts/geist-mono/GeistMono-Italic.woff2') format('woff2');
|
|
732
|
+
font-weight: 100 900;
|
|
733
|
+
font-style: italic;
|
|
734
|
+
font-display: swap;
|
|
735
|
+
}
|
|
736
|
+
`;
|
|
737
|
+
var PRELOAD_TAGS = `<link rel="preload" href="/fonts/geist/Geist-Variable.woff2" as="font" type="font/woff2" crossorigin />
|
|
738
|
+
<link rel="preload" href="/fonts/geist-mono/GeistMono-Variable.woff2" as="font" type="font/woff2" crossorigin />`;
|
|
739
|
+
function setupFontsGeneric(cwd, cssDir) {
|
|
740
|
+
copyFonts(cwd);
|
|
741
|
+
const fontsPath = path10.join(cssDir, "fonts.css");
|
|
742
|
+
if (!fs9.existsSync(fontsPath)) {
|
|
743
|
+
fs9.mkdirSync(path10.dirname(fontsPath), { recursive: true });
|
|
744
|
+
fs9.writeFileSync(fontsPath, FONTS_CSS_CONTENT, "utf-8");
|
|
745
|
+
console.log(` ${CHECK} Created ${path10.relative(cwd, fontsPath)}`);
|
|
746
|
+
} else {
|
|
747
|
+
console.log(` ${dim(path10.relative(cwd, fontsPath) + " (already exists, skipped)")}`);
|
|
748
|
+
}
|
|
749
|
+
return { preloadTags: PRELOAD_TAGS };
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// src/utils/setup-fonts-nextjs.ts
|
|
753
|
+
import fs10 from "fs";
|
|
754
|
+
import path11 from "path";
|
|
755
|
+
var GEIST_IMPORTS = `import { GeistSans } from "geist/font/sans"
|
|
756
|
+
import { GeistMono } from "geist/font/mono"`;
|
|
757
|
+
function canPatchLayout(content) {
|
|
758
|
+
if (!/<html[\s>]/.test(content)) return false;
|
|
759
|
+
if ((content.match(/<html[\s>]/g) || []).length > 1) return false;
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
function insertImports(content, imports) {
|
|
763
|
+
const lines = content.split("\n");
|
|
764
|
+
let lastImportIndex = -1;
|
|
765
|
+
for (let i = 0; i < lines.length; i++) {
|
|
766
|
+
const trimmed = lines[i].trim();
|
|
767
|
+
if (trimmed.startsWith("import ") || trimmed.startsWith("import{") || trimmed.startsWith("import ")) {
|
|
768
|
+
lastImportIndex = i;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (lastImportIndex >= 0) {
|
|
772
|
+
lines.splice(lastImportIndex + 1, 0, imports);
|
|
773
|
+
} else {
|
|
774
|
+
lines.unshift(imports);
|
|
775
|
+
}
|
|
776
|
+
return lines.join("\n");
|
|
777
|
+
}
|
|
778
|
+
function addClassNameToHtml(content) {
|
|
779
|
+
const fontClasses = "${GeistSans.variable} ${GeistMono.variable}";
|
|
780
|
+
const templateMatch = content.match(
|
|
781
|
+
/(<html[^>]*className=\{`)(.*?)(`\})/s
|
|
782
|
+
);
|
|
783
|
+
if (templateMatch) {
|
|
784
|
+
const existing = templateMatch[2].trim();
|
|
785
|
+
const newClassName = existing ? `${existing} ${fontClasses}` : fontClasses;
|
|
786
|
+
return content.replace(
|
|
787
|
+
templateMatch[0],
|
|
788
|
+
`${templateMatch[1]}${newClassName}${templateMatch[3]}`
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const exprMatch = content.match(
|
|
792
|
+
/(<html[^>]*className=\{)([^}`]+)(\})/
|
|
793
|
+
);
|
|
794
|
+
if (exprMatch) {
|
|
795
|
+
return content.replace(
|
|
796
|
+
exprMatch[0],
|
|
797
|
+
`${exprMatch[1]}\`\${${exprMatch[2].trim()}} ${fontClasses}\`${exprMatch[3]}`
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
const stringMatch = content.match(
|
|
801
|
+
/(<html[^>]*className=)(["'])([^"']*)(["'])/
|
|
802
|
+
);
|
|
803
|
+
if (stringMatch) {
|
|
804
|
+
const existing = stringMatch[3].trim();
|
|
805
|
+
const newClassName = existing ? `${existing} ${fontClasses}` : fontClasses;
|
|
806
|
+
return content.replace(
|
|
807
|
+
stringMatch[0],
|
|
808
|
+
`${stringMatch[1]}{\`${newClassName}\`}`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
return content.replace(
|
|
812
|
+
/<html(\s)/,
|
|
813
|
+
`<html className={\`${fontClasses}\`}$1`
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
function generateSnippet() {
|
|
817
|
+
return `
|
|
818
|
+
Add these imports to your root layout file:
|
|
819
|
+
|
|
820
|
+
${dim(GEIST_IMPORTS)}
|
|
821
|
+
|
|
822
|
+
Then add the font variables to your <html> tag:
|
|
823
|
+
|
|
824
|
+
${dim("<html className={`${GeistSans.variable} ${GeistMono.variable}`}>")}
|
|
825
|
+
`;
|
|
826
|
+
}
|
|
827
|
+
function setupFontsNextjs(cwd, layoutFile, cssFile) {
|
|
828
|
+
const layoutPath = path11.join(cwd, layoutFile);
|
|
829
|
+
const cssPath = path11.join(cwd, cssFile);
|
|
830
|
+
let layoutContent = fs10.readFileSync(layoutPath, "utf-8");
|
|
831
|
+
if (layoutContent.includes("geist/font/sans") || layoutContent.includes("GeistSans")) {
|
|
832
|
+
console.log(` ${dim("geist/font already configured in " + layoutFile)}`);
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
if (!canPatchLayout(layoutContent)) {
|
|
836
|
+
console.log(` ${WARN} Could not auto-patch ${layoutFile}`);
|
|
837
|
+
console.log(generateSnippet());
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
layoutContent = insertImports(layoutContent, GEIST_IMPORTS);
|
|
841
|
+
layoutContent = addClassNameToHtml(layoutContent);
|
|
842
|
+
fs10.writeFileSync(layoutPath, layoutContent, "utf-8");
|
|
843
|
+
console.log(` ${CHECK} Updated ${layoutFile} with geist/font imports`);
|
|
844
|
+
if (fs10.existsSync(cssPath)) {
|
|
845
|
+
const patched = patchCssVariables(cssPath, {
|
|
846
|
+
"--font-display": "var(--font-geist-sans)",
|
|
847
|
+
"--font-body": "var(--font-geist-sans)"
|
|
848
|
+
});
|
|
849
|
+
if (patched.length > 0) {
|
|
850
|
+
console.log(` ${CHECK} Updated font variables in ${path11.relative(cwd, cssPath)}`);
|
|
851
|
+
}
|
|
852
|
+
removeCssImport(cssPath, "./fonts.css");
|
|
853
|
+
}
|
|
854
|
+
return true;
|
|
490
855
|
}
|
|
491
856
|
|
|
492
857
|
// src/commands/init.ts
|
|
493
|
-
function buildConfig(project) {
|
|
858
|
+
function buildConfig(project, fontChoice) {
|
|
494
859
|
let cssPath;
|
|
495
860
|
if (project.existingCssFile) {
|
|
496
861
|
cssPath = project.existingCssFile;
|
|
@@ -509,6 +874,10 @@ function buildConfig(project) {
|
|
|
509
874
|
ui: "@/components/ui",
|
|
510
875
|
lib: "@/lib",
|
|
511
876
|
hooks: "@/hooks"
|
|
877
|
+
},
|
|
878
|
+
fonts: {
|
|
879
|
+
strategy: fontChoice.strategy,
|
|
880
|
+
...fontChoice.useNextFont ? { nextFont: true } : {}
|
|
512
881
|
}
|
|
513
882
|
};
|
|
514
883
|
}
|
|
@@ -516,7 +885,7 @@ function stripTailwindCoreImport(content) {
|
|
|
516
885
|
return content.split("\n").filter((line) => !line.trim().match(/^@import\s+["']tailwindcss["']/)).join("\n");
|
|
517
886
|
}
|
|
518
887
|
function insertImportLine(filePath, importStatement) {
|
|
519
|
-
const content =
|
|
888
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
520
889
|
const lines = content.split("\n");
|
|
521
890
|
let lastImportIndex = -1;
|
|
522
891
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -529,10 +898,100 @@ function insertImportLine(filePath, importStatement) {
|
|
|
529
898
|
} else {
|
|
530
899
|
lines.unshift(importStatement);
|
|
531
900
|
}
|
|
532
|
-
|
|
901
|
+
fs11.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
902
|
+
}
|
|
903
|
+
async function promptFontChoice(project, detected, skipPrompts) {
|
|
904
|
+
const hasExistingFonts = detected.hasNextFont || detected.hasFontFace || detected.hasGoogleFontsLink;
|
|
905
|
+
const defaultStrategy = hasExistingFonts ? "keep-existing" : "stera-default";
|
|
906
|
+
if (skipPrompts) {
|
|
907
|
+
return {
|
|
908
|
+
strategy: defaultStrategy,
|
|
909
|
+
useNextFont: project.framework === "next" && defaultStrategy === "stera-default"
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
if (hasExistingFonts) {
|
|
913
|
+
const sources = [];
|
|
914
|
+
if (detected.fontFamilies.length > 0) {
|
|
915
|
+
sources.push(detected.fontFamilies.join(", "));
|
|
916
|
+
}
|
|
917
|
+
if (detected.hasNextFont) sources.push("next/font");
|
|
918
|
+
if (detected.hasGoogleFontsLink) sources.push("Google Fonts");
|
|
919
|
+
console.log(` Detected existing fonts: ${sources.join(" via ")}
|
|
920
|
+
`);
|
|
921
|
+
}
|
|
922
|
+
const choices = [];
|
|
923
|
+
if (hasExistingFonts) {
|
|
924
|
+
choices.push({
|
|
925
|
+
value: "keep-existing",
|
|
926
|
+
name: "Keep my current fonts",
|
|
927
|
+
description: "Update CSS variables to use your existing fonts"
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
choices.push({
|
|
931
|
+
value: "stera-default",
|
|
932
|
+
name: "Use Stera UI default fonts (Geist Sans + Geist Mono)",
|
|
933
|
+
description: "Self-hosted, optimized variable fonts"
|
|
934
|
+
});
|
|
935
|
+
choices.push({
|
|
936
|
+
value: "skip",
|
|
937
|
+
name: "Skip font setup",
|
|
938
|
+
description: "Configure fonts manually later"
|
|
939
|
+
});
|
|
940
|
+
const strategy = await select({
|
|
941
|
+
message: "How would you like to set up fonts?",
|
|
942
|
+
choices,
|
|
943
|
+
default: defaultStrategy
|
|
944
|
+
});
|
|
945
|
+
let useNextFont = false;
|
|
946
|
+
if (project.framework === "next" && strategy === "stera-default") {
|
|
947
|
+
useNextFont = await confirm({
|
|
948
|
+
message: "Use next/font for automatic optimization? (recommended)",
|
|
949
|
+
default: true
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
console.log("");
|
|
953
|
+
return { strategy, useNextFont };
|
|
954
|
+
}
|
|
955
|
+
async function executeFontStrategy(fontChoice, project, detected, config, cwd) {
|
|
956
|
+
const cssDir = path12.dirname(path12.resolve(cwd, config.css));
|
|
957
|
+
const cssPath = path12.resolve(cwd, config.css);
|
|
958
|
+
if (fontChoice.strategy === "skip") {
|
|
959
|
+
return {};
|
|
960
|
+
}
|
|
961
|
+
if (fontChoice.strategy === "keep-existing") {
|
|
962
|
+
if (fs11.existsSync(cssPath)) {
|
|
963
|
+
const fontFamily = detected.fontFamilies.length > 0 ? detected.fontFamilies[0] : null;
|
|
964
|
+
const fontValue = detected.nextFontVariable ? `var(${detected.nextFontVariable})` : fontFamily ?? "Geist";
|
|
965
|
+
const patched = patchCssVariables(cssPath, {
|
|
966
|
+
"--font-display": fontValue,
|
|
967
|
+
"--font-body": fontValue
|
|
968
|
+
});
|
|
969
|
+
const steraUiPath = path12.join(cssDir, "stera-ui.css");
|
|
970
|
+
if (fs11.existsSync(steraUiPath)) {
|
|
971
|
+
patchCssVariables(steraUiPath, {
|
|
972
|
+
"--font-display": fontValue,
|
|
973
|
+
"--font-body": fontValue
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (patched.length > 0) {
|
|
977
|
+
console.log(` ${CHECK} Updated font variables to use ${fontValue}`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return {};
|
|
981
|
+
}
|
|
982
|
+
if (fontChoice.useNextFont && project.layoutFile) {
|
|
983
|
+
await installDependencies(["geist"], cwd);
|
|
984
|
+
const targetCss = fs11.existsSync(path12.join(cssDir, "stera-ui.css")) ? path12.join(cssDir, "stera-ui.css") : cssPath;
|
|
985
|
+
setupFontsNextjs(cwd, project.layoutFile, targetCss);
|
|
986
|
+
return {};
|
|
987
|
+
} else {
|
|
988
|
+
await installDependencies(["geist"], cwd);
|
|
989
|
+
const result = setupFontsGeneric(cwd, cssDir);
|
|
990
|
+
return { preloadTags: result.preloadTags };
|
|
991
|
+
}
|
|
533
992
|
}
|
|
534
993
|
async function init(options) {
|
|
535
|
-
const cwd = options.cwd ?
|
|
994
|
+
const cwd = options.cwd ? path12.resolve(options.cwd) : process.cwd();
|
|
536
995
|
console.log(`
|
|
537
996
|
${LOGO} Stera UI
|
|
538
997
|
`);
|
|
@@ -542,8 +1001,8 @@ async function init(options) {
|
|
|
542
1001
|
console.log(' Run "stera-ui add <component>" to add components.\n');
|
|
543
1002
|
return;
|
|
544
1003
|
}
|
|
545
|
-
const pkgPath =
|
|
546
|
-
if (!
|
|
1004
|
+
const pkgPath = path12.join(cwd, "package.json");
|
|
1005
|
+
if (!fs11.existsSync(pkgPath)) {
|
|
547
1006
|
console.error(
|
|
548
1007
|
" Error: No package.json found. Please run this in a project directory."
|
|
549
1008
|
);
|
|
@@ -558,47 +1017,41 @@ async function init(options) {
|
|
|
558
1017
|
console.log(` Detected ${parts.join(" project with ")} project
|
|
559
1018
|
`);
|
|
560
1019
|
}
|
|
561
|
-
const
|
|
562
|
-
const
|
|
563
|
-
|
|
1020
|
+
const detectedFonts = detectFonts(cwd, project);
|
|
1021
|
+
const fontChoice = await promptFontChoice(
|
|
1022
|
+
project,
|
|
1023
|
+
detectedFonts,
|
|
1024
|
+
options.yes ?? false
|
|
1025
|
+
);
|
|
1026
|
+
const config = buildConfig(project, fontChoice);
|
|
1027
|
+
const configPath = path12.join(cwd, CONFIG_FILE);
|
|
1028
|
+
fs11.writeFileSync(
|
|
564
1029
|
configPath,
|
|
565
1030
|
JSON.stringify(config, null, 2) + "\n",
|
|
566
1031
|
"utf-8"
|
|
567
1032
|
);
|
|
568
1033
|
console.log(` ${CHECK} Created components.json`);
|
|
569
|
-
const cssDir =
|
|
1034
|
+
const cssDir = path12.dirname(path12.resolve(cwd, config.css));
|
|
570
1035
|
if (project.existingCssFile) {
|
|
571
1036
|
if (hasGlobalsCss(config, cwd)) {
|
|
572
1037
|
console.log(
|
|
573
|
-
` ${CHECK} Stera already initialised ${dim(`(${
|
|
1038
|
+
` ${CHECK} Stera already initialised ${dim(`(${path12.join(path12.dirname(project.existingCssFile), "stera-ui.css")} exists)`)}`
|
|
574
1039
|
);
|
|
575
1040
|
} else {
|
|
576
1041
|
try {
|
|
577
1042
|
const globalsItem = await fetchRegistryItem("globals");
|
|
578
1043
|
const globalsContent = globalsItem.files[0].content ?? "";
|
|
579
1044
|
const steraUiContent = stripTailwindCoreImport(globalsContent);
|
|
580
|
-
const steraUiPath =
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
console.log(` ${CHECK} Created ${
|
|
584
|
-
const existingCssPath =
|
|
1045
|
+
const steraUiPath = path12.join(cssDir, "stera-ui.css");
|
|
1046
|
+
fs11.mkdirSync(cssDir, { recursive: true });
|
|
1047
|
+
fs11.writeFileSync(steraUiPath, steraUiContent, "utf-8");
|
|
1048
|
+
console.log(` ${CHECK} Created ${path12.relative(cwd, steraUiPath)}`);
|
|
1049
|
+
const existingCssPath = path12.resolve(cwd, project.existingCssFile);
|
|
585
1050
|
insertImportLine(existingCssPath, "@import './stera-ui.css';");
|
|
586
1051
|
console.log(` ${CHECK} Added @import to ${project.existingCssFile}`);
|
|
587
1052
|
} catch {
|
|
588
1053
|
}
|
|
589
1054
|
}
|
|
590
|
-
try {
|
|
591
|
-
const fontsItem = await fetchRegistryItem("fonts");
|
|
592
|
-
const fontsPath = path8.join(cssDir, "fonts.css");
|
|
593
|
-
fs6.mkdirSync(path8.dirname(fontsPath), { recursive: true });
|
|
594
|
-
if (!fs6.existsSync(fontsPath)) {
|
|
595
|
-
fs6.writeFileSync(fontsPath, fontsItem.files[0].content ?? "", "utf-8");
|
|
596
|
-
console.log(` ${CHECK} Created ${path8.relative(cwd, fontsPath)}`);
|
|
597
|
-
} else {
|
|
598
|
-
console.log(` ${dim(path8.relative(cwd, fontsPath) + " (already exists, skipped)")}`);
|
|
599
|
-
}
|
|
600
|
-
} catch {
|
|
601
|
-
}
|
|
602
1055
|
} else {
|
|
603
1056
|
const resolved = await resolveDependencies(["globals"]);
|
|
604
1057
|
const { written } = await writeComponentFiles(resolved, config, cwd);
|
|
@@ -606,6 +1059,13 @@ async function init(options) {
|
|
|
606
1059
|
console.log(` ${CHECK} Created ${file}`);
|
|
607
1060
|
}
|
|
608
1061
|
}
|
|
1062
|
+
const fontResult = await executeFontStrategy(
|
|
1063
|
+
fontChoice,
|
|
1064
|
+
project,
|
|
1065
|
+
detectedFonts,
|
|
1066
|
+
config,
|
|
1067
|
+
cwd
|
|
1068
|
+
);
|
|
609
1069
|
try {
|
|
610
1070
|
const globalsItem = await fetchRegistryItem("globals");
|
|
611
1071
|
if (globalsItem.dependencies) {
|
|
@@ -618,8 +1078,18 @@ async function init(options) {
|
|
|
618
1078
|
console.log("");
|
|
619
1079
|
console.log(" Next steps:");
|
|
620
1080
|
console.log(" stera-ui add <component>");
|
|
621
|
-
|
|
622
|
-
|
|
1081
|
+
if (fontResult.preloadTags) {
|
|
1082
|
+
console.log("");
|
|
1083
|
+
console.log(` ${dim("For optimal font loading, add these to your HTML <head>:")}`);
|
|
1084
|
+
console.log("");
|
|
1085
|
+
for (const line of fontResult.preloadTags.split("\n")) {
|
|
1086
|
+
console.log(` ${dim(line)}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (fontChoice.strategy === "skip") {
|
|
1090
|
+
console.log("");
|
|
1091
|
+
console.log(` ${dim("Font setup was skipped. See https://ui.stera.sh for font configuration docs.")}`);
|
|
1092
|
+
}
|
|
623
1093
|
console.log("");
|
|
624
1094
|
}
|
|
625
1095
|
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
registryItemFileSchema,
|
|
12
12
|
registryItemSchema,
|
|
13
13
|
registryItemTypeSchema
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AXIACJ2C.js";
|
|
15
15
|
import {
|
|
16
16
|
CHECK,
|
|
17
17
|
CONFIG_FILE,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
resolveOutputPath,
|
|
32
32
|
transformImports,
|
|
33
33
|
writeComponentFiles
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-MFUZBXO6.js";
|
|
35
35
|
export {
|
|
36
36
|
CHECK,
|
|
37
37
|
CONFIG_FILE,
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -8,13 +8,13 @@ declare const registryItemFileSchema: z.ZodObject<{
|
|
|
8
8
|
content: z.ZodOptional<z.ZodString>;
|
|
9
9
|
target: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
path: string;
|
|
12
11
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
12
|
+
path: string;
|
|
13
13
|
content?: string | undefined;
|
|
14
14
|
target?: string | undefined;
|
|
15
15
|
}, {
|
|
16
|
-
path: string;
|
|
17
16
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
17
|
+
path: string;
|
|
18
18
|
content?: string | undefined;
|
|
19
19
|
target?: string | undefined;
|
|
20
20
|
}>;
|
|
@@ -30,33 +30,33 @@ declare const registryItemSchema: z.ZodObject<{
|
|
|
30
30
|
content: z.ZodOptional<z.ZodString>;
|
|
31
31
|
target: z.ZodOptional<z.ZodString>;
|
|
32
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
|
-
path: string;
|
|
34
33
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
34
|
+
path: string;
|
|
35
35
|
content?: string | undefined;
|
|
36
36
|
target?: string | undefined;
|
|
37
37
|
}, {
|
|
38
|
-
path: string;
|
|
39
38
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
39
|
+
path: string;
|
|
40
40
|
content?: string | undefined;
|
|
41
41
|
target?: string | undefined;
|
|
42
42
|
}>, "many">;
|
|
43
43
|
}, "strip", z.ZodTypeAny, {
|
|
44
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
45
44
|
name: string;
|
|
45
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
46
46
|
files: {
|
|
47
|
-
path: string;
|
|
48
47
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
48
|
+
path: string;
|
|
49
49
|
content?: string | undefined;
|
|
50
50
|
target?: string | undefined;
|
|
51
51
|
}[];
|
|
52
52
|
dependencies?: string[] | undefined;
|
|
53
53
|
registryDependencies?: string[] | undefined;
|
|
54
54
|
}, {
|
|
55
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
56
55
|
name: string;
|
|
56
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
57
57
|
files: {
|
|
58
|
-
path: string;
|
|
59
58
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
59
|
+
path: string;
|
|
60
60
|
content?: string | undefined;
|
|
61
61
|
target?: string | undefined;
|
|
62
62
|
}[];
|
|
@@ -77,33 +77,33 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
77
77
|
content: z.ZodOptional<z.ZodString>;
|
|
78
78
|
target: z.ZodOptional<z.ZodString>;
|
|
79
79
|
}, "strip", z.ZodTypeAny, {
|
|
80
|
-
path: string;
|
|
81
80
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
81
|
+
path: string;
|
|
82
82
|
content?: string | undefined;
|
|
83
83
|
target?: string | undefined;
|
|
84
84
|
}, {
|
|
85
|
-
path: string;
|
|
86
85
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
86
|
+
path: string;
|
|
87
87
|
content?: string | undefined;
|
|
88
88
|
target?: string | undefined;
|
|
89
89
|
}>, "many">;
|
|
90
90
|
}, "strip", z.ZodTypeAny, {
|
|
91
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
92
91
|
name: string;
|
|
92
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
93
93
|
files: {
|
|
94
|
-
path: string;
|
|
95
94
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
95
|
+
path: string;
|
|
96
96
|
content?: string | undefined;
|
|
97
97
|
target?: string | undefined;
|
|
98
98
|
}[];
|
|
99
99
|
dependencies?: string[] | undefined;
|
|
100
100
|
registryDependencies?: string[] | undefined;
|
|
101
101
|
}, {
|
|
102
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
103
102
|
name: string;
|
|
103
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
104
104
|
files: {
|
|
105
|
-
path: string;
|
|
106
105
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
106
|
+
path: string;
|
|
107
107
|
content?: string | undefined;
|
|
108
108
|
target?: string | undefined;
|
|
109
109
|
}[];
|
|
@@ -113,11 +113,11 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
113
113
|
}, "strip", z.ZodTypeAny, {
|
|
114
114
|
name: string;
|
|
115
115
|
items: {
|
|
116
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
117
116
|
name: string;
|
|
117
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
118
118
|
files: {
|
|
119
|
-
path: string;
|
|
120
119
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
120
|
+
path: string;
|
|
121
121
|
content?: string | undefined;
|
|
122
122
|
target?: string | undefined;
|
|
123
123
|
}[];
|
|
@@ -127,11 +127,11 @@ declare const registryIndexSchema: z.ZodObject<{
|
|
|
127
127
|
}, {
|
|
128
128
|
name: string;
|
|
129
129
|
items: {
|
|
130
|
-
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
131
130
|
name: string;
|
|
131
|
+
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
132
132
|
files: {
|
|
133
|
-
path: string;
|
|
134
133
|
type: "registry:ui" | "registry:component" | "registry:hook" | "registry:lib" | "registry:style";
|
|
134
|
+
path: string;
|
|
135
135
|
content?: string | undefined;
|
|
136
136
|
target?: string | undefined;
|
|
137
137
|
}[];
|
|
@@ -163,6 +163,16 @@ declare const configSchema: z.ZodObject<{
|
|
|
163
163
|
lib: string;
|
|
164
164
|
hooks: string;
|
|
165
165
|
}>;
|
|
166
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
167
|
+
strategy: z.ZodEnum<["stera-default", "keep-existing", "skip"]>;
|
|
168
|
+
nextFont: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
}, "strip", z.ZodTypeAny, {
|
|
170
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
171
|
+
nextFont?: boolean | undefined;
|
|
172
|
+
}, {
|
|
173
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
174
|
+
nextFont?: boolean | undefined;
|
|
175
|
+
}>>;
|
|
166
176
|
}, "strip", z.ZodTypeAny, {
|
|
167
177
|
version: 1;
|
|
168
178
|
css: string;
|
|
@@ -174,6 +184,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
174
184
|
hooks: string;
|
|
175
185
|
};
|
|
176
186
|
$schema?: string | undefined;
|
|
187
|
+
fonts?: {
|
|
188
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
189
|
+
nextFont?: boolean | undefined;
|
|
190
|
+
} | undefined;
|
|
177
191
|
}, {
|
|
178
192
|
version: 1;
|
|
179
193
|
css: string;
|
|
@@ -185,6 +199,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
185
199
|
hooks: string;
|
|
186
200
|
};
|
|
187
201
|
$schema?: string | undefined;
|
|
202
|
+
fonts?: {
|
|
203
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
204
|
+
nextFont?: boolean | undefined;
|
|
205
|
+
} | undefined;
|
|
188
206
|
}>;
|
|
189
207
|
type SteraConfig = z.infer<typeof configSchema>;
|
|
190
208
|
|
package/dist/schema/index.js
CHANGED
package/dist/utils/index.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ interface SteraConfig {
|
|
|
12
12
|
lib: string;
|
|
13
13
|
hooks: string;
|
|
14
14
|
};
|
|
15
|
+
fonts?: {
|
|
16
|
+
strategy: "stera-default" | "keep-existing" | "skip";
|
|
17
|
+
nextFont?: boolean;
|
|
18
|
+
};
|
|
15
19
|
}
|
|
16
20
|
declare const CONFIG_FILE = "components.json";
|
|
17
21
|
declare function findConfigPath(cwd: string): string | null;
|
|
@@ -50,6 +54,8 @@ interface ProjectInfo {
|
|
|
50
54
|
framework: "next" | "vite" | "other";
|
|
51
55
|
rsc: boolean;
|
|
52
56
|
existingCssFile: string | null;
|
|
57
|
+
/** Path to the root layout file (layout.tsx for Next.js, index.html for Vite) */
|
|
58
|
+
layoutFile: string | null;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
55
61
|
* Detect the project structure to inform configuration defaults.
|
package/dist/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stera-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Add Stera UI components to your project.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@antfu/ni": "^30.0.0",
|
|
37
|
+
"@inquirer/prompts": "^7.0.0",
|
|
37
38
|
"commander": "^13.1.0",
|
|
38
39
|
"execa": "^9.6.1",
|
|
39
40
|
"tsconfig-paths": "^4.2.0",
|