@wangs-ui/create-react-app 1.0.38 → 1.0.40
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/dist/bin.js +123 -11
- package/package.json +2 -2
- package/template/package.json +4 -4
package/dist/bin.js
CHANGED
|
@@ -7,6 +7,7 @@ import { styleText } from "node:util";
|
|
|
7
7
|
import * as l from "node:readline";
|
|
8
8
|
import l__default from "node:readline";
|
|
9
9
|
import { ReadStream } from "node:tty";
|
|
10
|
+
import { execSync } from "node:child_process";
|
|
10
11
|
//#region \0rolldown/runtime.js
|
|
11
12
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
12
13
|
//#endregion
|
|
@@ -645,6 +646,23 @@ var V = class {
|
|
|
645
646
|
}
|
|
646
647
|
}
|
|
647
648
|
};
|
|
649
|
+
var r = class extends V {
|
|
650
|
+
get cursor() {
|
|
651
|
+
return this.value ? 0 : 1;
|
|
652
|
+
}
|
|
653
|
+
get _value() {
|
|
654
|
+
return this.cursor === 0;
|
|
655
|
+
}
|
|
656
|
+
constructor(t) {
|
|
657
|
+
super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
|
|
658
|
+
this.value = this._value;
|
|
659
|
+
}), this.on("confirm", (i) => {
|
|
660
|
+
this.output.write(import_src.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
|
|
661
|
+
}), this.on("cursor", () => {
|
|
662
|
+
this.value = !this.value;
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
};
|
|
648
666
|
var a = class extends V {
|
|
649
667
|
options;
|
|
650
668
|
cursor = 0;
|
|
@@ -812,6 +830,35 @@ var limitOptions = ({ cursor: l, options: e, style: w, output: p = process.stdou
|
|
|
812
830
|
for (const t of s) for (const n of t) x.push(n);
|
|
813
831
|
return c && x.push(M), x;
|
|
814
832
|
};
|
|
833
|
+
var confirm = (i) => {
|
|
834
|
+
const a = i.active ?? "Yes", s = i.inactive ?? "No";
|
|
835
|
+
return new r({
|
|
836
|
+
active: a,
|
|
837
|
+
inactive: s,
|
|
838
|
+
signal: i.signal,
|
|
839
|
+
input: i.input,
|
|
840
|
+
output: i.output,
|
|
841
|
+
initialValue: i.initialValue ?? true,
|
|
842
|
+
render() {
|
|
843
|
+
const e = i.withGuide ?? settings.withGuide, u = `${symbol(this.state)} `, l = e ? `${styleText("gray", S_BAR)} ` : "", f = wrapTextWithPrefix(i.output, i.message, l, u), o = `${e ? `${styleText("gray", S_BAR)}
|
|
844
|
+
` : ""}${f}
|
|
845
|
+
`, c = this.value ? a : s;
|
|
846
|
+
switch (this.state) {
|
|
847
|
+
case "submit": return `${o}${e ? `${styleText("gray", S_BAR)} ` : ""}${styleText("dim", c)}`;
|
|
848
|
+
case "cancel": return `${o}${e ? `${styleText("gray", S_BAR)} ` : ""}${styleText(["strikethrough", "dim"], c)}${e ? `
|
|
849
|
+
${styleText("gray", S_BAR)}` : ""}`;
|
|
850
|
+
default: {
|
|
851
|
+
const r = e ? `${styleText("cyan", S_BAR)} ` : "", g = e ? styleText("cyan", S_BAR_END) : "";
|
|
852
|
+
return `${o}${r}${this.value ? `${styleText("green", S_RADIO_ACTIVE)} ${a}` : `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", a)}`}${i.vertical ? e ? `
|
|
853
|
+
${styleText("cyan", S_BAR)} ` : `
|
|
854
|
+
` : ` ${styleText("dim", "/")} `}${this.value ? `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", s)}` : `${styleText("green", S_RADIO_ACTIVE)} ${s}`}
|
|
855
|
+
${g}
|
|
856
|
+
`;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}).prompt();
|
|
861
|
+
};
|
|
815
862
|
var MULTISELECT_INSTRUCTIONS = [
|
|
816
863
|
`${styleText("dim", "↑/↓")} to navigate`,
|
|
817
864
|
`${styleText("dim", "Space:")} select`,
|
|
@@ -1122,7 +1169,53 @@ function copyProjectTemplate(options) {
|
|
|
1122
1169
|
}
|
|
1123
1170
|
}
|
|
1124
1171
|
//#endregion
|
|
1125
|
-
//#region
|
|
1172
|
+
//#region src/utils/git.ts
|
|
1173
|
+
function initGitRepository(targetDir) {
|
|
1174
|
+
try {
|
|
1175
|
+
if (fs.existsSync(path.join(targetDir, ".git"))) return false;
|
|
1176
|
+
try {
|
|
1177
|
+
execSync("git init -b main", {
|
|
1178
|
+
cwd: targetDir,
|
|
1179
|
+
stdio: "ignore"
|
|
1180
|
+
});
|
|
1181
|
+
} catch {
|
|
1182
|
+
execSync("git init", {
|
|
1183
|
+
cwd: targetDir,
|
|
1184
|
+
stdio: "ignore"
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
} catch {
|
|
1188
|
+
return false;
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
execSync("git add -A", {
|
|
1192
|
+
cwd: targetDir,
|
|
1193
|
+
stdio: "ignore"
|
|
1194
|
+
});
|
|
1195
|
+
let hasAuthor = true;
|
|
1196
|
+
try {
|
|
1197
|
+
execSync("git config user.name", {
|
|
1198
|
+
cwd: targetDir,
|
|
1199
|
+
stdio: "ignore"
|
|
1200
|
+
});
|
|
1201
|
+
} catch {
|
|
1202
|
+
hasAuthor = false;
|
|
1203
|
+
}
|
|
1204
|
+
if (hasAuthor) execSync("git commit -m \"chore: initial commit from @wangs-ui/create-react-app\"", {
|
|
1205
|
+
cwd: targetDir,
|
|
1206
|
+
stdio: "ignore"
|
|
1207
|
+
});
|
|
1208
|
+
else execSync("git -c user.name=\"Wangs UI Scaffolder\" -c user.email=\"dev@wangs-ui.internal\" commit -m \"chore: initial commit from @wangs-ui/create-react-app\"", {
|
|
1209
|
+
cwd: targetDir,
|
|
1210
|
+
stdio: "ignore"
|
|
1211
|
+
});
|
|
1212
|
+
return true;
|
|
1213
|
+
} catch {
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region ../skills/dist/src-ER4GIzua.js
|
|
1126
1219
|
var SKILL_default$5 = "---\nname: create-form\ndescription: Real-world patterns for building strongly-typed forms, multipart file uploads, server validation mapping, and dirty tracking with @wangs-ui/form.\n---\n\n# Skill: Real-World Form Workflows with `@wangs-ui/form`\n\nUse this skill when building CRUD forms, data entry dialogs, multi-field settings pages, or multipart forms in Wangs UI applications.\n\n---\n\n## 1. MCP Inspection Step (Before Building Fields)\n\nBefore implementing specific input controls, query the MCP server to inspect their exact prop signatures:\n\n- `get-documentation({ id: \"select\" })` — Check option formats (`{ label, value }` vs object).\n- `get-documentation({ id: \"datepicker\" })` — Check `selectionMode` (`single`, `range`, `multiple`) and date format props.\n- `get-documentation({ id: \"multiselect\" })` — Check chips display and filter behavior.\n- `get-documentation({ id: \"fileupload\" })` — Check accepted mime types and upload handlers.\n\n---\n\n## 2. Recipe 1: Standard CRUD Form with Server Validation Mapping\n\nReal-world forms must handle async submission, loading state, and map backend validation errors back into `<Field>` errors:\n\n```tsx\nimport React, { useState } from 'react';\nimport Card from '@wangs-ui/react-core/primitive/card';\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport InputText from '@wangs-ui/react-core/primitive/inputtext';\nimport Select from '@wangs-ui/react-core/primitive/select';\nimport { Form, Field } from '@wangs-ui/react-core';\nimport { useFormControl } from '@wangs-ui/form';\nimport { useI18n } from '@wangs-ui/react-i18n';\n\ninterface UserFormValues {\n fullName: string;\n email: string;\n role: string;\n}\n\nexport default function UserForm({ onSuccess }: { onSuccess?: () => void }) {\n const { t } = useI18n();\n const formControl = useFormControl<UserFormValues>({ type: 'json' });\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const roleOptions = [\n { label: t('Administrator'), value: 'admin' },\n { label: t('Operator'), value: 'operator' },\n { label: t('Viewer'), value: 'viewer' },\n ];\n\n const handleSubmit = async (values: UserFormValues) => {\n setIsSubmitting(true);\n try {\n // Execute API call: await api.createUser(values);\n onSuccess?.();\n } catch (err: any) {\n // Map server validation error directly to field\n if (err?.fieldErrors?.email) {\n formControl.setError('email', {\n type: 'server',\n message: err.fieldErrors.email,\n });\n }\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <Card className=\"p-6\">\n <Form control={formControl} onSubmit={handleSubmit} className=\"flex flex-col gap-m\">\n <h2 className=\"heading-2\">{t('User Information')}</h2>\n\n {/* Text Field */}\n <Field<string>\n name=\"fullName\"\n label={t('Full Name')}\n required\n rules={{ required: t('Full name is required') }}\n >\n {(field) => (\n <InputText {...field} placeholder={t('e.g. Jane Doe')} value={field.value || ''} />\n )}\n </Field>\n\n {/* Email Field with Regex Validation */}\n <Field<string>\n name=\"email\"\n label={t('Email Address')}\n required\n rules={{\n required: t('Email is required'),\n pattern: {\n value: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,\n message: t('Enter a valid email address'),\n },\n }}\n >\n {(field) => (\n <InputText {...field} placeholder={t('name@company.com')} value={field.value || ''} />\n )}\n </Field>\n\n {/* Select Dropdown Field */}\n <Field<string>\n name=\"role\"\n label={t('Access Role')}\n required\n rules={{ required: t('Role selection is required') }}\n >\n {(field) => (\n <Select\n {...field}\n options={roleOptions}\n placeholder={t('Select role')}\n value={field.value}\n onChange={(e) => field.onChange(e.value)}\n />\n )}\n </Field>\n\n {/* Form Actions */}\n <div className=\"flex justify-end gap-s pt-s\">\n <Button\n type=\"button\"\n variant=\"text\"\n label={t('Reset')}\n onClick={() => formControl.reset()}\n disabled={isSubmitting}\n />\n <Button type=\"submit\" label={t('Save User')} severity=\"primary\" loading={isSubmitting} />\n </div>\n </Form>\n </Card>\n );\n}\n```\n\n---\n\n## 3. Recipe 2: Multipart Form (File Upload + Metadata)\n\nFor file uploads (avatar, attachment, documents), set `type: 'formdata'`:\n\n```tsx\nimport { useFormControl } from '@wangs-ui/form';\n\ninterface ProfileUploadValues {\n displayName: string;\n avatar: File | null;\n}\n\n// Generates FormData under the hood\nconst formControl = useFormControl<ProfileUploadValues>({ type: 'formdata' });\n```\n\n---\n\n## 4. Key Implementation Rules\n\n1. **Always Use `<Form>` & `<Field>`**: Never bind raw uncontrolled inputs.\n2. **Translate All Labels & Error Messages**: Wrap text in `t('...')`.\n3. **Handle Loading State**: Disable reset buttons and set `loading={isSubmitting}` on submit buttons.\n";
|
|
1127
1220
|
var SKILL_default$4 = "---\nname: data-table\ndescription: Real-world patterns for building full CRUD DataTables with server pagination, search filters, batch actions, and confirmation modals.\n---\n\n# Skill: Real-World DataTable & Filter Workflows\n\nUse this skill when building administrative grids, filtered listing pages, or management dashboards with `@wangs-ui/react-core`.\n\n---\n\n## 1. MCP Inspection Step (Before Building Table)\n\nQuery the MCP server to inspect supported features and slots:\n\n- `get-documentation({ id: \"datatable\" })` — Check `paginator`, `lazy`, `onPage`, `onSort`, `selectionMode`, and `dataKey`.\n- `get-documentation({ id: \"column\" })` — Check `body` template, `sortable`, `frozen`, and `headerStyle`.\n- `get-documentation({ id: \"tag\" })` — Check severity colors for status pill badges (`success`, `warning`, `danger`, `info`).\n\n---\n\n## 2. Recipe: Full CRUD Data Grid with Filter Toolbar & Batch Actions\n\n```tsx\nimport React, { useState } from 'react';\nimport DataTable, { Column } from '@wangs-ui/react-core/primitive/datatable';\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport InputText from '@wangs-ui/react-core/primitive/inputtext';\nimport Select from '@wangs-ui/react-core/primitive/select';\nimport Tag from '@wangs-ui/react-core/primitive/tag';\nimport Dialog from '@wangs-ui/react-core/primitive/dialog';\nimport Card from '@wangs-ui/react-core/primitive/card';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { SearchLine, DeleteBin6Line, EditLine, AddLine } from '@wangs-ui/react-icons';\n\ninterface CustomerRecord {\n id: string;\n name: string;\n email: string;\n status: 'active' | 'pending' | 'suspended';\n createdAt: string;\n}\n\nexport default function CustomerManagementView() {\n const { t } = useI18n();\n const [records, setRecords] = useState<CustomerRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedStatus, setSelectedStatus] = useState<string | null>(null);\n const [selectedRows, setSelectedRows] = useState<CustomerRecord[]>([]);\n const [deleteTarget, setDeleteTarget] = useState<CustomerRecord | null>(null);\n\n // Status Badge Template\n const statusTemplate = (row: CustomerRecord) => {\n const severityMap: Record<string, 'success' | 'warning' | 'danger'> = {\n active: 'success',\n pending: 'warning',\n suspended: 'danger',\n };\n return <Tag value={t(row.status)} severity={severityMap[row.status] || 'info'} />;\n };\n\n // Row Actions Template\n const actionsTemplate = (row: CustomerRecord) => (\n <div className=\"flex items-center gap-xs\">\n <Button\n variant=\"text\"\n icon={<EditLine />}\n aria-label={t('Edit')}\n onClick={() => console.log('Edit', row.id)}\n />\n <Button\n variant=\"text\"\n severity=\"danger\"\n icon={<DeleteBin6Line />}\n aria-label={t('Delete')}\n onClick={() => setDeleteTarget(row)}\n />\n </div>\n );\n\n return (\n <Card className=\"flex flex-col gap-m p-6\">\n {/* 1. Filter & Search Toolbar */}\n <div className=\"flex flex-wrap items-center justify-between gap-s\">\n <div className=\"flex flex-wrap items-center gap-s\">\n <InputText\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={t('Search customer...')}\n className=\"w-64\"\n />\n <Select\n value={selectedStatus}\n options={[\n { label: t('All Statuses'), value: null },\n { label: t('Active'), value: 'active' },\n { label: t('Pending'), value: 'pending' },\n { label: t('Suspended'), value: 'suspended' },\n ]}\n onChange={(e) => setSelectedStatus(e.value)}\n placeholder={t('Filter status')}\n />\n </div>\n\n <Button label={t('Add Customer')} icon={<AddLine />} severity=\"primary\" />\n </div>\n\n {/* 2. Batch Selection Action Bar */}\n {selectedRows.length > 0 && (\n <div className=\"flex items-center justify-between rounded bg-primary-50 px-4 py-2 text-primary-900\">\n <span className=\"p font-medium\">\n {t('{{count}} items selected', { count: selectedRows.length })}\n </span>\n <Button\n size=\"small\"\n severity=\"danger\"\n label={t('Delete Selected')}\n icon={<DeleteBin6Line />}\n onClick={() => console.log('Batch delete', selectedRows)}\n />\n </div>\n )}\n\n {/* 3. Paginated DataTable */}\n <DataTable\n value={records}\n loading={loading}\n selection={selectedRows}\n onSelectionChange={(e) => setSelectedRows(e.value)}\n dataKey=\"id\"\n paginator\n rows={10}\n rowsPerPageOptions={[10, 25, 50]}\n emptyMessage={t('No customers found')}\n >\n <Column selectionMode=\"multiple\" headerStyle={{ width: '3rem' }} />\n <Column field=\"name\" header={t('Customer Name')} sortable />\n <Column field=\"email\" header={t('Email')} sortable />\n <Column field=\"status\" header={t('Status')} body={statusTemplate} sortable />\n <Column header={t('Actions')} body={actionsTemplate} headerStyle={{ width: '6rem' }} />\n </DataTable>\n\n {/* 4. Delete Confirmation Dialog */}\n <Dialog\n visible={!!deleteTarget}\n onHide={() => setDeleteTarget(null)}\n header={t('Delete Customer')}\n footer={\n <div className=\"flex justify-end gap-xs\">\n <Button variant=\"text\" label={t('Cancel')} onClick={() => setDeleteTarget(null)} />\n <Button\n severity=\"danger\"\n label={t('Delete')}\n onClick={() => {\n // Execute delete API\n setDeleteTarget(null);\n }}\n />\n </div>\n }\n >\n <p className=\"p\">\n {t('Are you sure you want to delete {{name}}?', { name: deleteTarget?.name })}\n </p>\n </Dialog>\n </Card>\n );\n}\n```\n\n---\n\n## 3. Mandatory Best Practices\n\n1. **Always Supply `dataKey`**: Never enable row selection without `dataKey=\"id\"`.\n2. **Translate All Headers & Messages**: Pass table headers and empty state messages into `t()`.\n3. **Control Batch Action Appearance**: Show batch action banner only when `selectedRows.length > 0`.\n";
|
|
1128
1221
|
var SKILL_default$3 = "---\nname: dialog-modal\ndescription: Real-world patterns for modal forms, async confirmation workflows, and multi-step dialogs with Wangs UI Dialog and Modal components.\n---\n\n# Skill: Real-World Dialog & Modal Workflows\n\nUse this skill when building interactive modals, create/edit modal forms, destructive action confirmations, or slide-in overlay panels.\n\n---\n\n## 1. MCP Inspection Step (Before Implementing Overlays)\n\nQuery the MCP server to check overlay configuration and animation options:\n\n- `get-documentation({ id: \"dialog\" })` — Check `header`, `footer`, `visible`, `onHide`, `modal`, and `dismissableMask`.\n- `get-documentation({ id: \"modal\" })` — Check fullscreen modes, size variants, and slide-in drawer options.\n- `get-documentation({ id: \"toast\" })` — Check severity toasts (`success`, `error`, `info`, `warn`) to trigger after modal actions.\n\n---\n\n## 2. Recipe: Create/Edit Form inside a Modal Dialog\n\nThis real-world recipe coordinates a modal wrapper with an embedded `@wangs-ui/form`, handles saving state, prevents accidental dismiss while saving, and resets state upon close:\n\n```tsx\nimport React, { useState, useEffect } from 'react';\nimport Dialog from '@wangs-ui/react-core/primitive/dialog';\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport InputText from '@wangs-ui/react-core/primitive/inputtext';\nimport { Form, Field } from '@wangs-ui/react-core';\nimport { useFormControl } from '@wangs-ui/form';\nimport { useI18n } from '@wangs-ui/react-i18n';\n\ninterface EditItemModel {\n title: string;\n code: string;\n}\n\ninterface ItemModalProps {\n visible: boolean;\n item?: EditItemModel | null;\n onHide: () => void;\n onSaved: (item: EditItemModel) => void;\n}\n\nexport default function ItemFormModal({ visible, item, onHide, onSaved }: ItemModalProps) {\n const { t } = useI18n();\n const formControl = useFormControl<EditItemModel>({ type: 'json' });\n const [saving, setSaving] = useState(false);\n\n // Sync form values when modal opens or item changes\n useEffect(() => {\n if (visible) {\n formControl.reset(item || { title: '', code: '' });\n }\n }, [visible, item]);\n\n const handleFormSubmit = async (values: EditItemModel) => {\n setSaving(true);\n try {\n // Execute API call: await api.save(values);\n onSaved(values);\n onHide();\n } finally {\n setSaving(false);\n }\n };\n\n const footerActions = (\n <div className=\"flex justify-end gap-xs\">\n <Button type=\"button\" variant=\"text\" label={t('Cancel')} onClick={onHide} disabled={saving} />\n <Button\n type=\"submit\"\n form=\"modal-item-form\"\n label={item ? t('Save Changes') : t('Create Item')}\n severity=\"primary\"\n loading={saving}\n />\n </div>\n );\n\n return (\n <Dialog\n visible={visible}\n onHide={() => !saving && onHide()}\n header={item ? t('Edit Item') : t('New Item')}\n footer={footerActions}\n style={{ width: '450px' }}\n modal\n >\n <Form\n id=\"modal-item-form\"\n control={formControl}\n onSubmit={handleFormSubmit}\n className=\"flex flex-col gap-m pt-xs\"\n >\n <Field<string>\n name=\"title\"\n label={t('Item Title')}\n required\n rules={{ required: t('Title is required') }}\n >\n {(field) => (\n <InputText {...field} placeholder={t('Enter title')} value={field.value || ''} />\n )}\n </Field>\n\n <Field<string>\n name=\"code\"\n label={t('Item Code')}\n required\n rules={{ required: t('Code is required') }}\n >\n {(field) => (\n <InputText {...field} placeholder={t('e.g. SKU-100')} value={field.value || ''} />\n )}\n </Field>\n </Form>\n </Dialog>\n );\n}\n```\n\n---\n\n## 3. Mandatory Best Practices\n\n1. **Decouple Submit Button from Form Body**: Use `form=\"modal-item-form\"` on the submit button inside `footer` so actions stay neatly aligned in the footer bar.\n2. **Prevent Close During Mutation**: Guard `onHide={() => !saving && onHide()}` to prevent accidental dismissal during in-flight network requests.\n3. **Always Reset on Open**: Sync initial values in an effect keyed on `visible`.\n";
|
|
@@ -1285,7 +1378,7 @@ function getRunCommand(pm, script) {
|
|
|
1285
1378
|
//#region src/index.ts
|
|
1286
1379
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
1287
1380
|
function generateProject(options) {
|
|
1288
|
-
const { projectName, targetDir, preset, agents, skills } = options;
|
|
1381
|
+
const { projectName, targetDir, preset, agents, skills, git } = options;
|
|
1289
1382
|
let templateDir = path.resolve(__dirname, "../template");
|
|
1290
1383
|
if (!fs.existsSync(templateDir)) templateDir = path.resolve(__dirname, "template");
|
|
1291
1384
|
const s = spinner();
|
|
@@ -1308,13 +1401,16 @@ function generateProject(options) {
|
|
|
1308
1401
|
agents,
|
|
1309
1402
|
skills
|
|
1310
1403
|
});
|
|
1404
|
+
let gitInitialized = false;
|
|
1405
|
+
if (git !== false) gitInitialized = initGitRepository(targetDir);
|
|
1311
1406
|
s.stop("Project structure initialized.");
|
|
1312
1407
|
const pm = detectPackageManager();
|
|
1313
1408
|
const installCmd = getInstallCommand(pm);
|
|
1314
1409
|
const devCmd = getRunCommand(pm, "dev");
|
|
1315
1410
|
const lintCmd = getRunCommand(pm, "lint");
|
|
1316
|
-
if (configuredAgents.length > 0) {
|
|
1317
|
-
console.log("\n\x1B[1m
|
|
1411
|
+
if (configuredAgents.length > 0 || gitInitialized) {
|
|
1412
|
+
console.log("\n\x1B[1m⚙ Setup Highlights:\x1B[0m");
|
|
1413
|
+
if (gitInitialized) console.log(" \x1B[32m✔\x1B[0m Git repository initialized (initial commit created)");
|
|
1318
1414
|
for (const agent of configuredAgents) console.log(` \x1b[32m✔\x1b[0m ${agent}`);
|
|
1319
1415
|
}
|
|
1320
1416
|
outro(`\x1b[32m✨ Project created successfully!\x1b[0m\n\n\x1b[1mNext steps:\x1b[0m\n ${targetDir === process.cwd() ? "" : `cd ${path.relative(process.cwd(), targetDir)}\n `}${installCmd}\n ${devCmd}\n ${lintCmd} \x1b[2m(runs Oxlint)\x1b[0m`);
|
|
@@ -1322,7 +1418,7 @@ function generateProject(options) {
|
|
|
1322
1418
|
//#endregion
|
|
1323
1419
|
//#region src/prompts.ts
|
|
1324
1420
|
async function promptUser(initialOptions) {
|
|
1325
|
-
let { projectName, preset, agents, skills } = initialOptions;
|
|
1421
|
+
let { projectName, preset, agents, skills, git } = initialOptions;
|
|
1326
1422
|
const allSkills = loadAllSkills();
|
|
1327
1423
|
if (initialOptions.yes) return {
|
|
1328
1424
|
projectName: projectName || "my-wangs-app",
|
|
@@ -1333,9 +1429,10 @@ async function promptUser(initialOptions) {
|
|
|
1333
1429
|
"claude",
|
|
1334
1430
|
"kilo"
|
|
1335
1431
|
],
|
|
1336
|
-
skills: skills || allSkills.map((s) => s.id)
|
|
1432
|
+
skills: skills || allSkills.map((s) => s.id),
|
|
1433
|
+
git: git !== void 0 ? git : true
|
|
1337
1434
|
};
|
|
1338
|
-
intro(`\x1b[36m🚀 Wangs UI React App Scaffolder\x1b[0m [2m(v1.0.
|
|
1435
|
+
intro(`\x1b[36m🚀 Wangs UI React App Scaffolder\x1b[0m [2m(v1.0.40)[0m`);
|
|
1339
1436
|
if (!projectName) {
|
|
1340
1437
|
const nameResponse = await text({
|
|
1341
1438
|
message: "What is your project name?",
|
|
@@ -1425,11 +1522,23 @@ async function promptUser(initialOptions) {
|
|
|
1425
1522
|
}
|
|
1426
1523
|
skills = skillsResponse;
|
|
1427
1524
|
}
|
|
1525
|
+
if (git === void 0) {
|
|
1526
|
+
const gitResponse = await confirm({
|
|
1527
|
+
message: "Initialize a new git repository and create initial commit?",
|
|
1528
|
+
initialValue: true
|
|
1529
|
+
});
|
|
1530
|
+
if (isCancel(gitResponse)) {
|
|
1531
|
+
cancel("Project scaffolding cancelled.");
|
|
1532
|
+
process$1.exit(0);
|
|
1533
|
+
}
|
|
1534
|
+
git = Boolean(gitResponse);
|
|
1535
|
+
}
|
|
1428
1536
|
return {
|
|
1429
1537
|
projectName,
|
|
1430
1538
|
preset: "fixedasset",
|
|
1431
1539
|
agents,
|
|
1432
|
-
skills: skills || allSkills.map((s) => s.id)
|
|
1540
|
+
skills: skills || allSkills.map((s) => s.id),
|
|
1541
|
+
git: Boolean(git)
|
|
1433
1542
|
};
|
|
1434
1543
|
}
|
|
1435
1544
|
//#endregion
|
|
@@ -1486,16 +1595,19 @@ async function main() {
|
|
|
1486
1595
|
if (val === "all") options.skills = void 0;
|
|
1487
1596
|
else if (val === "none") options.skills = [];
|
|
1488
1597
|
else options.skills = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1489
|
-
} else if (
|
|
1598
|
+
} else if (arg === "--git" || arg === "--git=true") options.git = true;
|
|
1599
|
+
else if (arg === "--no-git" || arg === "--git=false") options.git = false;
|
|
1600
|
+
else if (!arg.startsWith("-") && !options.projectName) options.projectName = arg;
|
|
1490
1601
|
}
|
|
1491
|
-
const { projectName, preset, agents, skills } = await promptUser(options);
|
|
1602
|
+
const { projectName, preset, agents, skills, git } = await promptUser(options);
|
|
1492
1603
|
const targetDir = path.resolve(process$1.cwd(), projectName);
|
|
1493
1604
|
generateProject({
|
|
1494
1605
|
projectName: path.basename(targetDir),
|
|
1495
1606
|
targetDir,
|
|
1496
1607
|
preset,
|
|
1497
1608
|
agents,
|
|
1498
|
-
skills
|
|
1609
|
+
skills,
|
|
1610
|
+
git
|
|
1499
1611
|
});
|
|
1500
1612
|
}
|
|
1501
1613
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wangs-ui/create-react-app",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.40",
|
|
4
4
|
"description": "Scaffold a modern React app with Wangs UI, Vite 8, Oxlint, Oxfmt, and AI Agent MCP integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antigravity",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@clack/prompts": "^1.7.0",
|
|
54
|
-
"@wangs-ui/skills": "1.0.
|
|
54
|
+
"@wangs-ui/skills": "1.0.40"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {},
|
|
57
57
|
"scripts": {
|
package/template/package.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"format:check": "oxfmt --check"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@wangs-ui/react-core": "^1.0.
|
|
17
|
-
"@wangs-ui/react-i18n": "^1.0.
|
|
18
|
-
"@wangs-ui/react-icons": "^1.0.
|
|
19
|
-
"@wangs-ui/react-presets": "^1.0.
|
|
16
|
+
"@wangs-ui/react-core": "^1.0.40",
|
|
17
|
+
"@wangs-ui/react-i18n": "^1.0.40",
|
|
18
|
+
"@wangs-ui/react-icons": "^1.0.40",
|
|
19
|
+
"@wangs-ui/react-presets": "^1.0.40",
|
|
20
20
|
"clsx": "^2.1.1",
|
|
21
21
|
"react": "^19.2.7",
|
|
22
22
|
"react-dom": "^19.2.7",
|