gradient-forge 1.0.1 → 1.0.2
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/cli/index.mjs +98 -50
- package/package.json +1 -1
package/cli/index.mjs
CHANGED
|
@@ -567,6 +567,7 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
let index = defaultIndex;
|
|
570
|
+
let buffer = "";
|
|
570
571
|
|
|
571
572
|
const render = () => {
|
|
572
573
|
process.stdout.write("\x1b[2J\x1b[H");
|
|
@@ -581,27 +582,35 @@ const promptSelect = async (title, items, defaultIndex = 0) => {
|
|
|
581
582
|
};
|
|
582
583
|
|
|
583
584
|
const onKey = (data) => {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
585
|
+
buffer += data.toString();
|
|
586
|
+
|
|
587
|
+
// Handle escape sequences for arrow keys
|
|
588
|
+
if (buffer.includes("\u001b[A")) {
|
|
589
|
+
// Up arrow
|
|
590
|
+
index = index > 0 ? index - 1 : items.length - 1;
|
|
591
|
+
buffer = "";
|
|
592
|
+
render();
|
|
593
|
+
} else if (buffer.includes("\u001b[B")) {
|
|
594
|
+
// Down arrow
|
|
595
|
+
index = index < items.length - 1 ? index + 1 : 0;
|
|
596
|
+
buffer = "";
|
|
597
|
+
render();
|
|
598
|
+
} else if (buffer.includes("\r") || buffer.includes("\n")) {
|
|
599
|
+
// Enter key
|
|
591
600
|
process.stdin.setRawMode(false);
|
|
592
601
|
process.stdin.pause();
|
|
593
602
|
process.stdin.removeListener("data", onKey);
|
|
594
603
|
process.stdout.write("\x1b[?25h");
|
|
595
604
|
resolve(items[index]);
|
|
596
605
|
return;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
606
|
+
} else if (buffer.includes("\u0003")) {
|
|
607
|
+
// Ctrl+C
|
|
608
|
+
process.stdin.setRawMode(false);
|
|
609
|
+
process.stdin.pause();
|
|
610
|
+
process.exit(1);
|
|
611
|
+
} else {
|
|
612
|
+
// Clear buffer after processing
|
|
613
|
+
buffer = "";
|
|
605
614
|
}
|
|
606
615
|
};
|
|
607
616
|
|
|
@@ -750,40 +759,73 @@ const generateSetupInstructions = (projectRoot, themeId, mode, themeLabel) => {
|
|
|
750
759
|
Theme: ${themeLabel}
|
|
751
760
|
Mode: ${mode}
|
|
752
761
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
✅ Already configured:
|
|
756
|
-
- app/gradient-forge.css - Theme CSS
|
|
757
|
-
- components/theme/theme-context.tsx - Theme provider + ThemeSwitcher component
|
|
758
|
-
- components/theme/theme-engine.ts - Theme definitions
|
|
759
|
-
- app/globals.css - Theme imported
|
|
762
|
+
✅ All files created automatically!
|
|
763
|
+
✅ Theme applied!
|
|
760
764
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
1. Wrap your app with ThemeProvider in layout.tsx:
|
|
764
|
-
import { ThemeProvider } from "./components/theme/theme-context";
|
|
765
|
-
|
|
766
|
-
export default function RootLayout({ children }) {
|
|
767
|
-
return (
|
|
768
|
-
<html lang="en" className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">
|
|
769
|
-
<body>
|
|
770
|
-
<ThemeProvider>{children}</ThemeProvider>
|
|
771
|
-
</body>
|
|
772
|
-
</html>
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
2. Add the theme switcher anywhere in your app:
|
|
777
|
-
import { ThemeSwitcher } from "./components/theme/theme-context";
|
|
778
|
-
|
|
779
|
-
// Add <ThemeSwitcher /> anywhere to let users change themes!
|
|
780
|
-
|
|
781
|
-
## Done! 🎉 Your app now has gradient themes!
|
|
765
|
+
Your app now has beautiful gradient themes! 🎉
|
|
782
766
|
|
|
783
767
|
For more themes, visit: https://gradient-forge.vercel.app
|
|
784
768
|
`;
|
|
785
769
|
};
|
|
786
770
|
|
|
771
|
+
const autoInjectLayout = (projectRoot, themeId, mode) => {
|
|
772
|
+
const layoutPaths = [
|
|
773
|
+
path.join(projectRoot, "app", "layout.tsx"),
|
|
774
|
+
path.join(projectRoot, "src", "app", "layout.tsx"),
|
|
775
|
+
];
|
|
776
|
+
|
|
777
|
+
for (const layoutPath of layoutPaths) {
|
|
778
|
+
if (fs.existsSync(layoutPath)) {
|
|
779
|
+
let content = fs.readFileSync(layoutPath, "utf8");
|
|
780
|
+
|
|
781
|
+
// Check if already has ThemeProvider
|
|
782
|
+
if (content.includes("ThemeProvider")) {
|
|
783
|
+
return { success: true, path: layoutPath, message: "Already configured" };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Add import - insert after the first import or at the start
|
|
787
|
+
const importStatement = `import { ThemeProvider } from "./components/theme/theme-context";`;
|
|
788
|
+
|
|
789
|
+
// Find position after the last import line
|
|
790
|
+
const lines = content.split('\n');
|
|
791
|
+
let insertIndex = 0;
|
|
792
|
+
for (let i = 0; i < lines.length; i++) {
|
|
793
|
+
if (lines[i].trim().startsWith('import ') && !lines[i].includes('from "') && !lines[i].includes("from '")) {
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (lines[i].trim().startsWith('import ')) {
|
|
797
|
+
insertIndex = i + 1;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Insert the import
|
|
802
|
+
if (insertIndex > 0 && lines[insertIndex - 1].trim() !== '') {
|
|
803
|
+
lines.splice(insertIndex, 0, importStatement);
|
|
804
|
+
} else {
|
|
805
|
+
lines.splice(insertIndex, 0, importStatement);
|
|
806
|
+
}
|
|
807
|
+
content = lines.join('\n');
|
|
808
|
+
|
|
809
|
+
// Wrap body content with ThemeProvider
|
|
810
|
+
content = content.replace(
|
|
811
|
+
/(<body[^>]*>)([\s\S]*)(<\/body>)/,
|
|
812
|
+
'$1<ThemeProvider>$2</ThemeProvider>$3'
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
// Add theme classes to html tag - use className for JSX
|
|
816
|
+
content = content.replace(
|
|
817
|
+
/<html[^>]*>/,
|
|
818
|
+
`<html lang="en" className="${mode} ${themeId}" data-theme="${themeId}" data-color-mode="${mode}">`
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
fs.writeFileSync(layoutPath, content);
|
|
822
|
+
return { success: true, path: layoutPath };
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return { success: false, message: "No layout.tsx found" };
|
|
827
|
+
};
|
|
828
|
+
|
|
787
829
|
const init = async () => {
|
|
788
830
|
log("");
|
|
789
831
|
log("🎨 Gradient Forge CLI - Initialize Theme");
|
|
@@ -863,17 +905,23 @@ const init = async () => {
|
|
|
863
905
|
}
|
|
864
906
|
}
|
|
865
907
|
|
|
866
|
-
//
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
908
|
+
// Auto-inject into layout.tsx
|
|
909
|
+
const layoutResult = autoInjectLayout(projectRoot, themeId, mode);
|
|
910
|
+
if (layoutResult.success) {
|
|
911
|
+
logSuccess(`Updated layout: ${path.relative(projectRoot, layoutResult.path)}`);
|
|
912
|
+
} else {
|
|
913
|
+
logInfo("Note: Could not auto-detect layout.tsx");
|
|
914
|
+
}
|
|
915
|
+
|
|
872
916
|
log("");
|
|
873
917
|
log("======================================");
|
|
874
918
|
logSuccess("Theme setup complete! 🎉");
|
|
875
919
|
log("");
|
|
876
|
-
log(
|
|
920
|
+
log(`Theme: ${selectedTheme.label} (${mode})`);
|
|
921
|
+
log("");
|
|
922
|
+
log("Your app now has beautiful gradient themes!");
|
|
923
|
+
log("Run: npm run dev");
|
|
924
|
+
log("");
|
|
877
925
|
};
|
|
878
926
|
|
|
879
927
|
const exportCommand = async () => {
|