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,316 @@
1
+ import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ const CSS_VARIABLES_CONTENT = `
4
+ @import "tailwindcss";
5
+ @import "tw-animate-css";
6
+
7
+ @font-face {
8
+ font-family: 'Helvetica';
9
+ src: url('~/assets/fonts/Helvetica.ttf') format('truetype');
10
+ font-weight: normal;
11
+ font-style: normal;
12
+ font-display: swap;
13
+ }
14
+
15
+ @custom-variant dark (&:is(.dark *));
16
+
17
+ @theme inline {
18
+ --spacing: 4rem;
19
+ --font-helvetica: 'Helvetica', sans-serif;
20
+ --radius-sm: calc(var(--radius) - 4px);
21
+ --radius-md: calc(var(--radius) - 2px);
22
+ --radius-lg: var(--radius);
23
+ --radius-xl: calc(var(--radius) + 4px);
24
+ --color-background: var(--background);
25
+ --color-foreground: var(--foreground);
26
+ --color-card: var(--card);
27
+ --color-card-foreground: var(--card-foreground);
28
+ --color-popover: var(--popover);
29
+ --color-popover-foreground: var(--popover-foreground);
30
+ --color-primary: var(--primary);
31
+ --color-primary-foreground: var(--primary-foreground);
32
+ --color-secondary: var(--secondary);
33
+ --color-secondary-foreground: var(--secondary-foreground);
34
+ --color-muted: var(--muted);
35
+ --color-muted-foreground: var(--muted-foreground);
36
+ --color-accent: var(--accent);
37
+ --color-accent-foreground: var(--accent-foreground);
38
+ --color-destructive: var(--destructive);
39
+ --color-border: var(--border);
40
+ --color-input: var(--input);
41
+ --color-ring: var(--ring);
42
+ --color-chart-1: var(--chart-1);
43
+ --color-chart-2: var(--chart-2);
44
+ --color-chart-3: var(--chart-3);
45
+ --color-chart-4: var(--chart-4);
46
+ --color-chart-5: var(--chart-5);
47
+ --color-sidebar: var(--sidebar);
48
+ --color-sidebar-foreground: var(--sidebar-foreground);
49
+ --color-sidebar-primary: var(--sidebar-primary);
50
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
51
+ --color-sidebar-accent: var(--sidebar-accent);
52
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
53
+ --color-sidebar-border: var(--sidebar-border);
54
+ --color-sidebar-ring: var(--sidebar-ring);
55
+ }
56
+
57
+ :root {
58
+ /* osmo scaling system starts here */
59
+ --size-unit: 16; /* body font-size in design - no px */
60
+ --size-container-ideal: 1440; /* screen-size in design - no px */
61
+ --size-container-min: 992px;
62
+ --size-container-max: 1920px;
63
+ --size-container: clamp(var(--size-container-min), 100vw, var(--size-container-max));
64
+ --size-font: calc(var(--size-container) / (var(--size-container-ideal) / var(--size-unit)));
65
+
66
+ /* 1 fluid pixel unit */
67
+ --fluid-px: calc(var(--size-font) / 16);
68
+ /* osmo scaling system ends here */
69
+
70
+
71
+
72
+ --radius: 0.625rem;
73
+ --background: oklch(1 0 0);
74
+ --foreground: oklch(0.145 0 0);
75
+ --card: oklch(1 0 0);
76
+ --card-foreground: oklch(0.145 0 0);
77
+ --popover: oklch(1 0 0);
78
+ --popover-foreground: oklch(0.145 0 0);
79
+ --primary: oklch(0.205 0 0);
80
+ --primary-foreground: oklch(0.985 0 0);
81
+ --secondary: oklch(0.97 0 0);
82
+ --secondary-foreground: oklch(0.205 0 0);
83
+ --muted: oklch(0.97 0 0);
84
+ --muted-foreground: oklch(0.556 0 0);
85
+ --accent: oklch(0.97 0 0);
86
+ --accent-foreground: oklch(0.205 0 0);
87
+ --destructive: oklch(0.577 0.245 27.325);
88
+ --border: oklch(0.922 0 0);
89
+ --input: oklch(0.922 0 0);
90
+ --ring: oklch(0.708 0 0);
91
+ --chart-1: oklch(0.646 0.222 41.116);
92
+ --chart-2: oklch(0.6 0.118 184.704);
93
+ --chart-3: oklch(0.398 0.07 227.392);
94
+ --chart-4: oklch(0.828 0.189 84.429);
95
+ --chart-5: oklch(0.769 0.188 70.08);
96
+ --sidebar: oklch(0.985 0 0);
97
+ --sidebar-foreground: oklch(0.145 0 0);
98
+ --sidebar-primary: oklch(0.205 0 0);
99
+ --sidebar-primary-foreground: oklch(0.985 0 0);
100
+ --sidebar-accent: oklch(0.97 0 0);
101
+ --sidebar-accent-foreground: oklch(0.205 0 0);
102
+ --sidebar-border: oklch(0.922 0 0);
103
+ --sidebar-ring: oklch(0.708 0 0);
104
+ }
105
+
106
+ .dark {
107
+ --background: oklch(0.145 0 0);
108
+ --foreground: oklch(0.985 0 0);
109
+ --card: oklch(0.205 0 0);
110
+ --card-foreground: oklch(0.985 0 0);
111
+ --popover: oklch(0.205 0 0);
112
+ --popover-foreground: oklch(0.985 0 0);
113
+ --primary: oklch(0.922 0 0);
114
+ --primary-foreground: oklch(0.205 0 0);
115
+ --secondary: oklch(0.269 0 0);
116
+ --secondary-foreground: oklch(0.985 0 0);
117
+ --muted: oklch(0.269 0 0);
118
+ --muted-foreground: oklch(0.708 0 0);
119
+ --accent: oklch(0.269 0 0);
120
+ --accent-foreground: oklch(0.985 0 0);
121
+ --destructive: oklch(0.704 0.191 22.216);
122
+ --border: oklch(1 0 0 / 10%);
123
+ --input: oklch(1 0 0 / 15%);
124
+ --ring: oklch(0.556 0 0);
125
+ --chart-1: oklch(0.488 0.243 264.376);
126
+ --chart-2: oklch(0.696 0.17 162.48);
127
+ --chart-3: oklch(0.769 0.188 70.08);
128
+ --chart-4: oklch(0.627 0.265 303.9);
129
+ --chart-5: oklch(0.645 0.246 16.439);
130
+ --sidebar: oklch(0.205 0 0);
131
+ --sidebar-foreground: oklch(0.985 0 0);
132
+ --sidebar-primary: oklch(0.488 0.243 264.376);
133
+ --sidebar-primary-foreground: oklch(0.985 0 0);
134
+ --sidebar-accent: oklch(0.269 0 0);
135
+ --sidebar-accent-foreground: oklch(0.985 0 0);
136
+ --sidebar-border: oklch(1 0 0 / 10%);
137
+ --sidebar-ring: oklch(0.556 0 0);
138
+ }
139
+
140
+ @layer base {
141
+ * {
142
+ @apply border-border outline-ring/50;
143
+ }
144
+
145
+ body {
146
+ @apply bg-background text-foreground;
147
+ }
148
+ }
149
+
150
+ html {
151
+ font-size: var(--fluid-px);
152
+ }
153
+
154
+ body {
155
+ /* figma design 16px fontsize = 16rem fluid pixel unit */
156
+ font-size: 16rem;
157
+ font-family: var(--font-helvetica);
158
+ }
159
+
160
+
161
+ /* osmo scaling system starts here */
162
+
163
+
164
+ .container {
165
+ max-width: var(--size-container);
166
+ }
167
+
168
+ .container.medium {
169
+ max-width: calc(var(--size-container) * 0.85);
170
+ }
171
+
172
+ .container.small {
173
+ max-width: calc(var(--size-container) * 0.7);
174
+ }
175
+
176
+ /* Tablet */
177
+ @media screen and (max-width: 991px) {
178
+ :root {
179
+ --size-container-ideal: 834;
180
+ /* screen-size in design - no px */
181
+ --size-container-min: 768px;
182
+ --size-container-max: 991px;
183
+ }
184
+
185
+ body {
186
+ background-color: red;
187
+ }
188
+ }
189
+
190
+ /* Mobile Landscape */
191
+ @media screen and (max-width: 767px) {
192
+ :root {
193
+ --size-container-ideal: 550;
194
+ /* screen-size in design - no px */
195
+ --size-container-min: 480px;
196
+ --size-container-max: 767px;
197
+ }
198
+
199
+ body {
200
+ background-color: blue;
201
+ }
202
+ }
203
+
204
+ /* Mobile Portrait */
205
+ @media screen and (max-width: 479px) {
206
+ :root {
207
+ --size-container-ideal: 375;
208
+ /* screen-size in design - no px */
209
+ --size-container-min: 320px;
210
+ --size-container-max: 479px;
211
+ }
212
+
213
+ body {
214
+ background-color: green;
215
+ }
216
+ }
217
+
218
+ `;
219
+ export async function generateCSSVariables(projectType, projectPath, initialSetup = false) {
220
+ let cssPath;
221
+ if (projectType === 'nuxt') {
222
+ // Nuxt: always use app/assets/css/tailwind.css
223
+ cssPath = join(projectPath, 'app', 'assets', 'css', 'tailwind.css');
224
+ }
225
+ else {
226
+ // Vue: src/assets/style.css
227
+ cssPath = join(projectPath, 'src', 'assets', 'style.css');
228
+ }
229
+ // Ensure directory exists
230
+ mkdirSync(dirname(cssPath), { recursive: true });
231
+ if (initialSetup && projectType === 'nuxt') {
232
+ // For initial Nuxt setup, just write the basic import
233
+ writeFileSync(cssPath, '@import "tailwindcss";\n', 'utf-8');
234
+ }
235
+ else {
236
+ // Overwrite the CSS file with full content
237
+ writeFileSync(cssPath, CSS_VARIABLES_CONTENT.trim() + '\n', 'utf-8');
238
+ }
239
+ }
240
+ export async function updateIndexHtml(projectPath) {
241
+ const indexPath = join(projectPath, 'index.html');
242
+ if (!existsSync(indexPath)) {
243
+ return; // Skip if index.html doesn't exist
244
+ }
245
+ let htmlContent = readFileSync(indexPath, 'utf-8');
246
+ // Check if stylesheet link already exists
247
+ if (htmlContent.includes('/src/assets/style.css')) {
248
+ return; // Already added
249
+ }
250
+ // Add stylesheet link in the head section
251
+ // Look for <head> tag and add the link before closing </head>
252
+ if (htmlContent.includes('</head>')) {
253
+ htmlContent = htmlContent.replace('</head>', ' <link href="/src/assets/style.css" rel="stylesheet">\n</head>');
254
+ }
255
+ else if (htmlContent.includes('<head>')) {
256
+ // If no closing head tag, add after opening head tag
257
+ htmlContent = htmlContent.replace('<head>', '<head>\n <link href="/src/assets/style.css" rel="stylesheet">');
258
+ }
259
+ else {
260
+ // If no head tag at all, add it after <html>
261
+ htmlContent = htmlContent.replace('<html>', '<html>\n<head>\n <link href="/src/assets/style.css" rel="stylesheet">\n</head>');
262
+ }
263
+ writeFileSync(indexPath, htmlContent, 'utf-8');
264
+ }
265
+ export async function createTypographyPage(projectPath) {
266
+ const typographyPagePath = join(projectPath, 'pages', 'typography', 'index.vue');
267
+ // Ensure directory exists
268
+ mkdirSync(dirname(typographyPagePath), { recursive: true });
269
+ const typographyPageContent = `<script setup lang="ts">
270
+ </script>
271
+
272
+ <template>
273
+ <section class="w-full py-20 px-4 min-h-screen flex items-center justify-center bg-[#DADADA] flex-col gap-16 ">
274
+ <div class="hero-container flex flex-col gap-16 leading-[1.2]">
275
+ <!-- figma design 120px fontsize -->
276
+ <h1 class="md:text-[120rem] text-[72rem]">Heading H1</h1>
277
+ <div class="divider"></div>
278
+ <!-- figma design 80px fontsize -->
279
+ <h2 class="md:text-[80rem] text-[64rem]">Heading H2</h2>
280
+ <div class="divider"></div>
281
+ <h3 class="md:text-[40rem] text-[32rem]">Heading H3</h3>
282
+ <!-- figma design 40px fontsize -->
283
+ <div class="divider"></div>
284
+ <h4 class="md:text-[28rem] text-[24rem]">Heading H4</h4>
285
+ <!-- figma design 28px fontsize -->
286
+ <div class="divider"></div>
287
+ <h5 class="md:text-[20rem] text-[18rem]">Heading H5</h5>
288
+ <!-- figma design 20px fontsize -->
289
+ <div class="divider"></div>
290
+ <!-- figma design 16px fontsize -->
291
+ <p class="text-[16rem] leading-[1.4] max-md:max-w-85.75 w-full font-helvetica">
292
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum
293
+ tristique. Duis cursus, all links in the website, eros dolor interdum nulla, ut commodo diam libero
294
+ vitae erat. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
295
+ laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae
296
+ vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
297
+ sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
298
+ </p>
299
+ </div>
300
+ </section>
301
+ </template>
302
+
303
+ <style scoped>
304
+ .hero-container {
305
+ max-width: calc(1184em / 16);
306
+ }
307
+
308
+ .divider {
309
+ background-color: #969696;
310
+ height: 1px;
311
+ width: 100%;
312
+ }
313
+ </style>
314
+ `;
315
+ writeFileSync(typographyPagePath, typographyPageContent, 'utf-8');
316
+ }
@@ -0,0 +1,11 @@
1
+ export interface DependencySet {
2
+ production: string[];
3
+ dev: string[];
4
+ }
5
+ export declare const BASE_NUXT_DEPS: DependencySet;
6
+ export declare const BASE_VUE_DEPS: DependencySet;
7
+ export declare const THREEJS_NUXT_DEPS: DependencySet;
8
+ export declare const THREEJS_VUE_DEPS: DependencySet;
9
+ export declare const CSS_VARS_DEPS: DependencySet;
10
+ export declare const CSS_VARS_NUXT_DEPS: DependencySet;
11
+ export declare function getDependencies(projectType: 'nuxt' | 'vue', threejs: boolean, cssVars: boolean): DependencySet;
@@ -0,0 +1,68 @@
1
+ export const BASE_NUXT_DEPS = {
2
+ production: [
3
+ '@vueuse/core',
4
+ '@nuxtjs/seo',
5
+ '@nuxt/image',
6
+ '@nuxtjs/device',
7
+ 'shadcn-nuxt',
8
+ ],
9
+ dev: [],
10
+ };
11
+ export const BASE_VUE_DEPS = {
12
+ production: [],
13
+ dev: [],
14
+ };
15
+ export const THREEJS_NUXT_DEPS = {
16
+ production: ['three', '@vueuse/core', 'postprocessing'],
17
+ dev: ['@types/three'],
18
+ };
19
+ export const THREEJS_VUE_DEPS = {
20
+ production: ['three', '@vueuse/core'],
21
+ dev: [],
22
+ };
23
+ export const CSS_VARS_DEPS = {
24
+ production: [],
25
+ dev: ['tailwindcss', '@tailwindcss/vite'],
26
+ };
27
+ export const CSS_VARS_NUXT_DEPS = {
28
+ production: [],
29
+ dev: ['typescript', 'tailwindcss', '@tailwindcss/vite'],
30
+ };
31
+ export function getDependencies(projectType, threejs, cssVars) {
32
+ const deps = {
33
+ production: ['gsap', 'lenis'],
34
+ dev: [],
35
+ };
36
+ if (projectType === 'nuxt') {
37
+ deps.production.push(...BASE_NUXT_DEPS.production);
38
+ deps.dev.push(...BASE_NUXT_DEPS.dev);
39
+ }
40
+ else {
41
+ deps.production.push(...BASE_VUE_DEPS.production);
42
+ deps.dev.push(...BASE_VUE_DEPS.dev);
43
+ }
44
+ if (threejs) {
45
+ if (projectType === 'nuxt') {
46
+ deps.production.push(...THREEJS_NUXT_DEPS.production);
47
+ deps.dev.push(...THREEJS_NUXT_DEPS.dev);
48
+ }
49
+ else {
50
+ deps.production.push(...THREEJS_VUE_DEPS.production);
51
+ deps.dev.push(...THREEJS_VUE_DEPS.dev);
52
+ }
53
+ }
54
+ if (cssVars) {
55
+ if (projectType === 'nuxt') {
56
+ deps.production.push(...CSS_VARS_NUXT_DEPS.production);
57
+ deps.dev.push(...CSS_VARS_NUXT_DEPS.dev);
58
+ }
59
+ else {
60
+ deps.production.push(...CSS_VARS_DEPS.production);
61
+ deps.dev.push(...CSS_VARS_DEPS.dev);
62
+ }
63
+ }
64
+ // Remove duplicates
65
+ deps.production = [...new Set(deps.production)];
66
+ deps.dev = [...new Set(deps.dev)];
67
+ return deps;
68
+ }
@@ -0,0 +1,4 @@
1
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn';
2
+ export declare function detectPackageManager(cwd?: string): Promise<PackageManager>;
3
+ export declare function getInstallCommand(pm: PackageManager, packages: string[]): string;
4
+ export declare function getDevInstallCommand(pm: PackageManager, packages: string[]): string;
@@ -0,0 +1,56 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { execSync } from 'child_process';
4
+ export async function detectPackageManager(cwd = process.cwd()) {
5
+ // Check for lock files
6
+ if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
7
+ return 'pnpm';
8
+ }
9
+ if (existsSync(join(cwd, 'yarn.lock'))) {
10
+ return 'yarn';
11
+ }
12
+ if (existsSync(join(cwd, 'package-lock.json'))) {
13
+ return 'npm';
14
+ }
15
+ // Check if package managers are available
16
+ try {
17
+ execSync('pnpm --version', { stdio: 'ignore' });
18
+ return 'pnpm';
19
+ }
20
+ catch {
21
+ // pnpm not available
22
+ }
23
+ try {
24
+ execSync('yarn --version', { stdio: 'ignore' });
25
+ return 'yarn';
26
+ }
27
+ catch {
28
+ // yarn not available
29
+ }
30
+ // Default to npm
31
+ return 'npm';
32
+ }
33
+ export function getInstallCommand(pm, packages) {
34
+ const packageList = packages.join(' ');
35
+ switch (pm) {
36
+ case 'pnpm':
37
+ return `pnpm add ${packageList}`;
38
+ case 'yarn':
39
+ return `yarn add ${packageList}`;
40
+ case 'npm':
41
+ default:
42
+ return `npm install ${packageList}`;
43
+ }
44
+ }
45
+ export function getDevInstallCommand(pm, packages) {
46
+ const packageList = packages.join(' ');
47
+ switch (pm) {
48
+ case 'pnpm':
49
+ return `pnpm add -D ${packageList}`;
50
+ case 'yarn':
51
+ return `yarn add -D ${packageList}`;
52
+ case 'npm':
53
+ default:
54
+ return `npm install --save-dev ${packageList}`;
55
+ }
56
+ }
@@ -0,0 +1,2 @@
1
+ export type ProjectType = 'nuxt' | 'vue' | 'none';
2
+ export declare function detectProjectType(cwd?: string): Promise<ProjectType>;
@@ -0,0 +1,60 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ export async function detectProjectType(cwd = process.cwd()) {
4
+ // Check for Nuxt project
5
+ const nuxtConfigFiles = [
6
+ 'nuxt.config.ts',
7
+ 'nuxt.config.js',
8
+ 'nuxt.config.mjs',
9
+ ];
10
+ const hasNuxtConfig = nuxtConfigFiles.some(file => existsSync(join(cwd, file)));
11
+ if (hasNuxtConfig) {
12
+ return 'nuxt';
13
+ }
14
+ // Check package.json for Nuxt
15
+ const packageJsonPath = join(cwd, 'package.json');
16
+ if (existsSync(packageJsonPath)) {
17
+ try {
18
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
19
+ const deps = {
20
+ ...packageJson.dependencies,
21
+ ...packageJson.devDependencies,
22
+ };
23
+ if (deps.nuxt || deps['@nuxt/kit']) {
24
+ return 'nuxt';
25
+ }
26
+ }
27
+ catch {
28
+ // Ignore parse errors
29
+ }
30
+ }
31
+ // Check for Vue project
32
+ const viteConfigFiles = [
33
+ 'vite.config.ts',
34
+ 'vite.config.js',
35
+ 'vite.config.mjs',
36
+ ];
37
+ const vueConfigFiles = ['vue.config.js', 'vue.config.ts'];
38
+ const hasViteConfig = viteConfigFiles.some(file => existsSync(join(cwd, file)));
39
+ const hasVueConfig = vueConfigFiles.some(file => existsSync(join(cwd, file)));
40
+ if (hasViteConfig || hasVueConfig) {
41
+ return 'vue';
42
+ }
43
+ // Check package.json for Vue
44
+ if (existsSync(packageJsonPath)) {
45
+ try {
46
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
47
+ const deps = {
48
+ ...packageJson.dependencies,
49
+ ...packageJson.devDependencies,
50
+ };
51
+ if (deps.vue && !deps.nuxt) {
52
+ return 'vue';
53
+ }
54
+ }
55
+ catch {
56
+ // Ignore parse errors
57
+ }
58
+ }
59
+ return 'none';
60
+ }
@@ -0,0 +1,2 @@
1
+ import type { PackageManager } from './package-manager.js';
2
+ export declare function setupShadcnNuxt(projectPath: string, packageManager: PackageManager): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { execSync } from 'child_process';
2
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ export async function setupShadcnNuxt(projectPath, packageManager) {
5
+ const isPnpm = packageManager === 'pnpm';
6
+ const execCommand = isPnpm ? 'pnpm dlx' : 'npx';
7
+ // Step 1: Create initial tailwind.css with basic import
8
+ // Always use app/assets/css/tailwind.css for Nuxt projects
9
+ const tailwindCssPath = join(projectPath, 'app', 'assets', 'css', 'tailwind.css');
10
+ mkdirSync(dirname(tailwindCssPath), { recursive: true });
11
+ writeFileSync(tailwindCssPath, '@import "tailwindcss";\n', 'utf-8');
12
+ // Step 2: Run nuxi module add shadcn-nuxt
13
+ execSync(`${execCommand} nuxi@latest module add shadcn-nuxt`, {
14
+ cwd: projectPath,
15
+ stdio: 'inherit',
16
+ });
17
+ // Step 3: Create SSR width plugin
18
+ const pluginPath1 = join(projectPath, 'app', 'plugins', 'ssr-width.ts');
19
+ const pluginPath2 = join(projectPath, 'plugins', 'ssr-width.ts');
20
+ const pluginPath = existsSync(join(projectPath, 'app'))
21
+ ? pluginPath1
22
+ : pluginPath2;
23
+ mkdirSync(dirname(pluginPath), { recursive: true });
24
+ const pluginContent = `import { provideSSRWidth } from '@vueuse/core'
25
+
26
+ export default defineNuxtPlugin((nuxtApp) => {
27
+ provideSSRWidth(1024, nuxtApp.vueApp)
28
+ })
29
+ `;
30
+ writeFileSync(pluginPath, pluginContent, 'utf-8');
31
+ // Step 4: Run nuxi prepare
32
+ execSync(`${execCommand} nuxi@latest prepare`, {
33
+ cwd: projectPath,
34
+ stdio: 'inherit',
35
+ });
36
+ // Step 5: Run shadcn-vue init
37
+ execSync(`${execCommand} shadcn-vue@latest init`, {
38
+ cwd: projectPath,
39
+ stdio: 'inherit',
40
+ });
41
+ // Step 6: Run shadcn-vue add button
42
+ execSync(`${execCommand} shadcn-vue@latest add button`, {
43
+ cwd: projectPath,
44
+ stdio: 'inherit',
45
+ });
46
+ }
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "pni",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for creating Nuxt/Vue projects with Three.js and CSS variables setup",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "pni": "dist/cli.js"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/cli.js",
11
+ "engines": {
12
+ "node": ">=16"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/alanfrancis442/pni.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/alanfrancis442/pni/issues"
20
+ },
21
+ "homepage": "https://github.com/alanfrancis442/pni#readme",
22
+ "scripts": {
23
+ "build": "tsc && npm run copy-templates",
24
+ "copy-templates": "node scripts/copy-templates.js",
25
+ "dev": "tsc --watch",
26
+ "dev:cli": "tsc && DEV=true node dist/cli.js",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "test": "prettier --check . && xo && ava",
30
+ "prepublishOnly": "npm run build"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "keywords": [
36
+ "nuxt",
37
+ "vue",
38
+ "threejs",
39
+ "three.js",
40
+ "cli",
41
+ "scaffold",
42
+ "generator",
43
+ "tailwind",
44
+ "css-variables",
45
+ "shadcn"
46
+ ],
47
+ "dependencies": {
48
+ "execa": "^8.0.1",
49
+ "ink": "^4.1.0",
50
+ "meow": "^11.0.0",
51
+ "react": "^18.2.0",
52
+ "react-devtools-core": "^4.28.5"
53
+ },
54
+ "devDependencies": {
55
+ "@sindresorhus/tsconfig": "^3.0.1",
56
+ "@types/react": "^18.0.32",
57
+ "@vdemedes/prettier-config": "^2.0.1",
58
+ "ava": "^5.2.0",
59
+ "chalk": "^5.2.0",
60
+ "ink-testing-library": "^3.0.0",
61
+ "prettier": "^2.8.7",
62
+ "ts-node": "^10.9.1",
63
+ "typescript": "^5.0.3",
64
+ "xo": "^1.2.3"
65
+ },
66
+ "ava": {
67
+ "extensions": {
68
+ "ts": "module",
69
+ "tsx": "module"
70
+ },
71
+ "nodeArguments": [
72
+ "--loader=ts-node/esm"
73
+ ]
74
+ },
75
+ "overrides": {
76
+ "braces": "3.0.3",
77
+ "micromatch": "4.0.8",
78
+ "semver": "7.5.2"
79
+ },
80
+ "prettier": "@vdemedes/prettier-config"
81
+ }