@nqlib/nqui 0.4.0 → 0.4.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/INSTALLATION.md +215 -0
- package/README.md +2 -1
- package/dist/command-palette-BuYcxPCc.cjs +5 -0
- package/dist/command-palette-dEJ9aEk4.js +694 -0
- package/dist/command.cjs.js +1 -1
- package/dist/command.es.js +1 -1
- package/dist/components/custom/enhanced-badge.d.ts +1 -1
- package/dist/components/custom/enhanced-button.d.ts +6 -1
- package/dist/components/custom/enhanced-button.d.ts.map +1 -1
- package/dist/components/custom/enhanced-checkbox.d.ts +11 -0
- package/dist/components/custom/enhanced-checkbox.d.ts.map +1 -1
- package/dist/components/custom/enhanced-radio-group.d.ts +13 -4
- package/dist/components/custom/enhanced-radio-group.d.ts.map +1 -1
- package/dist/components/custom/enhanced-sonner.d.ts +5 -6
- package/dist/components/custom/enhanced-sonner.d.ts.map +1 -1
- package/dist/components/custom/enhanced-tabs.d.ts.map +1 -1
- package/dist/components/error-boundary.d.ts +20 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/index.d.ts +102 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/checkbox.d.ts +4 -1
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/input-group.d.ts.map +1 -1
- package/dist/components/ui/radio-group.d.ts +3 -1
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/debug-panel-AjzBdMMz.js +9198 -0
- package/dist/debug-panel-NaOmD68t.cjs +171 -0
- package/dist/debug.cjs.js +1 -0
- package/dist/debug.es.js +7 -0
- package/dist/drawer-Cqq0Ozb2.cjs +1 -0
- package/dist/{drawer-CU4lkcz7.js → drawer-pUXPg3lF.js} +2 -2
- package/dist/drawer.cjs.js +1 -1
- package/dist/drawer.es.js +1 -1
- package/dist/entries/debug.d.ts +14 -0
- package/dist/entries/debug.d.ts.map +1 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-scroll-spy.d.ts.map +1 -1
- package/dist/index-CI756mSv.cjs +41 -0
- package/dist/index-CgfzsUO6.js +1069 -0
- package/dist/index.d.ts +2 -98
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/index.d.ts +9 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/nqui.cjs.js +42 -212
- package/dist/nqui.es.js +8589 -17780
- package/dist/sonner-BtzU00r3.js +248 -0
- package/dist/sonner-Dfk26eds.cjs +54 -0
- package/dist/sonner.cjs.js +1 -1
- package/dist/sonner.es.js +1 -1
- package/dist/styles.css +3 -0
- package/docs/components/README.md +99 -1
- package/docs/components/nqui-card.md +7 -0
- package/docs/components/nqui-checkbox.md +23 -1
- package/docs/components/nqui-radio-group.md +45 -2
- package/docs/components/nqui-tabs.md +11 -1
- package/docs/nqui-skills/SKILL.md +95 -0
- package/docs/nqui-skills/design-system.md +130 -0
- package/docs/nqui-skills/rules/composition.md +183 -0
- package/docs/nqui-skills/rules/forms.md +190 -0
- package/docs/nqui-skills/rules/icons.md +158 -0
- package/docs/nqui-skills/rules/styling.md +192 -0
- package/package.json +23 -10
- package/scripts/cli.js +1 -0
- package/scripts/download-skills.js +91 -0
- package/scripts/examples/nextjs-layout-sidebar.tsx +100 -0
- package/scripts/examples/nextjs-page-sidebar.tsx +81 -0
- package/scripts/examples/vite-app.tsx +135 -0
- package/scripts/examples/vite-main.tsx +17 -0
- package/scripts/examples.js +92 -6
- package/scripts/generate-docs.js +169 -0
- package/scripts/init-css.js +34 -14
- package/scripts/init-cursor.js +8 -0
- package/scripts/post-install.js +41 -9
- package/scripts/resolve-target-dir.js +20 -1
- package/scripts/wizard.js +12 -7
- package/dist/command-palette-UHk8zZOg.cjs +0 -45
- package/dist/command-palette-d-TrdBsM.js +0 -1778
- package/dist/drawer-BcIxWRN8.cjs +0 -1
- package/dist/sonner-Co6YpYVs.js +0 -546
- package/dist/sonner-DbQhVp8m.cjs +0 -330
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { StrictMode } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { ThemeProvider } from "next-themes";
|
|
4
|
+
import { BrowserRouter } from "react-router-dom";
|
|
5
|
+
import "./index.css";
|
|
6
|
+
import App from "./App";
|
|
7
|
+
// Required dependencies: npm install @nqlib/nqui tw-animate-css next-themes hugeicons-core-free-icons hugeicons-react react-router-dom
|
|
8
|
+
|
|
9
|
+
createRoot(document.getElementById("root")!).render(
|
|
10
|
+
<StrictMode>
|
|
11
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
|
|
12
|
+
<BrowserRouter>
|
|
13
|
+
<App />
|
|
14
|
+
</BrowserRouter>
|
|
15
|
+
</ThemeProvider>
|
|
16
|
+
</StrictMode>
|
|
17
|
+
);
|
package/scripts/examples.js
CHANGED
|
@@ -7,7 +7,7 @@ import { askQuestion } from './wizard.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* Copy Next.js example files to user's project
|
|
9
9
|
*/
|
|
10
|
-
export async function copyNextJsExamples(framework, { force }) {
|
|
10
|
+
export async function copyNextJsExamples(framework, { force, sidebar }) {
|
|
11
11
|
if (framework !== 'nextjs') {
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
@@ -21,9 +21,95 @@ export async function copyNextJsExamples(framework, { force }) {
|
|
|
21
21
|
? join(cwd, 'src', 'app')
|
|
22
22
|
: join(cwd, 'app');
|
|
23
23
|
|
|
24
|
+
// Choose examples based on sidebar flag
|
|
25
|
+
const examples = sidebar
|
|
26
|
+
? [
|
|
27
|
+
{ src: 'nextjs-page-sidebar.tsx', dest: join(appDir, 'page.tsx'), name: 'page.tsx' },
|
|
28
|
+
{ src: 'nextjs-layout-sidebar.tsx', dest: join(appDir, 'layout.tsx'), name: 'layout.tsx' },
|
|
29
|
+
]
|
|
30
|
+
: [
|
|
31
|
+
{ src: 'nextjs-page.tsx', dest: join(appDir, 'page.tsx'), name: 'page.tsx' },
|
|
32
|
+
{ src: 'nextjs-layout.tsx', dest: join(appDir, 'layout.tsx'), name: 'layout.tsx' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const copied = [];
|
|
36
|
+
const existing = [];
|
|
37
|
+
|
|
38
|
+
// Check which files exist
|
|
39
|
+
for (const { dest, name } of examples) {
|
|
40
|
+
if (existsSync(dest)) {
|
|
41
|
+
existing.push({ dest, name });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Ask about overwriting if files exist and force is not set
|
|
46
|
+
let shouldOverwrite = force;
|
|
47
|
+
if (existing.length > 0 && !force) {
|
|
48
|
+
const fileList = existing.map(e => e.name).join(' and ');
|
|
49
|
+
const answer = await askQuestion(
|
|
50
|
+
`\n⚠️ ${fileList} already exist(s). Overwrite? (y/n): `
|
|
51
|
+
);
|
|
52
|
+
shouldOverwrite = answer === 'y';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const { src, dest, name } of examples) {
|
|
56
|
+
const srcPath = join(examplesDir, src);
|
|
57
|
+
|
|
58
|
+
if (!existsSync(srcPath)) {
|
|
59
|
+
console.warn(`⚠️ Example file not found: ${srcPath}`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (existsSync(dest) && !shouldOverwrite) {
|
|
64
|
+
console.log(`⏭️ Skipped: ${dest} (already exists)`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const content = readFileSync(srcPath, 'utf8');
|
|
69
|
+
writeFileSync(dest, content, 'utf8');
|
|
70
|
+
copied.push(dest);
|
|
71
|
+
console.log(`✅ ${existsSync(dest) && shouldOverwrite ? 'Overwritten' : 'Created'}: ${dest}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (copied.length > 0) {
|
|
75
|
+
const deps = sidebar
|
|
76
|
+
? 'npm install @nqlib/nqui @hugeicons/react @hugeicons/core-free-icons tw-animate-css next-themes react-router-dom'
|
|
77
|
+
: 'npm install @nqlib/nqui tw-animate-css next-themes';
|
|
78
|
+
console.log(`\n📝 Required dependencies for example files:`);
|
|
79
|
+
console.log(` ${deps}\n`);
|
|
80
|
+
if (sidebar) {
|
|
81
|
+
console.log(` Note: 3-column layout uses Sidebar, TableOfContents; main.tsx includes ThemeProvider + BrowserRouter for Vite.\n`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return copied;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Copy Vite example files to user's project
|
|
90
|
+
*/
|
|
91
|
+
export async function copyViteExamples(framework, { force, sidebar }) {
|
|
92
|
+
if (framework !== 'vite') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const root = getPackageRoot();
|
|
97
|
+
const examplesDir = join(root, 'scripts', 'examples');
|
|
98
|
+
const cwd = process.cwd();
|
|
99
|
+
|
|
100
|
+
// Determine src directory
|
|
101
|
+
const srcDir = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : join(cwd, 'src');
|
|
102
|
+
|
|
103
|
+
// Ensure src directory exists
|
|
104
|
+
if (!existsSync(srcDir)) {
|
|
105
|
+
console.warn('⚠️ src directory not found');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Choose examples based on sidebar flag (3-column with TOC when sidebar; same files + main for ThemeProvider)
|
|
24
110
|
const examples = [
|
|
25
|
-
{ src: '
|
|
26
|
-
{ src: '
|
|
111
|
+
{ src: 'vite-app.tsx', dest: join(srcDir, 'App.tsx'), name: 'App.tsx' },
|
|
112
|
+
{ src: 'vite-main.tsx', dest: join(srcDir, 'main.tsx'), name: 'main.tsx' },
|
|
27
113
|
];
|
|
28
114
|
|
|
29
115
|
const copied = [];
|
|
@@ -66,11 +152,11 @@ export async function copyNextJsExamples(framework, { force }) {
|
|
|
66
152
|
}
|
|
67
153
|
|
|
68
154
|
if (copied.length > 0) {
|
|
155
|
+
const deps = 'npm install @nqlib/nqui tw-animate-css next-themes @hugeicons/react @hugeicons/core-free-icons react-router-dom';
|
|
69
156
|
console.log(`\n📝 Required dependencies for example files:`);
|
|
70
|
-
console.log(`
|
|
71
|
-
console.log(`
|
|
157
|
+
console.log(` ${deps}`);
|
|
158
|
+
console.log(` Note: For Vite, wrap your app with ThemeProvider and BrowserRouter in main.tsx\n`);
|
|
72
159
|
}
|
|
73
160
|
|
|
74
161
|
return copied;
|
|
75
162
|
}
|
|
76
|
-
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates downloadable documentation files for nqui.
|
|
3
|
+
*
|
|
4
|
+
* Outputs:
|
|
5
|
+
* - INSTALL.md: Installation instructions
|
|
6
|
+
* - COMPONENTS.md: All component documentation bundled
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node scripts/generate-docs.js
|
|
10
|
+
* node scripts/generate-docs.js --install
|
|
11
|
+
* node scripts/generate-docs.js --components
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'fs';
|
|
15
|
+
import { join, dirname } from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const DOCS_DIR = join(__dirname, '../docs');
|
|
20
|
+
const OUTPUT_DIR = join(__dirname, '../dist/docs');
|
|
21
|
+
|
|
22
|
+
// Ensure output directory exists
|
|
23
|
+
import { mkdirSync, rmSync } from 'fs';
|
|
24
|
+
|
|
25
|
+
function ensureDir(dir) {
|
|
26
|
+
if (!existsSync(dir)) {
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readDoc(filePath) {
|
|
32
|
+
try {
|
|
33
|
+
return readFileSync(filePath, 'utf-8');
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function generateInstallDoc() {
|
|
40
|
+
const installPath = join(DOCS_DIR, 'internal-notes/INSTALLATION.md');
|
|
41
|
+
let content = readDoc(installPath);
|
|
42
|
+
|
|
43
|
+
if (!content) {
|
|
44
|
+
console.error('Could not find INSTALLATION.md');
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add header
|
|
49
|
+
const header = `# @nqlib/nqui Installation Guide
|
|
50
|
+
|
|
51
|
+
> Generated for LLM consumption. Use this file to understand how to install and set up nqui.
|
|
52
|
+
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
return header + content;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function generateComponentsDoc() {
|
|
59
|
+
// Read the main README for shared conventions
|
|
60
|
+
const readmePath = join(DOCS_DIR, 'components/README.md');
|
|
61
|
+
let readmeContent = readDoc(readmePath);
|
|
62
|
+
|
|
63
|
+
if (!readmeContent) {
|
|
64
|
+
console.error('Could not find README.md');
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Extract sections from README that are useful for implementation
|
|
69
|
+
// (Prerequisites, Shared Conventions, When to Use tables)
|
|
70
|
+
const sectionsToKeep = [
|
|
71
|
+
'# nqui Component Instructions',
|
|
72
|
+
'## Prerequisites',
|
|
73
|
+
'## Shared Conventions',
|
|
74
|
+
'## When to Use',
|
|
75
|
+
'## AI Implementation Checklist'
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// Build the content
|
|
79
|
+
let content = `# @nqlib/nqui Component Reference
|
|
80
|
+
|
|
81
|
+
> Generated for LLM consumption. Use this file when implementing nqui components.
|
|
82
|
+
|
|
83
|
+
**Import:** \`import { X } from "@nqlib/nqui"\`
|
|
84
|
+
**CSS:** \`@import "@nqlib/nqui/styles"\` (via \`npx @nqlib/nqui init-css\`)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
// Add the main sections from README
|
|
91
|
+
let currentSection = '';
|
|
92
|
+
const lines = readmeContent.split('\n');
|
|
93
|
+
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
// Track current section
|
|
96
|
+
if (line.startsWith('##')) {
|
|
97
|
+
currentSection = line;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Keep specific sections
|
|
101
|
+
if (sectionsToKeep.some(s => line.includes(s) || currentSection.includes(s.replace('## ', '').replace('# ', '')))) {
|
|
102
|
+
content += line + '\n';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Add component docs
|
|
107
|
+
content += '\n---\n\n## Component Documentation\n\n';
|
|
108
|
+
|
|
109
|
+
const componentsDir = join(DOCS_DIR, 'components');
|
|
110
|
+
const files = readdirSync(componentsDir)
|
|
111
|
+
.filter(f => f.startsWith('nqui-') && f.endsWith('.md'))
|
|
112
|
+
.sort();
|
|
113
|
+
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
const componentPath = join(componentsDir, file);
|
|
116
|
+
const componentContent = readDoc(componentPath);
|
|
117
|
+
|
|
118
|
+
if (componentContent) {
|
|
119
|
+
// Extract the relevant part (without frontmatter if present)
|
|
120
|
+
let docContent = componentContent;
|
|
121
|
+
|
|
122
|
+
// Remove frontmatter
|
|
123
|
+
if (docContent.startsWith('---')) {
|
|
124
|
+
const endOfFm = docContent.indexOf('---', 3);
|
|
125
|
+
if (endOfFm !== -1) {
|
|
126
|
+
docContent = docContent.substring(endOfFm + 3);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Clean up and add
|
|
131
|
+
docContent = docContent.trim();
|
|
132
|
+
content += docContent + '\n\n---\n\n';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return content;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function main() {
|
|
140
|
+
const args = process.argv.slice(2);
|
|
141
|
+
const generateInstall = args.includes('--install') || args.length === 0;
|
|
142
|
+
const generateComponents = args.includes('--components') || args.length === 0;
|
|
143
|
+
|
|
144
|
+
ensureDir(OUTPUT_DIR);
|
|
145
|
+
|
|
146
|
+
console.log('Generating nqui documentation...\n');
|
|
147
|
+
|
|
148
|
+
if (generateInstall) {
|
|
149
|
+
const installDoc = generateInstallDoc();
|
|
150
|
+
if (installDoc) {
|
|
151
|
+
const outputPath = join(OUTPUT_DIR, 'INSTALL.md');
|
|
152
|
+
writeFileSync(outputPath, installDoc);
|
|
153
|
+
console.log(`Generated: ${outputPath}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (generateComponents) {
|
|
158
|
+
const componentsDoc = generateComponentsDoc();
|
|
159
|
+
if (componentsDoc) {
|
|
160
|
+
const outputPath = join(OUTPUT_DIR, 'COMPONENTS.md');
|
|
161
|
+
writeFileSync(outputPath, componentsDoc);
|
|
162
|
+
console.log(`Generated: ${outputPath}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log('\nDone!');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
main();
|
package/scripts/init-css.js
CHANGED
|
@@ -16,12 +16,12 @@ import { runPipeline } from './pipeline/index.js';
|
|
|
16
16
|
import { wizard, askAboutExamples } from './wizard.js';
|
|
17
17
|
import { detectFramework, findMainCssFile } from './framework.js';
|
|
18
18
|
import { generateSetupContent } from './setup-helper.js';
|
|
19
|
-
import { copyNextJsExamples } from './examples.js';
|
|
19
|
+
import { copyNextJsExamples, copyViteExamples } from './examples.js';
|
|
20
20
|
import { emit } from './pipeline/emit.js';
|
|
21
21
|
|
|
22
22
|
// Guard: if first arg is a subcommand, user has old package (main bin was init-css). Redirect.
|
|
23
23
|
const firstArg = process.argv[2];
|
|
24
|
-
const subcommands = ['init-cursor', 'install-peers', 'init-debug', 'init-debug-css', 'setup'];
|
|
24
|
+
const subcommands = ['init-cursor', 'init-skills', 'install-peers', 'init-debug', 'init-debug-css', 'setup'];
|
|
25
25
|
if (subcommands.includes(firstArg)) {
|
|
26
26
|
console.error(`
|
|
27
27
|
❌ Outdated @nqlib/nqui — "npx @nqlib/nqui ${firstArg}" routed to init-css.
|
|
@@ -29,17 +29,17 @@ if (subcommands.includes(firstArg)) {
|
|
|
29
29
|
Fix: npm install @nqlib/nqui@latest
|
|
30
30
|
Then: npx @nqlib/nqui ${firstArg}
|
|
31
31
|
|
|
32
|
-
Or run the binary directly: npm exec nqui-init-
|
|
32
|
+
Or run the binary directly: npm exec nqui-init-skills (for init-skills), etc.
|
|
33
33
|
`);
|
|
34
34
|
process.exit(1);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// Filter out command names from argv
|
|
38
|
-
const commandNames = ['init-css', 'nqui', 'nqui-init-css'];
|
|
37
|
+
// Filter out command names from argv (so they are never used as output path)
|
|
38
|
+
const commandNames = ['init-css', 'init-skills', 'nqui', 'nqui-init-css'];
|
|
39
39
|
const filteredArgs = process.argv.slice(2).filter(arg => !commandNames.includes(arg));
|
|
40
40
|
|
|
41
41
|
const args = minimist(filteredArgs, {
|
|
42
|
-
boolean: ['js', 'tokens-only', 'force', 'dry-run', 'wizard', 'setup', 'help', 'version', 'local-copy'],
|
|
42
|
+
boolean: ['js', 'tokens-only', 'force', 'dry-run', 'wizard', 'setup', 'help', 'version', 'local-copy', 'sidebar'],
|
|
43
43
|
alias: { h: 'help', v: 'version' },
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -55,7 +55,11 @@ if (args.version) {
|
|
|
55
55
|
process.exit(0);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
// Prevent subcommand names or bare filenames from becoming output path (would e.g. create init-skills or root nqui-setup.css)
|
|
59
|
+
const rawOutput = args._[0] || 'nqui/index.css';
|
|
60
|
+
const output = !rawOutput || rawOutput === 'init-skills' || rawOutput === 'init-cursor' || !dirname(rawOutput)
|
|
61
|
+
? 'nqui/index.css'
|
|
62
|
+
: rawOutput;
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
65
|
* Generate index.css that imports from library package
|
|
@@ -101,29 +105,45 @@ function generateIndexCssContent() {
|
|
|
101
105
|
});
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
// Generate setup helper file (always, unless dry-run)
|
|
108
|
+
// Generate setup helper file (always, unless dry-run). Always under nqui/ to avoid writing to project root.
|
|
105
109
|
if (!args['dry-run'] && (args.setup || !args['tokens-only'])) {
|
|
106
110
|
const setupContent = generateSetupContent(framework, output, useLibraryImport);
|
|
107
|
-
const
|
|
111
|
+
const setupDir = dirname(output) || 'nqui';
|
|
112
|
+
const setupPath = resolve(process.cwd(), setupDir, 'nqui-setup.css');
|
|
108
113
|
emit(setupPath, setupContent, { force: args.force, dryRun: false });
|
|
109
114
|
|
|
110
115
|
const mainCssFile = findMainCssFile(framework);
|
|
111
116
|
|
|
112
117
|
console.log(`\n📝 Next steps:`);
|
|
113
|
-
console.log(` 1. Copy the contents of ${
|
|
118
|
+
console.log(` 1. Copy the contents of ${setupDir}/nqui-setup.css`);
|
|
114
119
|
console.log(` 2. Paste them at the VERY TOP of your main CSS file`);
|
|
115
120
|
console.log(` (e.g. ${mainCssFile})\n`);
|
|
116
121
|
}
|
|
117
122
|
|
|
118
|
-
//
|
|
123
|
+
// Copy examples: when --sidebar and --force, auto-enable so nqui:init is non-interactive
|
|
119
124
|
let shouldCopyExamples = wiz.copyExamples;
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
let useSidebar = wiz.sidebarLayout || args.sidebar;
|
|
126
|
+
if (!args.wizard && (framework === 'nextjs' || framework === 'vite')) {
|
|
127
|
+
if (args.sidebar && args.force) {
|
|
128
|
+
shouldCopyExamples = true;
|
|
129
|
+
useSidebar = true;
|
|
130
|
+
} else if (!args['dry-run'] && !shouldCopyExamples) {
|
|
131
|
+
shouldCopyExamples = await askAboutExamples(framework, useSidebar);
|
|
132
|
+
if (shouldCopyExamples) {
|
|
133
|
+
const sidebarAnswer = await askQuestion('\n🎨 Use sidebar layout (recommended for apps)? (y/n): ');
|
|
134
|
+
useSidebar = sidebarAnswer === 'y' || sidebarAnswer === 'yes';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
// Copy example files if requested
|
|
125
140
|
if (!args['dry-run'] && shouldCopyExamples) {
|
|
126
|
-
|
|
141
|
+
let copied;
|
|
142
|
+
if (framework === 'nextjs') {
|
|
143
|
+
copied = await copyNextJsExamples(framework, { force: args.force, sidebar: useSidebar });
|
|
144
|
+
} else if (framework === 'vite') {
|
|
145
|
+
copied = await copyViteExamples(framework, { force: args.force, sidebar: useSidebar });
|
|
146
|
+
}
|
|
127
147
|
if (copied && copied.length === 0) {
|
|
128
148
|
// Files were skipped (user said no to overwrite)
|
|
129
149
|
console.log('\n⏭️ Example files skipped. Run with --force to overwrite existing files.\n');
|
package/scripts/init-cursor.js
CHANGED
|
@@ -13,6 +13,7 @@ import { fileURLToPath } from 'url';
|
|
|
13
13
|
import { FULL_PEER_LIST } from './peer-deps.js';
|
|
14
14
|
import { buildInstallSkill, buildComponentsSkill } from './skill-templates.js';
|
|
15
15
|
import { resolveTargetDir } from './resolve-target-dir.js';
|
|
16
|
+
import { downloadSkills } from './download-skills.js';
|
|
16
17
|
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const cwd = process.cwd();
|
|
@@ -153,12 +154,19 @@ function main() {
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
writeCursorRule(targetDir);
|
|
157
|
+
|
|
158
|
+
// Also download nqui-skills to user's .cursor folder
|
|
159
|
+
console.log('\n📚 Downloading nqui-skills...');
|
|
160
|
+
downloadSkills({ force: false });
|
|
161
|
+
|
|
156
162
|
console.log(`
|
|
157
163
|
✅ Cursor rules + skills installed
|
|
158
164
|
|
|
159
165
|
${displayPath}/.cursor/rules/nqui-components.mdc
|
|
160
166
|
${displayPath}/.cursor/skills/nqui-install/
|
|
161
167
|
${displayPath}/.cursor/skills/nqui-components/
|
|
168
|
+
${displayPath}/.cursor/nqui-skills/
|
|
169
|
+
${displayPath}/AGENTS.md
|
|
162
170
|
|
|
163
171
|
Open this folder in Cursor for skills to work. Docs: node_modules/@nqlib/nqui/docs/components/
|
|
164
172
|
`);
|
package/scripts/post-install.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Auto-injects Cursor rules so consumers don't need to remember init-cursor.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { existsSync } from 'fs';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
11
11
|
import { resolve } from 'path';
|
|
12
12
|
|
|
13
13
|
// Skip in CI to reduce noise
|
|
@@ -36,6 +36,31 @@ function getInstallCmd(pkgs) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function addNquiInitScript() {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
const packageJsonPath = resolve(cwd, 'package.json');
|
|
42
|
+
|
|
43
|
+
if (!existsSync(packageJsonPath)) return false;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
47
|
+
|
|
48
|
+
// Check if script already exists
|
|
49
|
+
if (pkg.scripts?.['nqui:init']) {
|
|
50
|
+
return false; // Already exists
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add the script
|
|
54
|
+
pkg.scripts = pkg.scripts || {};
|
|
55
|
+
pkg.scripts['nqui:init'] = 'npx @nqlib/nqui install-peers && npx @nqlib/nqui init-cursor && npx @nqlib/nqui init-skills && npx @nqlib/nqui init-css --sidebar --force';
|
|
56
|
+
|
|
57
|
+
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
58
|
+
return true;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
39
64
|
import { FULL_PEER_LIST } from './peer-deps.js';
|
|
40
65
|
import { writeCursorRule } from './init-cursor.js';
|
|
41
66
|
import { resolveTargetDir } from './resolve-target-dir.js';
|
|
@@ -47,21 +72,28 @@ const installRequired = getInstallCmd(requiredPeers);
|
|
|
47
72
|
const installFull = getInstallCmd(['@nqlib/nqui', ...FULL_PEER_LIST]);
|
|
48
73
|
const installRecommended = getInstallCmd(recommended);
|
|
49
74
|
|
|
75
|
+
// Auto-add nqui:init script to package.json
|
|
76
|
+
const scriptAdded = addNquiInitScript();
|
|
77
|
+
|
|
50
78
|
const msg = `
|
|
51
79
|
╔══════════════════════════════════════════════════════════════════╗
|
|
52
|
-
║ nqui – Next steps
|
|
80
|
+
║ nqui – Next steps ║
|
|
53
81
|
╚══════════════════════════════════════════════════════════════════╝
|
|
54
82
|
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
${scriptAdded ? '✅ Added "nqui:init" script to package.json\n' : ''}Run the full setup:
|
|
84
|
+
|
|
85
|
+
npm run nqui:init
|
|
57
86
|
|
|
58
|
-
|
|
59
|
-
Full: npx @nqlib/nqui install-peers
|
|
87
|
+
Or step by step:
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
npx @nqlib/nqui init-css --sidebar # CSS + sidebar layout
|
|
90
|
+
npx @nqlib/nqui install-peers # Install dependencies
|
|
91
|
+
npx @nqlib/nqui init-cursor # Setup Cursor skills
|
|
62
92
|
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
Manual commands:
|
|
94
|
+
- Install peers: ${installRequired}
|
|
95
|
+
- Full peers: npx @nqlib/nqui install-peers
|
|
96
|
+
- Refresh skills: npx @nqlib/nqui init-skills
|
|
65
97
|
|
|
66
98
|
→ Run "npx nqui-setup" anytime to see this again.
|
|
67
99
|
`;
|
|
@@ -59,11 +59,24 @@ function getWorkspacePackageDirs(workspaceRoot) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function isNquiSourceDir(dir) {
|
|
62
|
+
// Must NOT be inside node_modules (that means we're installed as a dep, not dev source)
|
|
63
|
+
if (resolve(dir).includes('node_modules')) return false;
|
|
62
64
|
return existsSync(resolve(dir, 'docs/components/README.md')) &&
|
|
63
65
|
existsSync(resolve(dir, 'package.json')) &&
|
|
64
66
|
readFileSync(resolve(dir, 'package.json'), 'utf8').includes('"name": "@nqlib/nqui"');
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
function findProjectRootFromNodeModules(startDir) {
|
|
70
|
+
let dir = resolve(startDir);
|
|
71
|
+
for (let i = 0; i < 20; i++) {
|
|
72
|
+
if (existsSync(resolve(dir, 'node_modules/@nqlib/nqui'))) return dir;
|
|
73
|
+
const parent = dirname(dir);
|
|
74
|
+
if (parent === dir) break;
|
|
75
|
+
dir = parent;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
67
80
|
/**
|
|
68
81
|
* Returns the directory where .cursor/ should be written.
|
|
69
82
|
* Prefers a dir with node_modules/@nqlib/nqui so docs path resolves.
|
|
@@ -72,7 +85,13 @@ function isNquiSourceDir(dir) {
|
|
|
72
85
|
* @returns {string} Absolute path to target directory
|
|
73
86
|
*/
|
|
74
87
|
export function resolveTargetDir(startDir) {
|
|
75
|
-
|
|
88
|
+
let cwd = resolve(startDir);
|
|
89
|
+
|
|
90
|
+
// 0. Running from node_modules (postinstall as dep) -> use host project root
|
|
91
|
+
if (cwd.includes('node_modules')) {
|
|
92
|
+
const projectRoot = findProjectRootFromNodeModules(cwd);
|
|
93
|
+
if (projectRoot) return projectRoot;
|
|
94
|
+
}
|
|
76
95
|
|
|
77
96
|
// 1. Current dir has nqui in node_modules -> use it
|
|
78
97
|
if (hasNquiInNodeModules(cwd)) return cwd;
|
package/scripts/wizard.js
CHANGED
|
@@ -35,23 +35,28 @@ export async function wizard() {
|
|
|
35
35
|
|
|
36
36
|
const framework = detectFramework();
|
|
37
37
|
let copyExamples = false;
|
|
38
|
+
let sidebarLayout = false;
|
|
38
39
|
|
|
39
|
-
if (framework === 'nextjs') {
|
|
40
|
-
copyExamples = (await ask(
|
|
40
|
+
if (framework === 'nextjs' || framework === 'vite') {
|
|
41
|
+
copyExamples = (await ask(`\nCopy ${framework} example files? (y/n): `)) === 'y';
|
|
42
|
+
if (copyExamples) {
|
|
43
|
+
sidebarLayout = (await ask('\nUse sidebar layout (recommended for apps)? (y/n): ')) === 'y';
|
|
44
|
+
}
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
return { tokensOnly, js, copyExamples };
|
|
47
|
+
return { tokensOnly, js, copyExamples, sidebarLayout };
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
|
-
* Ask about copying examples (used in default mode
|
|
51
|
+
* Ask about copying examples (used in default mode)
|
|
48
52
|
*/
|
|
49
|
-
export async function askAboutExamples(framework) {
|
|
50
|
-
if (framework !== 'nextjs') {
|
|
53
|
+
export async function askAboutExamples(framework, useSidebar = false) {
|
|
54
|
+
if (framework !== 'nextjs' && framework !== 'vite') {
|
|
51
55
|
return false;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
const
|
|
58
|
+
const layoutType = useSidebar ? 'sidebar' : 'basic';
|
|
59
|
+
const answer = await askQuestion(`\n📦 Copy ${framework} example files (${layoutType} layout)? (y/n): `);
|
|
55
60
|
return answer === 'y' || answer === 'yes';
|
|
56
61
|
}
|
|
57
62
|
|