pni 1.0.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.
Files changed (50) hide show
  1. package/dist/add-three-app.d.ts +6 -0
  2. package/dist/add-three-app.js +111 -0
  3. package/dist/app.d.ts +11 -0
  4. package/dist/app.js +143 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +71 -0
  7. package/dist/components/FeatureSelector.d.ts +21 -0
  8. package/dist/components/FeatureSelector.js +175 -0
  9. package/dist/components/ProgressIndicator.d.ts +7 -0
  10. package/dist/components/ProgressIndicator.js +46 -0
  11. package/dist/components/Summary.d.ts +8 -0
  12. package/dist/components/Summary.js +51 -0
  13. package/dist/components/WelcomeHeader.d.ts +2 -0
  14. package/dist/components/WelcomeHeader.js +8 -0
  15. package/dist/template_code/three/README.md +146 -0
  16. package/dist/template_code/three/World.js +133 -0
  17. package/dist/template_code/three/camera.js +30 -0
  18. package/dist/template_code/three/components/GlobeSphere.js +608 -0
  19. package/dist/template_code/three/components/cube.js +27 -0
  20. package/dist/template_code/three/components/lights.js +16 -0
  21. package/dist/template_code/three/components/sphere.js +26 -0
  22. package/dist/template_code/three/components/torus.js +25 -0
  23. package/dist/template_code/three/scene.js +28 -0
  24. package/dist/template_code/three/systems/Loop.js +43 -0
  25. package/dist/template_code/three/systems/Resizer.js +26 -0
  26. package/dist/template_code/three/systems/controls.js +19 -0
  27. package/dist/template_code/three/systems/post-processing.js +50 -0
  28. package/dist/template_code/three/systems/renderer.js +17 -0
  29. package/dist/template_code/three/utils/deviceDetector.js +141 -0
  30. package/dist/template_code/three/utils/gltfLoader.js +14 -0
  31. package/dist/template_code/three/utils/loadKTX2Texture.js +42 -0
  32. package/dist/template_code/three/utils/textureLoader.js +21 -0
  33. package/dist/utils/add-three.d.ts +7 -0
  34. package/dist/utils/add-three.js +288 -0
  35. package/dist/utils/app-creation.d.ts +4 -0
  36. package/dist/utils/app-creation.js +35 -0
  37. package/dist/utils/config-generator.d.ts +6 -0
  38. package/dist/utils/config-generator.js +508 -0
  39. package/dist/utils/css-variables.d.ts +4 -0
  40. package/dist/utils/css-variables.js +316 -0
  41. package/dist/utils/dependencies.d.ts +11 -0
  42. package/dist/utils/dependencies.js +68 -0
  43. package/dist/utils/package-manager.d.ts +4 -0
  44. package/dist/utils/package-manager.js +56 -0
  45. package/dist/utils/project-detection.d.ts +2 -0
  46. package/dist/utils/project-detection.js +60 -0
  47. package/dist/utils/shadcn-setup.d.ts +2 -0
  48. package/dist/utils/shadcn-setup.js +46 -0
  49. package/package.json +81 -0
  50. package/readme.md +119 -0
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ dir?: string;
4
+ };
5
+ export default function AddThreeApp({ dir }: Props): React.JSX.Element | null;
6
+ export {};
@@ -0,0 +1,111 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { addThree } from './utils/add-three.js';
4
+ import ProgressIndicator from './components/ProgressIndicator.js';
5
+ import { resolve } from 'path';
6
+ export default function AddThreeApp({ dir }) {
7
+ const [step, setStep] = useState('checking');
8
+ const [error, setError] = useState(null);
9
+ const [result, setResult] = useState(null);
10
+ useEffect(() => {
11
+ async function initialize() {
12
+ try {
13
+ const cwd = dir ? resolve(dir) : process.cwd();
14
+ setStep('copying');
15
+ const result = await addThree(cwd);
16
+ setResult(result);
17
+ setStep('completed');
18
+ }
19
+ catch (err) {
20
+ setError(err instanceof Error ? err.message : 'Unknown error');
21
+ setStep('error');
22
+ }
23
+ }
24
+ initialize();
25
+ }, [dir]);
26
+ if (step === 'checking') {
27
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
28
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDD0D Three.js Setup"),
29
+ React.createElement(Text, null, " "),
30
+ React.createElement(ProgressIndicator, { message: "Checking Three.js installation...", status: "in-progress" })));
31
+ }
32
+ if (step === 'copying') {
33
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
34
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDCE6 Adding Three.js Template"),
35
+ React.createElement(Text, null, " "),
36
+ React.createElement(ProgressIndicator, { message: "Copying template files...", status: "in-progress" })));
37
+ }
38
+ if (step === 'error') {
39
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
40
+ React.createElement(Box, { borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, marginBottom: 1 },
41
+ React.createElement(Text, { color: "red", bold: true }, "\u2717 Error")),
42
+ React.createElement(Box, { paddingLeft: 2 },
43
+ React.createElement(Text, { color: "red" }, error))));
44
+ }
45
+ if (step === 'completed' && result) {
46
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
47
+ React.createElement(Box, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, marginBottom: 1 },
48
+ React.createElement(Text, { color: "green", bold: true }, "\u2728 Three.js template added successfully!")),
49
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 1 },
50
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDCC1 Files Created"),
51
+ React.createElement(Text, null, " "),
52
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 },
53
+ React.createElement(Text, null,
54
+ React.createElement(Text, { color: "yellow" }, "\u2022"),
55
+ " ",
56
+ React.createElement(Text, { color: "white" },
57
+ result.threePath,
58
+ "/"),
59
+ " ",
60
+ React.createElement(Text, { color: "gray" }, "(template files)")),
61
+ React.createElement(Text, null,
62
+ React.createElement(Text, { color: "yellow" }, "\u2022"),
63
+ " ",
64
+ React.createElement(Text, { color: "white" },
65
+ result.projectType === 'vue' ? 'src' : 'app',
66
+ "/composables/"),
67
+ React.createElement(Text, { color: "green" },
68
+ result.directoryName,
69
+ "/usethree.",
70
+ result.fileExtension)),
71
+ React.createElement(Text, null,
72
+ React.createElement(Text, { color: "yellow" }, "\u2022"),
73
+ " ",
74
+ React.createElement(Text, { color: "white" },
75
+ result.projectType === 'vue' ? 'src' : 'app',
76
+ "/composables/"),
77
+ React.createElement(Text, { color: "green" },
78
+ result.directoryName,
79
+ "/useThreeAdvanced.",
80
+ result.fileExtension))),
81
+ React.createElement(Text, null, " "),
82
+ React.createElement(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, paddingY: 1 },
83
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDE80 Next Steps"),
84
+ React.createElement(Text, null, " "),
85
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 },
86
+ React.createElement(Text, null,
87
+ React.createElement(Text, { color: "yellow" }, "1."),
88
+ " ",
89
+ React.createElement(Text, { color: "white" }, "Import and use the composable in your "),
90
+ React.createElement(Text, { color: "green", bold: true }, result.projectType === 'vue' ? 'Vue' : 'Nuxt'),
91
+ React.createElement(Text, { color: "white" }, " component")),
92
+ React.createElement(Text, null,
93
+ React.createElement(Text, { color: "yellow" }, "2."),
94
+ " ",
95
+ React.createElement(Text, { color: "gray" }, "Example: "),
96
+ React.createElement(Text, { color: "cyan" }, "import"),
97
+ " ",
98
+ React.createElement(Text, { color: "yellow" },
99
+ '{',
100
+ " useThree ",
101
+ '}'),
102
+ " ",
103
+ React.createElement(Text, { color: "cyan" }, "from"),
104
+ " ",
105
+ React.createElement(Text, { color: "green" },
106
+ "'@/composables/",
107
+ result.directoryName,
108
+ "/usethree'")))))));
109
+ }
110
+ return null;
111
+ }
package/dist/app.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ nuxt?: boolean;
4
+ vue?: boolean;
5
+ threejs?: boolean;
6
+ cssVars?: boolean;
7
+ dir?: string;
8
+ nonInteractive?: boolean;
9
+ };
10
+ export default function App({ nuxt, vue, threejs, cssVars, dir, nonInteractive, }: Props): React.JSX.Element | null;
11
+ export {};
package/dist/app.js ADDED
@@ -0,0 +1,143 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { detectProjectType } from './utils/project-detection.js';
4
+ import { createApp } from './utils/app-creation.js';
5
+ import { detectPackageManager, getInstallCommand, getDevInstallCommand, } from './utils/package-manager.js';
6
+ import { getDependencies } from './utils/dependencies.js';
7
+ import { generateConfigFiles } from './utils/config-generator.js';
8
+ import { generateCSSVariables, updateIndexHtml, createTypographyPage } from './utils/css-variables.js';
9
+ import { setupShadcnNuxt } from './utils/shadcn-setup.js';
10
+ import FeatureSelector from './components/FeatureSelector.js';
11
+ import ProgressIndicator from './components/ProgressIndicator.js';
12
+ import Summary from './components/Summary.js';
13
+ import WelcomeHeader from './components/WelcomeHeader.js';
14
+ import { execSync } from 'child_process';
15
+ import { join, resolve } from 'path';
16
+ export default function App({ nuxt = false, vue = false, threejs = false, cssVars = false, dir, nonInteractive = false, }) {
17
+ const [step, setStep] = useState('detecting');
18
+ const [projectType, setProjectType] = useState('none');
19
+ const [features, setFeatures] = useState(null);
20
+ const [error, setError] = useState(null);
21
+ const [projectPath, setProjectPath] = useState('');
22
+ useEffect(() => {
23
+ async function initialize() {
24
+ try {
25
+ const cwd = dir ? resolve(dir) : process.cwd();
26
+ setProjectPath(cwd);
27
+ const detected = await detectProjectType(cwd);
28
+ setProjectType(detected);
29
+ setStep('selecting');
30
+ }
31
+ catch (err) {
32
+ setError(err instanceof Error ? err.message : 'Unknown error');
33
+ setStep('error');
34
+ }
35
+ }
36
+ initialize();
37
+ }, [dir]);
38
+ const handleFeatureSelect = async (selectedFeatures) => {
39
+ setFeatures(selectedFeatures);
40
+ const finalProjectType = nuxt
41
+ ? 'nuxt'
42
+ : vue
43
+ ? 'vue'
44
+ : selectedFeatures.projectType === 'none'
45
+ ? 'nuxt' // Default to nuxt if none selected
46
+ : selectedFeatures.projectType;
47
+ // finalProjectType is guaranteed to be 'nuxt' or 'vue' at this point
48
+ try {
49
+ const cwd = dir ? resolve(dir) : process.cwd();
50
+ let workingPath = cwd;
51
+ // Create app if needed
52
+ if (projectType === 'none' && selectedFeatures.projectName) {
53
+ setStep('creating');
54
+ const parentDir = dir ? resolve(dir) : process.cwd();
55
+ await createApp(finalProjectType, parentDir, selectedFeatures.projectName);
56
+ workingPath = join(parentDir, selectedFeatures.projectName);
57
+ setProjectPath(workingPath);
58
+ }
59
+ // Install dependencies
60
+ setStep('installing');
61
+ const pm = await detectPackageManager(workingPath);
62
+ const deps = getDependencies(finalProjectType, selectedFeatures.threejs, selectedFeatures.cssVars);
63
+ if (deps.production.length > 0) {
64
+ const installCmd = getInstallCommand(pm, deps.production);
65
+ execSync(installCmd, { cwd: workingPath, stdio: 'inherit' });
66
+ }
67
+ if (deps.dev.length > 0) {
68
+ const devInstallCmd = getDevInstallCommand(pm, deps.dev);
69
+ execSync(devInstallCmd, { cwd: workingPath, stdio: 'inherit' });
70
+ }
71
+ // Generate configuration files
72
+ setStep('configuring');
73
+ await generateConfigFiles(finalProjectType, workingPath, selectedFeatures.threejs, selectedFeatures.cssVars);
74
+ // Generate CSS variables (always enabled - will be overwritten after shadcn-setup)
75
+ if (finalProjectType === 'nuxt') {
76
+ // For Nuxt, first create basic tailwind.css
77
+ await generateCSSVariables(finalProjectType, workingPath, true);
78
+ // Then run shadcn setup
79
+ setStep('configuring');
80
+ await setupShadcnNuxt(workingPath, pm);
81
+ // Finally, replace with full CSS content
82
+ await generateCSSVariables(finalProjectType, workingPath, false);
83
+ // Create typography page after CSS variables are set up
84
+ if (selectedFeatures.cssVars) {
85
+ await createTypographyPage(workingPath);
86
+ }
87
+ }
88
+ else {
89
+ await generateCSSVariables(finalProjectType, workingPath);
90
+ // For Vue projects, update index.html with stylesheet link
91
+ if (finalProjectType === 'vue' && selectedFeatures.cssVars) {
92
+ await updateIndexHtml(workingPath);
93
+ }
94
+ }
95
+ setStep('completed');
96
+ }
97
+ catch (err) {
98
+ setError(err instanceof Error ? err.message : 'Unknown error');
99
+ setStep('error');
100
+ }
101
+ };
102
+ if (step === 'detecting') {
103
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
104
+ React.createElement(WelcomeHeader, null),
105
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDD0D Analyzing project..."),
106
+ React.createElement(Text, null, " "),
107
+ React.createElement(ProgressIndicator, { message: "Detecting project type...", status: "in-progress" })));
108
+ }
109
+ if (step === 'selecting') {
110
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
111
+ React.createElement(WelcomeHeader, null),
112
+ React.createElement(FeatureSelector, { detectedType: projectType, onSelect: handleFeatureSelect, nonInteractive: nonInteractive, flags: { nuxt, vue, threejs, cssVars } })));
113
+ }
114
+ if (step === 'creating') {
115
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
116
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDCE6 Creating project..."),
117
+ React.createElement(Text, null, " "),
118
+ React.createElement(ProgressIndicator, { message: "Setting up project structure...", status: "in-progress" })));
119
+ }
120
+ if (step === 'installing') {
121
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
122
+ React.createElement(Text, { color: "yellow", bold: true }, "\uD83D\uDCE5 Installing dependencies..."),
123
+ React.createElement(Text, null, " "),
124
+ React.createElement(ProgressIndicator, { message: "This may take a few moments...", status: "in-progress" })));
125
+ }
126
+ if (step === 'configuring') {
127
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
128
+ React.createElement(Text, { color: "magenta", bold: true }, "\u2699\uFE0F Configuring project..."),
129
+ React.createElement(Text, null, " "),
130
+ React.createElement(ProgressIndicator, { message: "Generating configuration files...", status: "in-progress" })));
131
+ }
132
+ if (step === 'error') {
133
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
134
+ React.createElement(Box, { borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1, marginBottom: 1 },
135
+ React.createElement(Text, { color: "red", bold: true }, "\u2717 Error")),
136
+ React.createElement(Box, { paddingLeft: 2 },
137
+ React.createElement(Text, { color: "red" }, error))));
138
+ }
139
+ if (step === 'completed' && features) {
140
+ return React.createElement(Summary, { features: features, projectPath: projectPath });
141
+ }
142
+ return null;
143
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import React from 'react';
3
+ import { render } from 'ink';
4
+ import meow from 'meow';
5
+ import App from './app.js';
6
+ import AddThreeApp from './add-three-app.js';
7
+ // import { render as renderTestingLibrary } from 'ink-testing-library';
8
+ const cli = meow(`
9
+ Usage
10
+ $ pni [options]
11
+ $ pni add three [options]
12
+
13
+ Options
14
+ --nuxt Force Nuxt project type
15
+ --vue Force Vue project type
16
+ --threejs Include Three.js setup
17
+ --css-vars Include CSS variables (shadcn-style) setup
18
+ --dir <path> Target directory (default: current directory)
19
+ --non-interactive Skip prompts, use flags only
20
+
21
+ Examples
22
+ $ pni
23
+ Interactive setup
24
+
25
+ $ pni --nuxt --threejs --css-vars
26
+ Create Nuxt app with Three.js and CSS variables
27
+
28
+ $ pni --vue --threejs
29
+ Create Vue app with Three.js
30
+
31
+ $ pni --css-vars
32
+ Add CSS variables to existing project
33
+
34
+ $ pni add three
35
+ Add Three.js template to existing project
36
+ `, {
37
+ importMeta: import.meta,
38
+ flags: {
39
+ nuxt: {
40
+ type: 'boolean',
41
+ default: false,
42
+ },
43
+ vue: {
44
+ type: 'boolean',
45
+ default: false,
46
+ },
47
+ threejs: {
48
+ type: 'boolean',
49
+ default: false,
50
+ },
51
+ cssVars: {
52
+ type: 'boolean',
53
+ default: false,
54
+ },
55
+ dir: {
56
+ type: 'string',
57
+ },
58
+ nonInteractive: {
59
+ type: 'boolean',
60
+ default: false,
61
+ },
62
+ },
63
+ });
64
+ // Check for subcommands
65
+ const [command, subcommand] = cli.input;
66
+ if (command === 'add' && subcommand === 'three') {
67
+ render(React.createElement(AddThreeApp, { dir: cli.flags.dir }));
68
+ }
69
+ else {
70
+ render(React.createElement(App, { nuxt: cli.flags.nuxt, vue: cli.flags.vue, threejs: cli.flags.threejs, cssVars: cli.flags.cssVars, dir: cli.flags.dir, nonInteractive: cli.flags.nonInteractive }));
71
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import type { ProjectType } from '../utils/project-detection.js';
3
+ export interface SelectedFeatures {
4
+ projectType: ProjectType;
5
+ threejs: boolean;
6
+ cssVars: boolean;
7
+ projectName?: string;
8
+ }
9
+ type Props = {
10
+ detectedType: ProjectType;
11
+ onSelect: (features: SelectedFeatures) => void;
12
+ nonInteractive?: boolean;
13
+ flags?: {
14
+ nuxt?: boolean;
15
+ vue?: boolean;
16
+ threejs?: boolean;
17
+ cssVars?: boolean;
18
+ };
19
+ };
20
+ export default function FeatureSelector({ detectedType, onSelect, nonInteractive, flags, }: Props): React.JSX.Element;
21
+ export {};
@@ -0,0 +1,175 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Text, Box, useInput } from 'ink';
3
+ export default function FeatureSelector({ detectedType, onSelect, nonInteractive = false, flags = {}, }) {
4
+ const [step, setStep] = useState(0);
5
+ const [projectType, setProjectType] = useState(flags.nuxt
6
+ ? 'nuxt'
7
+ : flags.vue
8
+ ? 'vue'
9
+ : detectedType !== 'none'
10
+ ? detectedType
11
+ : 'nuxt');
12
+ const [threejs, setThreejs] = useState(flags.threejs ?? false);
13
+ const [projectName, setProjectName] = useState('');
14
+ const cssVars = true; // Always enabled - CSS will be overwritten after shadcn-setup
15
+ useEffect(() => {
16
+ if (nonInteractive) {
17
+ // Auto-select based on flags or detected type
18
+ onSelect({
19
+ projectType,
20
+ threejs,
21
+ cssVars,
22
+ });
23
+ }
24
+ }, [nonInteractive, projectType, threejs, cssVars, onSelect]);
25
+ useInput((input, key) => {
26
+ if (nonInteractive) {
27
+ return;
28
+ }
29
+ if (key.return) {
30
+ if (step === 0 && detectedType === 'none') {
31
+ // Project type selected
32
+ setStep(1);
33
+ }
34
+ else if (step === 1 && detectedType === 'none') {
35
+ // Project name entered
36
+ setStep(2);
37
+ }
38
+ else if (step === (detectedType === 'none' ? 2 : 0)) {
39
+ // Three.js question answered - CSS vars are always enabled
40
+ onSelect({
41
+ projectType,
42
+ threejs,
43
+ cssVars: true, // Always enable CSS vars
44
+ projectName: detectedType === 'none' ? projectName : undefined,
45
+ });
46
+ }
47
+ }
48
+ if (key.upArrow || key.downArrow) {
49
+ if (step === 0 && detectedType === 'none') {
50
+ setProjectType(prev => (prev === 'nuxt' ? 'vue' : 'nuxt'));
51
+ }
52
+ else if (step === (detectedType === 'none' ? 2 : 0)) {
53
+ // Three.js question
54
+ setThreejs(prev => !prev);
55
+ }
56
+ }
57
+ if (input === 'y' || input === 'Y') {
58
+ if (step === (detectedType === 'none' ? 2 : 0)) {
59
+ setThreejs(true);
60
+ }
61
+ }
62
+ if (input === 'n' || input === 'N') {
63
+ if (step === (detectedType === 'none' ? 2 : 0)) {
64
+ setThreejs(false);
65
+ }
66
+ }
67
+ // Handle project name input
68
+ if (step === 1 &&
69
+ detectedType === 'none' &&
70
+ !key.return &&
71
+ !key.upArrow &&
72
+ !key.downArrow) {
73
+ if (key.backspace || key.delete) {
74
+ setProjectName(prev => prev.slice(0, -1));
75
+ }
76
+ else if (input.length === 1) {
77
+ setProjectName(prev => prev + input);
78
+ }
79
+ }
80
+ });
81
+ if (nonInteractive) {
82
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
83
+ React.createElement(Text, { color: "cyan", bold: true }, "\u26A1 Quick Setup (Non-Interactive)"),
84
+ React.createElement(Text, null, " "),
85
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 },
86
+ React.createElement(Text, null,
87
+ React.createElement(Text, { color: "magenta" }, "Project Type:"),
88
+ " ",
89
+ React.createElement(Text, { color: "green", bold: true }, projectType)),
90
+ React.createElement(Text, null,
91
+ React.createElement(Text, { color: "magenta" }, "Three.js:"),
92
+ " ",
93
+ React.createElement(Text, { color: threejs ? 'green' : 'gray', bold: true }, threejs ? 'Yes' : 'No')),
94
+ React.createElement(Text, null,
95
+ React.createElement(Text, { color: "magenta" }, "CSS Vars:"),
96
+ " ",
97
+ React.createElement(Text, { color: "green", bold: true }, "Yes (auto)")))));
98
+ }
99
+ if (detectedType === 'none') {
100
+ if (step === 0) {
101
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
102
+ React.createElement(Text, { color: "yellow", bold: true }, "\uD83D\uDCE6 No project detected"),
103
+ React.createElement(Text, { color: "gray" }, "Select project type:"),
104
+ React.createElement(Text, null, " "),
105
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 },
106
+ React.createElement(Text, null,
107
+ React.createElement(Text, { color: projectType === 'nuxt' ? 'cyan' : 'gray' }, projectType === 'nuxt' ? '→' : ' '),
108
+ React.createElement(Text, { color: projectType === 'nuxt' ? 'cyan' : 'white', bold: true },
109
+ "[",
110
+ React.createElement(Text, { color: projectType === 'nuxt' ? 'green' : 'gray' }, projectType === 'nuxt' ? '●' : '○'),
111
+ "] Nuxt")),
112
+ React.createElement(Text, null,
113
+ React.createElement(Text, { color: projectType === 'vue' ? 'cyan' : 'gray' }, projectType === 'vue' ? '→' : ' '),
114
+ React.createElement(Text, { color: projectType === 'vue' ? 'cyan' : 'white', bold: true },
115
+ "[",
116
+ React.createElement(Text, { color: projectType === 'vue' ? 'green' : 'gray' }, projectType === 'vue' ? '●' : '○'),
117
+ "] Vue"))),
118
+ React.createElement(Text, null, " "),
119
+ React.createElement(Text, { color: "gray" },
120
+ "Press ",
121
+ React.createElement(Text, { color: "cyan" }, "\u2191/\u2193"),
122
+ " to select, ",
123
+ React.createElement(Text, { color: "cyan" }, "Enter"),
124
+ " to continue")));
125
+ }
126
+ if (step === 1) {
127
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
128
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83D\uDCDD Project Setup"),
129
+ React.createElement(Text, null, " "),
130
+ React.createElement(Text, null,
131
+ React.createElement(Text, { color: "magenta" }, "Enter project name:"),
132
+ " ",
133
+ React.createElement(Text, { color: "green", bold: true }, projectName),
134
+ React.createElement(Text, { color: "cyan", bold: true }, "_")),
135
+ React.createElement(Text, null, " "),
136
+ React.createElement(Text, { color: "gray" },
137
+ "Type the project name and press ",
138
+ React.createElement(Text, { color: "cyan" }, "Enter"),
139
+ " when done")));
140
+ }
141
+ }
142
+ if (step === (detectedType === 'none' ? 2 : 0)) {
143
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
144
+ React.createElement(Text, { color: "cyan", bold: true }, "\uD83C\uDFA8 Additional Features"),
145
+ React.createElement(Text, null, " "),
146
+ React.createElement(Text, { color: "yellow", bold: true }, "Include Three.js setup?"),
147
+ React.createElement(Text, null, " "),
148
+ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 },
149
+ React.createElement(Text, null,
150
+ React.createElement(Text, { color: threejs ? 'cyan' : 'gray' }, threejs ? '→' : ' '),
151
+ React.createElement(Text, { color: threejs ? 'cyan' : 'white', bold: true },
152
+ "[",
153
+ React.createElement(Text, { color: threejs ? 'green' : 'gray' }, threejs ? '✓' : ' '),
154
+ "] ",
155
+ React.createElement(Text, { color: threejs ? 'green' : 'white' }, "Yes"))),
156
+ React.createElement(Text, null,
157
+ React.createElement(Text, { color: !threejs ? 'cyan' : 'gray' }, !threejs ? '→' : ' '),
158
+ React.createElement(Text, { color: !threejs ? 'cyan' : 'white', bold: true },
159
+ "[",
160
+ React.createElement(Text, { color: !threejs ? 'red' : 'gray' }, !threejs ? '✗' : ' '),
161
+ "] ",
162
+ React.createElement(Text, { color: !threejs ? 'red' : 'white' }, "No")))),
163
+ React.createElement(Text, null, " "),
164
+ React.createElement(Text, { color: "gray" },
165
+ "Use ",
166
+ React.createElement(Text, { color: "cyan" }, "\u2191/\u2193"),
167
+ " to toggle, ",
168
+ React.createElement(Text, { color: "cyan" }, "y/n"),
169
+ " to select, or ",
170
+ React.createElement(Text, { color: "cyan" }, "Enter"),
171
+ " to continue")));
172
+ }
173
+ return (React.createElement(Box, null,
174
+ React.createElement(Text, { color: "cyan" }, "Processing...")));
175
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ message: string;
4
+ status?: 'pending' | 'in-progress' | 'completed' | 'error';
5
+ };
6
+ export default function ProgressIndicator({ message, status, }: Props): React.JSX.Element;
7
+ export {};
@@ -0,0 +1,46 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Text, Box } from 'ink';
3
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4
+ export default function ProgressIndicator({ message, status = 'in-progress', }) {
5
+ const [frame, setFrame] = useState(0);
6
+ useEffect(() => {
7
+ if (status !== 'in-progress') {
8
+ return;
9
+ }
10
+ const timer = setInterval(() => {
11
+ setFrame(prev => (prev + 1) % SPINNER_FRAMES.length);
12
+ }, 100);
13
+ return () => {
14
+ clearInterval(timer);
15
+ };
16
+ }, [status]);
17
+ const getStatusIcon = () => {
18
+ switch (status) {
19
+ case 'completed':
20
+ return React.createElement(Text, { color: "green", bold: true }, "\u2713");
21
+ case 'error':
22
+ return React.createElement(Text, { color: "red", bold: true }, "\u2717");
23
+ case 'in-progress':
24
+ return React.createElement(Text, { color: "cyan", bold: true }, SPINNER_FRAMES[frame]);
25
+ case 'pending':
26
+ default:
27
+ return React.createElement(Text, { color: "gray" }, "\u25CB");
28
+ }
29
+ };
30
+ const getMessageColor = () => {
31
+ switch (status) {
32
+ case 'completed':
33
+ return 'green';
34
+ case 'error':
35
+ return 'red';
36
+ case 'in-progress':
37
+ return 'cyan';
38
+ case 'pending':
39
+ default:
40
+ return 'gray';
41
+ }
42
+ };
43
+ return (React.createElement(Box, null,
44
+ React.createElement(Box, { marginRight: 1 }, getStatusIcon()),
45
+ React.createElement(Text, { color: getMessageColor() }, message)));
46
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { SelectedFeatures } from './FeatureSelector.js';
3
+ type Props = {
4
+ features: SelectedFeatures;
5
+ projectPath: string;
6
+ };
7
+ export default function Summary({ features, projectPath }: Props): React.JSX.Element;
8
+ export {};