openprompt-lang 0.3.0 → 0.4.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/README.md +483 -32
- package/bin/cli.js +232 -41
- package/bin/create.js +135 -0
- package/bin/lint.js +20 -21
- package/docs/COMMANDS.md +200 -127
- package/docs/COMMITS/INDEX.md +1 -0
- package/docs/PROMPT_AI_CONTEXT.md +99 -0
- package/docs/langs/dotnet.md +36 -0
- package/docs/langs/java-spring.md +45 -0
- package/docs/langs/python-fastapi.md +35 -0
- package/docs/langs/unity.md +30 -0
- package/docs/langs/vue-nuxt.md +36 -0
- package/package.json +31 -3
- package/scaffolds/.cursorrules +6 -0
- package/scaffolds/AGENTS.md +27 -0
- package/scaffolds/Dockerfile +11 -0
- package/scaffolds/capacitor.config.ts +17 -0
- package/scaffolds/netlify.toml +8 -0
- package/scaffolds/prompt-lang.json +15 -0
- package/scaffolds/railway.json +12 -0
- package/scaffolds/tailwind.config.js +8 -0
- package/scaffolds/tauri.conf.json +26 -0
- package/schemas/language-module.json +116 -0
- package/schemas/prompt-lang.json +38 -3
- package/schemas/structures.json +145 -0
- package/src/ai/prompt-builder.js +184 -0
- package/src/ai/providers.js +247 -0
- package/src/annotations/registry.js +39 -0
- package/src/annotations/tags.json +24 -0
- package/src/commands/ai-gen.js +161 -0
- package/src/commands/component.js +242 -212
- package/src/commands/context.js +184 -109
- package/src/commands/extract.js +242 -0
- package/src/commands/figma.js +15 -15
- package/src/commands/init.js +197 -93
- package/src/commands/integrate.js +406 -0
- package/src/commands/lang.js +148 -0
- package/src/commands/qa-gen.js +139 -0
- package/src/commands/scaffold.js +127 -0
- package/src/commands/suggest.js +24 -14
- package/src/commands/teach.js +110 -0
- package/src/commands/validate.js +143 -83
- package/src/commands/wizard.js +456 -0
- package/src/generators/figma-prompt.js +20 -12
- package/src/language-service/plugin.cjs +94 -0
- package/src/language-service/plugin.d.ts +6 -0
- package/src/mcp-server.js +605 -0
- package/src/templates/langs/react/INDEX.json +262 -0
- package/src/templates/langs/react/MODULE.json +166 -0
- package/src/templates/langs/react/templates/hooks/useAuth.template.ts +134 -0
- package/src/templates/langs/react/templates/hooks/useDebounce.template.ts +45 -0
- package/src/templates/langs/react/templates/hooks/useForm.template.ts +146 -0
- package/src/templates/langs/react/templates/hooks/usePagination.template.ts +108 -0
- package/src/templates/langs/react/templates/services/apiService.template.ts +123 -0
- package/src/templates/langs/react/templates/ui/Button.template.tsx +87 -0
- package/src/templates/langs/react/templates/ui/Card.template.tsx +85 -0
- package/src/templates/langs/react/templates/ui/DataTable.template.tsx +163 -0
- package/src/templates/langs/react/templates/ui/Input.template.tsx +96 -0
- package/src/templates/langs/react/templates/ui/Modal.template.tsx +133 -0
- package/src/templates/langs/react/templates/ui/Select.template.tsx +99 -0
- package/src/templates/langs/vue/INDEX.json +246 -0
- package/src/templates/langs/vue/MODULE.json +105 -0
- package/src/templates/langs/vue/templates/composables/useAuth.template.ts +106 -0
- package/src/templates/langs/vue/templates/composables/useDebounce.template.ts +47 -0
- package/src/templates/langs/vue/templates/composables/useFetch.template.ts +54 -0
- package/src/templates/langs/vue/templates/composables/useForm.template.ts +127 -0
- package/src/templates/langs/vue/templates/composables/usePagination.template.ts +98 -0
- package/src/templates/langs/vue/templates/services/apiService.template.ts +116 -0
- package/src/templates/langs/vue/templates/ui/Button.template.vue +79 -0
- package/src/templates/langs/vue/templates/ui/Card.template.vue +73 -0
- package/src/templates/langs/vue/templates/ui/DataTable.template.vue +115 -0
- package/src/templates/langs/vue/templates/ui/Input.template.vue +70 -0
- package/src/templates/langs/vue/templates/ui/Modal.template.vue +112 -0
- package/src/templates/langs/vue/templates/ui/Select.template.vue +77 -0
- package/src/templates/scripts/log-actividad.sh +32 -0
- package/src/templates/scripts/log-commit.sh +35 -0
- package/src/templates/scripts/log-error.sh +45 -0
- package/src/templates/scripts/validate.sh +23 -0
- package/src/ts-transformer/index.cjs +86 -0
- package/src/utils/ai.js +35 -53
- package/src/utils/annotations.js +260 -214
- package/src/utils/config.js +61 -13
- package/src/utils/error-learner.js +203 -0
- package/src/utils/file-utils.js +119 -0
- package/src/utils/language-loader.js +167 -0
- package/src/utils/template-utils.js +45 -0
- package/src/vite-plugin/index.js +54 -0
- package/vscode-extension/package.json +23 -2
- package/vscode-extension/snippets/promptlang.json +1 -3
- package/vscode-extension/syntaxes/annotations-code.tmGrammar.json +15 -0
|
@@ -1,143 +1,208 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
readFileSync,
|
|
3
|
+
writeFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
statSync,
|
|
8
|
+
copyFileSync,
|
|
9
|
+
} from "fs"
|
|
10
|
+
import { join, relative, basename, extname, dirname } from "path"
|
|
11
|
+
import { fileURLToPath } from "url"
|
|
12
|
+
import chalk from "chalk"
|
|
13
|
+
import {
|
|
14
|
+
getLanguages,
|
|
15
|
+
getLanguageIndex,
|
|
16
|
+
searchTemplates,
|
|
17
|
+
getTemplate,
|
|
18
|
+
} from "../utils/language-loader.js"
|
|
19
|
+
|
|
20
|
+
const MANIFEST_FILE = "componentes.json"
|
|
6
21
|
|
|
7
22
|
export async function componentList(options) {
|
|
8
|
-
const cwd = process.cwd()
|
|
9
|
-
const config = loadProjectConfig(cwd)
|
|
10
|
-
const libPath = options.library || config?.components?.library || null
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const cwd = process.cwd()
|
|
24
|
+
const config = loadProjectConfig(cwd)
|
|
25
|
+
const libPath = options.library || config?.components?.library || null
|
|
26
|
+
const langId = config?.lenguaje || "react"
|
|
27
|
+
|
|
28
|
+
console.log(chalk.cyan("\n📦 Available components\n"))
|
|
29
|
+
|
|
30
|
+
// 1. From language module templates
|
|
31
|
+
const index = getLanguageIndex(langId)
|
|
32
|
+
if (index?.templates) {
|
|
33
|
+
console.log(chalk.blue(`📚 Templates (${langId} module):`))
|
|
34
|
+
for (const tmpl of index.templates) {
|
|
35
|
+
const teachBadge = tmpl.hasTeachMe ? chalk.green(" [teach]") : ""
|
|
36
|
+
const darkBadge = tmpl.darkMode ? " 🌙" : ""
|
|
37
|
+
console.log(` 📦 ${chalk.bold(tmpl.id)}${teachBadge}${darkBadge}`)
|
|
38
|
+
console.log(` ${tmpl.description}`)
|
|
39
|
+
console.log(` deps: ${tmpl.dependencies?.join(", ") || "none"}`)
|
|
40
|
+
console.log("")
|
|
26
41
|
}
|
|
27
|
-
} else {
|
|
28
|
-
console.log(chalk.yellow(` ⚠️ No existe ${relative(cwd, sourceDir)}/`));
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
//
|
|
44
|
+
// 2. From library (diseno-componentes)
|
|
32
45
|
if (libPath && existsSync(libPath)) {
|
|
33
|
-
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
46
|
+
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
34
47
|
if (existsSync(manifestPath)) {
|
|
35
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
36
|
-
console.log(chalk.blue(
|
|
48
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
49
|
+
console.log(chalk.blue(`📚 Library (${relative(cwd, libPath)}/):`))
|
|
37
50
|
for (const [name, info] of Object.entries(manifest.componentes)) {
|
|
38
|
-
const tags = info.tags ? info.tags.join(", ") : ""
|
|
39
|
-
console.log(` 📦 ${chalk.bold(name)}${tags ? ` — ${tags}` : ""}`)
|
|
40
|
-
if (info.variants) console.log(` Variants: ${info.variants.join(", ")}`)
|
|
51
|
+
const tags = info.tags ? info.tags.join(", ") : ""
|
|
52
|
+
console.log(` 📦 ${chalk.bold(name)}${tags ? ` — ${tags}` : ""}`)
|
|
53
|
+
if (info.variants) console.log(` Variants: ${info.variants.join(", ")}`)
|
|
54
|
+
console.log("")
|
|
41
55
|
}
|
|
42
56
|
}
|
|
43
57
|
} else if (libPath) {
|
|
44
|
-
console.log(chalk.yellow(
|
|
58
|
+
console.log(chalk.yellow(` ⚠️ Library not found: ${libPath}`))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Project components
|
|
62
|
+
const sourceDir = config?.components?.source || join(cwd, "src/components/ui")
|
|
63
|
+
if (existsSync(sourceDir)) {
|
|
64
|
+
console.log(chalk.blue(`📁 Project (${relative(cwd, sourceDir)}/):`))
|
|
65
|
+
const files = listComponents(sourceDir)
|
|
66
|
+
if (files.length === 0) {
|
|
67
|
+
console.log(" (empty)\n")
|
|
68
|
+
} else {
|
|
69
|
+
for (const f of files) {
|
|
70
|
+
const tags = extractTags(join(sourceDir, f))
|
|
71
|
+
console.log(` ✅ ${chalk.bold(f.replace(/\.(tsx|ts)$/, ""))}${tags ? ` — ${tags}` : ""}`)
|
|
72
|
+
}
|
|
73
|
+
console.log("")
|
|
74
|
+
}
|
|
45
75
|
}
|
|
46
76
|
|
|
47
|
-
console.log(
|
|
77
|
+
console.log(chalk.gray(` Tip: npx openPrompt-Lang component add <id> --template`))
|
|
78
|
+
console.log(chalk.gray(` Tip: npx openPrompt-Lang teach <id> for component lessons\n`))
|
|
48
79
|
}
|
|
49
80
|
|
|
50
81
|
export async function componentAdd(name, options) {
|
|
51
|
-
const cwd = process.cwd()
|
|
52
|
-
const config = loadProjectConfig(cwd)
|
|
53
|
-
const
|
|
82
|
+
const cwd = process.cwd()
|
|
83
|
+
const config = loadProjectConfig(cwd)
|
|
84
|
+
const langId = config?.lenguaje || "react"
|
|
85
|
+
|
|
86
|
+
// Try language module template first
|
|
87
|
+
if (options.template) {
|
|
88
|
+
const template = getTemplate(name, langId)
|
|
89
|
+
if (!template) {
|
|
90
|
+
console.log(chalk.red(`\n❌ Template "${name}" not found in ${langId} module\n`))
|
|
91
|
+
console.log(chalk.cyan(` Run: npx openPrompt-Lang lang index ${langId}\n`))
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const targetDir = config?.components?.source || join(cwd, "src/components/ui")
|
|
96
|
+
mkdirSync(targetDir, { recursive: true })
|
|
97
|
+
|
|
98
|
+
const ext = extname(template.metadata.file) || ".tsx"
|
|
99
|
+
const outputName = options.output || name
|
|
100
|
+
const targetFile = join(targetDir, `${outputName}${ext}`)
|
|
101
|
+
|
|
102
|
+
let content = template.content
|
|
103
|
+
|
|
104
|
+
// Process variants if specified
|
|
105
|
+
if (options.variants) {
|
|
106
|
+
const variants = options.variants.split(",").map((v) => v.trim())
|
|
107
|
+
content = injectVariants(content, variants)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (existsSync(targetFile) && !options.force) {
|
|
111
|
+
console.log(chalk.yellow(` ⚠️ ${outputName}${ext} already exists. Skipping.\n`))
|
|
112
|
+
console.log(chalk.cyan(` Use --force to overwrite.\n`))
|
|
113
|
+
return
|
|
114
|
+
}
|
|
54
115
|
|
|
116
|
+
writeFileSync(targetFile, content, "utf-8")
|
|
117
|
+
console.log(chalk.green(`\n ✅ ${outputName} added from "${name}" template\n`))
|
|
118
|
+
|
|
119
|
+
if (template.metadata.hasTeachMe) {
|
|
120
|
+
console.log(
|
|
121
|
+
chalk.cyan(` 💡 Tip: npx openPrompt-Lang teach ${name} to learn about this component`)
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
if (template.metadata.fixHistory?.length > 0) {
|
|
125
|
+
console.log(
|
|
126
|
+
chalk.yellow(` 🐛 This component has ${template.metadata.fixHistory.length} learned fixes`)
|
|
127
|
+
)
|
|
128
|
+
console.log(chalk.cyan(` npx openPrompt-Lang lang errors ${langId} --template ${name}\n`))
|
|
129
|
+
}
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Fallback to legacy library
|
|
134
|
+
const libPath = options.library || config?.components?.library || null
|
|
55
135
|
if (!libPath || !existsSync(libPath)) {
|
|
56
|
-
console.log(chalk.red(`\n❌
|
|
57
|
-
console.log(
|
|
58
|
-
|
|
136
|
+
console.log(chalk.red(`\n❌ Library not found: ${libPath || "not configured"}`))
|
|
137
|
+
console.log(
|
|
138
|
+
chalk.cyan(" Configure components.library in prompt-lang.json or use --template\n")
|
|
139
|
+
)
|
|
140
|
+
return
|
|
59
141
|
}
|
|
60
142
|
|
|
61
|
-
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
143
|
+
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
62
144
|
if (!existsSync(manifestPath)) {
|
|
63
|
-
console.log(chalk.red("\n❌
|
|
64
|
-
return
|
|
145
|
+
console.log(chalk.red("\n❌ Library has no componentes.json\n"))
|
|
146
|
+
return
|
|
65
147
|
}
|
|
66
148
|
|
|
67
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
68
|
-
const compInfo = manifest.componentes?.[name]
|
|
149
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
150
|
+
const compInfo = manifest.componentes?.[name]
|
|
69
151
|
if (!compInfo) {
|
|
70
|
-
console.log(chalk.red(`\n❌
|
|
71
|
-
|
|
72
|
-
return;
|
|
152
|
+
console.log(chalk.red(`\n❌ Component "${name}" not found in library\n`))
|
|
153
|
+
return
|
|
73
154
|
}
|
|
74
155
|
|
|
75
|
-
const srcFile = join(libPath, compInfo.path)
|
|
156
|
+
const srcFile = join(libPath, compInfo.path)
|
|
76
157
|
if (!existsSync(srcFile)) {
|
|
77
|
-
console.log(chalk.red(`\n❌
|
|
78
|
-
return
|
|
158
|
+
console.log(chalk.red(`\n❌ File not found: ${compInfo.path}\n`))
|
|
159
|
+
return
|
|
79
160
|
}
|
|
80
161
|
|
|
81
|
-
const targetDir = config?.components?.source || join(cwd, "src/components/ui")
|
|
82
|
-
mkdirSync(targetDir, { recursive: true })
|
|
162
|
+
const targetDir = config?.components?.source || join(cwd, "src/components/ui")
|
|
163
|
+
mkdirSync(targetDir, { recursive: true })
|
|
83
164
|
|
|
84
|
-
const targetFile = join(targetDir, basename(srcFile))
|
|
165
|
+
const targetFile = join(targetDir, basename(srcFile))
|
|
85
166
|
if (existsSync(targetFile)) {
|
|
86
|
-
console.log(chalk.yellow(
|
|
167
|
+
console.log(chalk.yellow(` ⚠️ ${basename(srcFile)} already exists. Overwritten.\n`))
|
|
87
168
|
}
|
|
88
169
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
console.log(chalk.green(`\n ✅ ${name} agregado a src/components/ui/${basename(srcFile)}\n`));
|
|
170
|
+
copyFileSync(srcFile, targetFile)
|
|
171
|
+
console.log(chalk.green(`\n ✅ ${name} added to src/components/ui/${basename(srcFile)}\n`))
|
|
92
172
|
}
|
|
93
173
|
|
|
94
174
|
export async function componentInit(options) {
|
|
95
|
-
const libPath = options.library || join(process.cwd(), "diseno-componentes")
|
|
175
|
+
const libPath = options.library || join(process.cwd(), "diseno-componentes")
|
|
96
176
|
|
|
97
177
|
if (existsSync(libPath) && readdirSync(libPath).length > 0) {
|
|
98
|
-
console.log(chalk.yellow(`\n ⚠️
|
|
99
|
-
return
|
|
178
|
+
console.log(chalk.yellow(`\n ⚠️ ${libPath} already exists and is not empty.\n`))
|
|
179
|
+
return
|
|
100
180
|
}
|
|
101
181
|
|
|
102
|
-
mkdirSync(join(libPath, "ui"), { recursive: true })
|
|
103
|
-
mkdirSync(join(libPath, "layouts"), { recursive: true })
|
|
104
|
-
|
|
105
|
-
// UI Components
|
|
106
|
-
const buttonContent = `import { cva, type VariantProps } from "class-variance-authority"
|
|
107
|
-
import { memo } from "react"
|
|
108
|
-
|
|
109
|
-
const buttonVariants = cva(
|
|
110
|
-
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
111
|
-
{
|
|
112
|
-
variants: {
|
|
113
|
-
variant: {
|
|
114
|
-
primary: "bg-blue-600 text-white hover:bg-blue-700",
|
|
115
|
-
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
|
116
|
-
danger: "bg-red-600 text-white hover:bg-red-700",
|
|
117
|
-
ghost: "hover:bg-gray-100 hover:text-gray-900",
|
|
118
|
-
},
|
|
119
|
-
size: {
|
|
120
|
-
default: "h-10 px-4 py-2",
|
|
121
|
-
sm: "h-9 rounded-md px-3",
|
|
122
|
-
lg: "h-11 rounded-md px-8",
|
|
123
|
-
icon: "h-10 w-10",
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
defaultVariants: { variant: "primary", size: "default" },
|
|
127
|
-
}
|
|
128
|
-
)
|
|
182
|
+
mkdirSync(join(libPath, "ui"), { recursive: true })
|
|
183
|
+
mkdirSync(join(libPath, "layouts"), { recursive: true })
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
const buttonContent =
|
|
186
|
+
readFromModule("react", "templates/ui/Button.template.tsx") ||
|
|
187
|
+
`import { memo } from "react"
|
|
188
|
+
|
|
189
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
190
|
+
variant?: "primary" | "secondary" | "danger" | "ghost"
|
|
191
|
+
size?: "sm" | "default" | "lg"
|
|
192
|
+
loading?: boolean
|
|
193
|
+
}
|
|
131
194
|
|
|
132
|
-
const Button = memo<ButtonProps>(({
|
|
133
|
-
<button className={
|
|
195
|
+
const Button = memo<ButtonProps>(({ variant = "primary", size = "default", loading, children, ...rest }) => (
|
|
196
|
+
<button type="button" className={\`btn btn-\${variant} btn-\${size}\`} disabled={loading} {...rest}>
|
|
197
|
+
{loading && <span className="spinner" />}
|
|
198
|
+
{children}
|
|
199
|
+
</button>
|
|
134
200
|
))
|
|
135
201
|
Button.displayName = "Button"
|
|
136
|
-
|
|
137
|
-
export { Button, buttonVariants }
|
|
202
|
+
export { Button }
|
|
138
203
|
`
|
|
139
204
|
|
|
140
|
-
const confirmModalContent = `import { useEffect, useRef
|
|
205
|
+
const confirmModalContent = `import { useEffect, useRef } from "react"
|
|
141
206
|
import { Button } from "./Button"
|
|
142
207
|
|
|
143
208
|
interface ConfirmModalProps {
|
|
@@ -146,54 +211,20 @@ interface ConfirmModalProps {
|
|
|
146
211
|
onCancel: () => void
|
|
147
212
|
title: string
|
|
148
213
|
message: string
|
|
149
|
-
confirmLabel?: string
|
|
150
|
-
cancelLabel?: string
|
|
151
214
|
variant?: "danger" | "primary"
|
|
152
215
|
loading?: boolean
|
|
153
|
-
children?: ReactNode
|
|
154
216
|
}
|
|
155
217
|
|
|
156
|
-
export function ConfirmModal({
|
|
157
|
-
open,
|
|
158
|
-
onConfirm,
|
|
159
|
-
onCancel,
|
|
160
|
-
title,
|
|
161
|
-
message,
|
|
162
|
-
confirmLabel = "Confirmar",
|
|
163
|
-
cancelLabel = "Cancelar",
|
|
164
|
-
variant = "danger",
|
|
165
|
-
loading = false,
|
|
166
|
-
children,
|
|
167
|
-
}: ConfirmModalProps) {
|
|
168
|
-
const overlayRef = useRef<HTMLDivElement>(null)
|
|
169
|
-
|
|
170
|
-
useEffect(() => {
|
|
171
|
-
const handleEscape = (e: KeyboardEvent) => {
|
|
172
|
-
if (e.key === "Escape" && !loading) onCancel()
|
|
173
|
-
}
|
|
174
|
-
if (open) document.addEventListener("keydown", handleEscape)
|
|
175
|
-
return () => document.removeEventListener("keydown", handleEscape)
|
|
176
|
-
}, [open, loading, onCancel])
|
|
177
|
-
|
|
218
|
+
export function ConfirmModal({ open, onConfirm, onCancel, title, message, variant = "danger", loading }: ConfirmModalProps) {
|
|
178
219
|
if (!open) return null
|
|
179
|
-
|
|
180
220
|
return (
|
|
181
|
-
<div
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
{message && <p className="mt-2 text-sm text-gray-600">{message}</p>}
|
|
189
|
-
{children && <div className="mt-3">{children}</div>}
|
|
190
|
-
<div className="mt-6 flex justify-end gap-3">
|
|
191
|
-
<Button variant="ghost" onClick={onCancel} disabled={loading}>
|
|
192
|
-
{cancelLabel}
|
|
193
|
-
</Button>
|
|
194
|
-
<Button variant={variant} onClick={onConfirm} disabled={loading}>
|
|
195
|
-
{loading ? "Procesando..." : confirmLabel}
|
|
196
|
-
</Button>
|
|
221
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onCancel}>
|
|
222
|
+
<div className="rounded-lg bg-white p-6 shadow-xl" onClick={(e) => e.stopPropagation()}>
|
|
223
|
+
<h3>{title}</h3>
|
|
224
|
+
<p>{message}</p>
|
|
225
|
+
<div className="mt-4 flex gap-2 justify-end">
|
|
226
|
+
<Button variant="ghost" onClick={onCancel}>Cancel</Button>
|
|
227
|
+
<Button variant={variant} onClick={onConfirm} loading={loading}>Confirm</Button>
|
|
197
228
|
</div>
|
|
198
229
|
</div>
|
|
199
230
|
</div>
|
|
@@ -201,126 +232,125 @@ export function ConfirmModal({
|
|
|
201
232
|
}
|
|
202
233
|
`
|
|
203
234
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
207
|
-
label?: string
|
|
208
|
-
error?: string
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const Input = forwardRef<HTMLInputElement, InputProps>(({ label, error, className, ...props }, ref) => (
|
|
212
|
-
<div className="w-full">
|
|
213
|
-
{label && <label className="mb-1 block text-sm font-medium text-gray-700">{label}</label>}
|
|
214
|
-
<input
|
|
215
|
-
ref={ref}
|
|
216
|
-
className={\`flex h-10 w-full rounded-md border px-3 py-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 \${
|
|
217
|
-
error ? "border-red-500 focus:ring-red-500" : "border-gray-300"
|
|
218
|
-
} \${className || ""}\`}
|
|
219
|
-
{...props}
|
|
220
|
-
/>
|
|
221
|
-
{error && <p className="mt-1 text-xs text-red-600">{error}</p>}
|
|
222
|
-
</div>
|
|
223
|
-
))
|
|
224
|
-
Input.displayName = "Input"
|
|
225
|
-
|
|
226
|
-
export { Input }
|
|
227
|
-
`
|
|
228
|
-
|
|
229
|
-
writeFileSync(join(libPath, "ui/Button.tsx"), buttonContent, "utf-8");
|
|
230
|
-
writeFileSync(join(libPath, "ui/ConfirmModal.tsx"), confirmModalContent, "utf-8");
|
|
231
|
-
writeFileSync(join(libPath, "ui/Input.tsx"), inputContent, "utf-8");
|
|
235
|
+
writeFileSync(join(libPath, "ui/Button.tsx"), buttonContent, "utf-8")
|
|
236
|
+
writeFileSync(join(libPath, "ui/ConfirmModal.tsx"), confirmModalContent, "utf-8")
|
|
232
237
|
|
|
233
|
-
|
|
234
|
-
|
|
238
|
+
writeFileSync(
|
|
239
|
+
join(libPath, "ui/index.ts"),
|
|
240
|
+
`export { Button } from "./Button"
|
|
235
241
|
export { ConfirmModal } from "./ConfirmModal"
|
|
236
|
-
|
|
237
|
-
|
|
242
|
+
`,
|
|
243
|
+
"utf-8"
|
|
244
|
+
)
|
|
238
245
|
|
|
239
|
-
// Manifest
|
|
240
246
|
const manifest = {
|
|
241
247
|
name: options.name || "mi-diseno",
|
|
242
248
|
version: "1.0.0",
|
|
243
|
-
|
|
249
|
+
description: "Reusable component library",
|
|
244
250
|
componentes: {
|
|
245
251
|
Button: {
|
|
246
252
|
path: "ui/Button.tsx",
|
|
247
|
-
tags: ["ui", "form", "
|
|
253
|
+
tags: ["ui", "form", "action"],
|
|
248
254
|
variants: ["primary", "secondary", "danger", "ghost"],
|
|
249
|
-
|
|
255
|
+
description: "Button with variants",
|
|
250
256
|
},
|
|
251
257
|
ConfirmModal: {
|
|
252
258
|
path: "ui/ConfirmModal.tsx",
|
|
253
|
-
tags: ["ui", "modal", "
|
|
254
|
-
|
|
255
|
-
},
|
|
256
|
-
Input: {
|
|
257
|
-
path: "ui/Input.tsx",
|
|
258
|
-
tags: ["ui", "form", "input"],
|
|
259
|
-
descripcion: "Input con label, error y forwardRef",
|
|
259
|
+
tags: ["ui", "modal", "confirmation"],
|
|
260
|
+
description: "Confirmation modal dialog",
|
|
260
261
|
},
|
|
261
262
|
},
|
|
262
|
-
}
|
|
263
|
+
}
|
|
263
264
|
|
|
264
|
-
writeFileSync(join(libPath, MANIFEST_FILE), JSON.stringify(manifest, null, 2), "utf-8")
|
|
265
|
-
console.log(chalk.green(`\n ✅
|
|
266
|
-
console.log(chalk.cyan(`
|
|
267
|
-
console.log(chalk.cyan(` Agrega a prompt-lang.json: "components": { "library": "${libPath}" }\n`));
|
|
265
|
+
writeFileSync(join(libPath, MANIFEST_FILE), JSON.stringify(manifest, null, 2), "utf-8")
|
|
266
|
+
console.log(chalk.green(`\n ✅ Library initialized at ${libPath}/`))
|
|
267
|
+
console.log(chalk.cyan(` 2 components created: Button, ConfirmModal\n`))
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
export async function componentManifest(options) {
|
|
271
|
-
const cwd = process.cwd()
|
|
272
|
-
const config = loadProjectConfig(cwd)
|
|
273
|
-
const libPath = options.library || config?.components?.library || null
|
|
271
|
+
const cwd = process.cwd()
|
|
272
|
+
const config = loadProjectConfig(cwd)
|
|
273
|
+
const libPath = options.library || config?.components?.library || null
|
|
274
274
|
|
|
275
275
|
if (!libPath || !existsSync(libPath)) {
|
|
276
|
-
console.log(chalk.red("\n❌
|
|
277
|
-
return
|
|
276
|
+
console.log(chalk.red("\n❌ Library not found\n"))
|
|
277
|
+
return
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
280
|
+
const manifestPath = join(libPath, MANIFEST_FILE)
|
|
281
281
|
if (!existsSync(manifestPath)) {
|
|
282
|
-
console.log(chalk.yellow("\n ⚠️ No
|
|
283
|
-
return
|
|
282
|
+
console.log(chalk.yellow("\n ⚠️ No componentes.json in library\n"))
|
|
283
|
+
return
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
287
|
-
console.log(chalk.cyan(`\n📚 ${manifest.name} v${manifest.version}`))
|
|
288
|
-
console.log(` ${manifest.
|
|
289
|
-
console.log(`\n
|
|
286
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
287
|
+
console.log(chalk.cyan(`\n📚 ${manifest.name} v${manifest.version}`))
|
|
288
|
+
console.log(` ${manifest.description || ""}`)
|
|
289
|
+
console.log(`\n Components (${Object.keys(manifest.componentes || {}).length}):`)
|
|
290
290
|
for (const [name, info] of Object.entries(manifest.componentes || {})) {
|
|
291
|
-
console.log(` 📦 ${chalk.bold(name)} → ${info.path}`)
|
|
292
|
-
if (info.
|
|
293
|
-
if (info.tags) console.log(` Tags: ${info.tags.join(", ")}`)
|
|
291
|
+
console.log(` 📦 ${chalk.bold(name)} → ${info.path}`)
|
|
292
|
+
if (info.description) console.log(` ${info.description}`)
|
|
293
|
+
if (info.tags) console.log(` Tags: ${info.tags.join(", ")}`)
|
|
294
294
|
}
|
|
295
|
-
console.log("")
|
|
295
|
+
console.log("")
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
299
299
|
|
|
300
300
|
function loadProjectConfig(cwd) {
|
|
301
|
-
const configPath = join(cwd, "prompt-lang.json")
|
|
302
|
-
if (!existsSync(configPath)) return null
|
|
301
|
+
const configPath = join(cwd, "prompt-lang.json")
|
|
302
|
+
if (!existsSync(configPath)) return null
|
|
303
303
|
try {
|
|
304
|
-
return JSON.parse(readFileSync(configPath, "utf-8"))
|
|
304
|
+
return JSON.parse(readFileSync(configPath, "utf-8"))
|
|
305
305
|
} catch {
|
|
306
|
-
return null
|
|
306
|
+
return null
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
function listComponents(dir) {
|
|
311
|
-
const entries = readdirSync(dir, { withFileTypes: true })
|
|
311
|
+
const entries = readdirSync(dir, { withFileTypes: true })
|
|
312
312
|
return entries
|
|
313
|
-
.filter(e => e.isFile() && (e.name.endsWith(".tsx") || e.name.endsWith(".
|
|
314
|
-
.map(e => e.name)
|
|
315
|
-
.sort()
|
|
313
|
+
.filter((e) => e.isFile() && (e.name.endsWith(".tsx") || e.name.endsWith(".ts")))
|
|
314
|
+
.map((e) => e.name)
|
|
315
|
+
.sort()
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
function extractTags(filePath) {
|
|
319
319
|
try {
|
|
320
|
-
const content = readFileSync(filePath, "utf-8")
|
|
321
|
-
const match = content.match(/\/\/\s*@kind\((\w+)\)/)
|
|
322
|
-
return match ? match[1] : null
|
|
320
|
+
const content = readFileSync(filePath, "utf-8")
|
|
321
|
+
const match = content.match(/\/\/\s*@kind\((\w+)\)/)
|
|
322
|
+
return match ? match[1] : null
|
|
323
|
+
} catch {
|
|
324
|
+
return null
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function injectVariants(content, variants) {
|
|
329
|
+
// Find the variants section in cva or the variant enum and filter
|
|
330
|
+
const variantMatch = content.match(/variant:\s*\{([^}]+)\}/s)
|
|
331
|
+
if (!variantMatch) return content
|
|
332
|
+
|
|
333
|
+
const variantBlock = variantMatch[1]
|
|
334
|
+
const variantLines = variantBlock.split("\n").map((l) => l.trim())
|
|
335
|
+
const kept = variantLines.filter((l) =>
|
|
336
|
+
variants.some((v) => l.startsWith(v + ":") || l.startsWith(`"${v}"`))
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
if (kept.length === 0) return content
|
|
340
|
+
|
|
341
|
+
return content.replace(
|
|
342
|
+
/variant:\s*\{[^}]+\}/s,
|
|
343
|
+
`variant: {\n${kept.map((l) => ` ${l}`).join("\n")}\n }`
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function readFromModule(langId, relativePath) {
|
|
348
|
+
try {
|
|
349
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
350
|
+
const fullPath = join(__dirname, "..", "templates", "langs", langId, relativePath)
|
|
351
|
+
if (!existsSync(fullPath)) return null
|
|
352
|
+
return readFileSync(fullPath, "utf-8")
|
|
323
353
|
} catch {
|
|
324
|
-
return null
|
|
354
|
+
return null
|
|
325
355
|
}
|
|
326
356
|
}
|