phpxui 0.1.3 → 0.1.5
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 +133 -41
- package/dist/cli.js +1 -0
- package/dist/commands/icons.js +1 -1
- package/dist/generators/php-component.js +1 -1
- package/dist/generators/php-components-bulk.js +1 -1
- package/dist/generators/phpxui-component-payload.js +1 -0
- package/dist/generators/write-component.js +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/load-config.js +1 -1
- package/dist/utils/phpxui-ai-context.js +1 -0
- package/dist/utils/phpxui-manifest.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
|
-
# **phpxui‑cli** —
|
|
1
|
+
# **phpxui‑cli** — Instant PHPXUI Component Generator 🚀
|
|
2
2
|
|
|
3
|
-
> **Generate fully‑typed PHPXUI components for Prisma
|
|
4
|
-
> ⚡ **
|
|
3
|
+
> **Generate fully‑typed PHPXUI components for Prisma PHP right from the terminal.**
|
|
4
|
+
> ⚡ **Add one** → `npx phpxui add Alert` | 🌌 **Add all** → `npx phpxui add --all` | 🔁 **Update installed** → `npx phpxui update`
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
## ✨
|
|
8
|
+
## ✨ Features
|
|
9
9
|
|
|
10
|
-
| Feature
|
|
11
|
-
|
|
|
12
|
-
| **Bulk install**
|
|
13
|
-
| **
|
|
14
|
-
| **
|
|
15
|
-
| **
|
|
16
|
-
| **
|
|
10
|
+
| Feature | Details |
|
|
11
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| **Bulk install** | `--all` downloads every component in one shot. |
|
|
13
|
+
| **Update in place** | `update` scans your `outputDir` and re‑downloads every installed component (overwrite). |
|
|
14
|
+
| **Ready‑to‑use code** | Each file already contains the `$class` merge logic and `{$attributes}` placeholder for **Wave** reactivity. |
|
|
15
|
+
| **Clean paths** | Files are written under `src/Lib/PHPXUI/FancyName.php` with OS‑agnostic separators. |
|
|
16
|
+
| **Friendly output** | Clear green / red summary with relative paths only. |
|
|
17
|
+
| **PPIcons bootstrap** | `add` and `update` install the required baseline `ppicons` set automatically, and still install any extra icons detected in generated files. |
|
|
18
|
+
| **Tailwind bootstrap** | Ensures `tw-animate-css` and (on first run / missing file) writes `src/app/globals.css` for a sensible baseline. |
|
|
19
|
+
| **AI project context** | Refreshes the `manifest` block inside `phpxui.json` and managed `AGENTS.md` / `.github/copilot-instructions.md` blocks after changes. |
|
|
20
|
+
|
|
21
|
+
`phpxui.json` is the CLI's own config file. In a Prisma PHP app, the framework config remains the root `prisma-php.json` file.
|
|
17
22
|
|
|
18
23
|
---
|
|
19
24
|
|
|
20
|
-
## 📦
|
|
25
|
+
## 📦 Installation
|
|
21
26
|
|
|
22
27
|
```bash
|
|
23
28
|
# Global
|
|
24
29
|
npm install -g phpxui
|
|
25
30
|
|
|
26
|
-
#
|
|
31
|
+
# Or as a dev‑dependency
|
|
27
32
|
npm install -D phpxui
|
|
28
33
|
```
|
|
29
34
|
|
|
30
|
-
> Requires **Node
|
|
35
|
+
> Requires **Node 18+** and a Prisma PHP project (PHP 8.2+).
|
|
31
36
|
|
|
32
37
|
---
|
|
33
38
|
|
|
34
|
-
## 🚀
|
|
39
|
+
## 🚀 Quick Start
|
|
35
40
|
|
|
36
41
|
```bash
|
|
37
|
-
#
|
|
42
|
+
# Add a single component
|
|
38
43
|
npx phpxui add Alert
|
|
39
44
|
|
|
40
|
-
#
|
|
45
|
+
# Add multiple components at once
|
|
41
46
|
npx phpxui add Alert Dialog Badge
|
|
42
47
|
|
|
43
|
-
#
|
|
48
|
+
# Add the entire component set
|
|
44
49
|
npx phpxui add --all
|
|
45
50
|
```
|
|
46
51
|
|
|
47
52
|
CLI output example:
|
|
48
53
|
|
|
49
54
|
```bash
|
|
50
|
-
📦 Installing ppicons CLI…
|
|
51
|
-
✨ Installing default icons: x chevron-down chevron-right
|
|
52
|
-
✔ Icons installed in src/Lib/PPIcons
|
|
53
|
-
|
|
54
55
|
✔ Alert → src/Lib/PHPXUI/Alert.php
|
|
55
56
|
✔ Dialog → src/Lib/PHPXUI/Dialog.php
|
|
56
57
|
✔ Badge → src/Lib/PHPXUI/Badge.php
|
|
@@ -68,11 +69,13 @@ class Alert extends PHPX
|
|
|
68
69
|
{
|
|
69
70
|
public function render(): string
|
|
70
71
|
{
|
|
71
|
-
$attributes = $this->getAttributes();
|
|
72
72
|
$class = $this->getMergeClasses();
|
|
73
|
+
$attributes = $this->getAttributes([
|
|
74
|
+
'class' => $class,
|
|
75
|
+
]);
|
|
73
76
|
|
|
74
77
|
return <<<HTML
|
|
75
|
-
<div {$attributes}
|
|
78
|
+
<div {$attributes}>
|
|
76
79
|
{$this->children}
|
|
77
80
|
</div>
|
|
78
81
|
HTML;
|
|
@@ -82,19 +85,108 @@ class Alert extends PHPX
|
|
|
82
85
|
|
|
83
86
|
---
|
|
84
87
|
|
|
85
|
-
##
|
|
88
|
+
## 🔁 Updating Components
|
|
89
|
+
|
|
90
|
+
### Update everything you already installed
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx phpxui update
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
What it does:
|
|
97
|
+
|
|
98
|
+
- Reads your `phpxui.json` (creates it if missing)
|
|
99
|
+
- Resolves your `outputDir` (default: `src/Lib/PHPXUI`)
|
|
100
|
+
- Scans that folder for `*.php` files (e.g. `Alert.php`, `Dialog.php`)
|
|
101
|
+
- Re-downloads each matching component from the PHPXUI catalogue
|
|
102
|
+
- **Overwrites files automatically** (no `--force` required)
|
|
103
|
+
|
|
104
|
+
If you have no generated components yet, it will report:
|
|
105
|
+
|
|
106
|
+
- `⚠ No components found to update.`
|
|
107
|
+
|
|
108
|
+
### Update a single component (targeted)
|
|
109
|
+
|
|
110
|
+
If you only want to refresh one component, use `add` with `--force`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx phpxui add Alert --force
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🔧 CLI Usage and Options
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
phpxui <command> [--all] [--force] <component…>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Commands
|
|
125
|
+
|
|
126
|
+
| Command | Purpose |
|
|
127
|
+
| -------- | ---------------------------------------------------------------------------- |
|
|
128
|
+
| `add` | Generate one or more components by name, or the full catalogue with `--all`. |
|
|
129
|
+
| `update` | Update **all installed** components found in `outputDir` (overwrites). |
|
|
130
|
+
|
|
131
|
+
### Flags / Arguments
|
|
132
|
+
|
|
133
|
+
| Flag / Argument | Description |
|
|
134
|
+
| --------------- | ---------------------------------------------------------------------------- |
|
|
135
|
+
| `<component …>` | One or more component names separated by space (or comma). Applies to `add`. |
|
|
136
|
+
| `--all` | Download the full catalogue in one request. Applies to `add`. |
|
|
137
|
+
| `--force` | Overwrite existing files. Applies to `add`. |
|
|
138
|
+
|
|
139
|
+
> **Note:** `phpxui` installs the required baseline `ppicons` automatically. Install any extra icons separately with `npx ppicons add <icon-name>`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🧩 Configuration (phpxui.json)
|
|
144
|
+
|
|
145
|
+
On first run, **phpxui‑cli** creates a `phpxui.json` in your project root. The most important fields are:
|
|
146
|
+
|
|
147
|
+
- `outputDir`: where PHPXUI components are written (default: `src/Lib/PHPXUI`)
|
|
148
|
+
- `psr4`: mapping hints for components and icons
|
|
149
|
+
- `tailwind.css`: where the base CSS should live (default: `src/app/globals.css`)
|
|
150
|
+
- `manifest`: auto-generated AI metadata and installed component inventory maintained by the CLI
|
|
151
|
+
|
|
152
|
+
This file is separate from the Prisma PHP framework config in `prisma-php.json` at the project root.
|
|
153
|
+
|
|
154
|
+
Example:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"outputDir": "src/Lib/PHPXUI",
|
|
159
|
+
"tailwind": {
|
|
160
|
+
"css": "src/app/globals.css",
|
|
161
|
+
"baseColor": "neutral",
|
|
162
|
+
"cssVariables": true,
|
|
163
|
+
"prefix": ""
|
|
164
|
+
},
|
|
165
|
+
"psr4": {
|
|
166
|
+
"Components": "src/Lib/PHPXUI/",
|
|
167
|
+
"Icons": "src/Lib/PPIcons/"
|
|
168
|
+
},
|
|
169
|
+
"iconLibrary": "ppicons"
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🤖 AI Context
|
|
86
176
|
|
|
87
|
-
|
|
88
|
-
| --------------- | -------------------------------------------------------- |
|
|
89
|
-
| `<component …>` | One or more component names separated by space or comma. |
|
|
90
|
-
| `--all` | Download the full catalogue in one request. |
|
|
91
|
-
| `--force` | Overwrite existing files. |
|
|
177
|
+
After `add` or `update`, **phpxui-cli** refreshes these files in your project root:
|
|
92
178
|
|
|
93
|
-
|
|
179
|
+
- `phpxui.json` (updates the `manifest` section)
|
|
180
|
+
- `AGENTS.md`
|
|
181
|
+
- `.github/copilot-instructions.md`
|
|
182
|
+
|
|
183
|
+
The markdown files receive a managed `phpxui` block that inventories installed components, records the component catalogue endpoints, and preserves any manual content outside that block.
|
|
94
184
|
|
|
95
185
|
---
|
|
96
186
|
|
|
97
|
-
## 🎨
|
|
187
|
+
## 🎨 Using Additional Icons
|
|
188
|
+
|
|
189
|
+
`phpxui add ...` and `phpxui update` already install the baseline icon set used across PHPXUI components.
|
|
98
190
|
|
|
99
191
|
Need more icons? Use the **PPIcons** CLI directly:
|
|
100
192
|
|
|
@@ -102,35 +194,35 @@ Need more icons? Use the **PPIcons** CLI directly:
|
|
|
102
194
|
npx ppicons add menu chevron-left arrow-right
|
|
103
195
|
```
|
|
104
196
|
|
|
105
|
-
This will place the requested icons under `src/Lib/PPIcons` with full PHPXUI typings.
|
|
106
|
-
Browse the complete icon catalogue and usage docs at **
|
|
197
|
+
This will place the requested icons under `src/Lib/PPIcons` with full PHPXUI typings.
|
|
198
|
+
Browse the complete icon catalogue and usage docs at **https://ppicons.tsnc.tech/**.
|
|
107
199
|
|
|
108
200
|
---
|
|
109
201
|
|
|
110
|
-
## 📚
|
|
202
|
+
## 📚 Documentation
|
|
111
203
|
|
|
112
|
-
Full guides and examples live at the
|
|
204
|
+
Full guides and examples live at the PHPXUI documentation site: [https://phpxui.tsnc.tech/](https://phpxui.tsnc.tech/)
|
|
113
205
|
|
|
114
206
|
---
|
|
115
207
|
|
|
116
|
-
## 💡
|
|
208
|
+
## 💡 Contributing
|
|
117
209
|
|
|
118
210
|
We welcome contributions to improve **phpxui‑cli**. If you have ideas, find bugs, or want to add features, open an issue or submit a pull request.
|
|
119
211
|
|
|
120
212
|
---
|
|
121
213
|
|
|
122
|
-
## 📄
|
|
214
|
+
## 📄 License
|
|
123
215
|
|
|
124
216
|
`phpxui‑cli` is released under the MIT License. See `LICENSE` for details.
|
|
125
217
|
|
|
126
218
|
---
|
|
127
219
|
|
|
128
|
-
## 👤
|
|
220
|
+
## 👤 Author
|
|
129
221
|
|
|
130
|
-
This project is developed and maintained by **The
|
|
222
|
+
This project is developed and maintained by **The Steel Ninja Code**, continuously pushing the boundaries of PHP development.
|
|
131
223
|
|
|
132
224
|
---
|
|
133
225
|
|
|
134
|
-
## 📧
|
|
226
|
+
## 📧 Contact
|
|
135
227
|
|
|
136
228
|
Questions or feedback? Reach us at [thesteelninjacode@gmail.com](mailto:thesteelninjacode@gmail.com) — we’d love to hear from you!
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile}from"node:fs/promises";import prompts from"prompts";import chalk from"chalk";import path from"path";import{kebabCase}from"change-case";import{installIcons}from"./commands/icons.js";import{generateComponent}from"./generators/php-component.js";import{generateAllComponents}from"./generators/php-components-bulk.js";import{ensurePackageInstalled}from"./generators/ensure-package.js";import{copyTailwindCss}from"./generators/copy-tailwind.js";import{loadPhpXUIConfig}from"./utils/load-config.js";import{refreshPhpXUIProjectContext}from"./utils/phpxui-ai-context.js";import{getInstalledComponents}from"./utils/scan-installed.js";const PPIconsPattern=/\\PPIcons\\([A-Za-z][A-Za-z0-9]*)\b/g;export async function collectComponentIcons(e,o){const t=new Set;for(const n of e){const e=path.isAbsolute(n)?n:path.resolve(o,n),a=await readFile(e,"utf8");for(const e of a.matchAll(PPIconsPattern))t.add(kebabCase(e[1]))}return[...t].sort((e,o)=>e.localeCompare(o))}const CORE_COMPONENTS=["Slot","Portal"],defaultDependencies={prompts,generateComponent,generateAllComponents,ensurePackageInstalled,copyTailwindCss,loadPhpXUIConfig,getInstalledComponents,collectComponentIcons,installIcons,refreshAiContext:refreshPhpXUIProjectContext,logger:console};export async function runCli(e=process.argv.slice(2),o={}){const t={...defaultDependencies,...o},[n,...a]=e;if("add"!==n&&"update"!==n)return t.logger.log(chalk.blue("Usage: phpxui <add|update> [--all] [--force] <component…>")),0;const s={all:!1,force:!1},r=[];for(let e=0;e<a.length;e++){const o=a[e];switch(o){case"--all":s.all=!0;break;case"--force":s.force=!0;break;default:r.push(o)}}const l=process.cwd(),{config:c,isFirstRun:p}=t.loadPhpXUIConfig();t.ensurePackageInstalled("tw-animate-css");if(t.copyTailwindCss(p)){const e=path.relative(l,"src/app/globals.css").replace(/\\/g,"/");t.logger.log(chalk.green(p?`✔ Installed base Tailwind CSS → ${e}`:`✔ Added Tailwind CSS (missing) → ${e}`))}const i=path.resolve(l,c.outputDir||"src/Lib/PHPXUI"),g=()=>t.refreshAiContext({projectRoot:l,targetDir:i,config:c}),m=async e=>{if(0===e.length)return;const o=await t.collectComponentIcons(e,l);await t.installIcons(o)};try{if("update"===n){t.logger.log(chalk.blue(`🔎 Scanning for installed components in ${c.outputDir}...`));const e=t.getInstalledComponents(i);if(0===e.length)return await g(),t.logger.log(chalk.yellow("⚠ No components found to update.")),0;t.logger.log(chalk.blue(`✨ Found ${e.length} components. Updating...`));const o=[],n=[],a=[];for(const s of e)try{const e=await t.generateComponent(s,i,!0),n=Array.isArray(e)?e:[e];a.push(...n),n.forEach(e=>o.push(path.basename(e))),t.logger.log(chalk.green(`✔ Updated ${s}`))}catch(e){n.push(`${s}: ${e.message}`),t.logger.log(chalk.red(`✖ Failed to update ${s}`))}return await m(a),await g(),t.logger.log(chalk.green(`\n✔ Successfully updated ${o.length} components.`)),n.length>0&&(t.logger.log(chalk.red(`✖ ${n.length} failures:`)),n.forEach(e=>t.logger.log(` - ${e}`))),0}const e=p||s.force;if(s.all){const{ok:o,fail:n}=await t.generateAllComponents(i,e);return await m(o),await g(),n.length?1:0}if(0===r.length){const e=(await t.prompts({type:"text",name:"componentList",message:"Which components do you want to add? (space- or comma-separated)",validate:e=>!!e.trim()||"Enter at least one name"})).componentList;if(!e)return 0;r.push(...e.split(/[\s,]+/))}if(p&&!s.all)for(const e of CORE_COMPONENTS){r.some(o=>o.toLowerCase()===e.toLowerCase())||r.unshift(e)}const o=[];for(const n of r){const a=await t.generateComponent(n,i,e),s=Array.isArray(a)?a:[a];o.push(...s);for(const e of s){const o=path.relative(process.cwd(),e).replace(/\\/g,"/");t.logger.log(chalk.green(`✔ ${n} → ${o}`))}}return await m(o),await g(),0}catch(e){return t.logger.error(chalk.red("✖ Error:"),e.message),1}}
|
package/dist/commands/icons.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execSync}from"child_process";import chalk from"chalk";
|
|
1
|
+
import{execSync}from"child_process";import chalk from"chalk";export const REQUIRED_PHPXUI_ICONS=["chevron-down","x","chevron-right","ellipsis","chevron-left","arrow-left","arrow-right","check","chevrons-up-down","search","circle","calendar","minus","chevron-up","panel-left"];export function resolvePhpXUIIcons(o){const n=[],e=new Set;for(const c of[...REQUIRED_PHPXUI_ICONS,...o]){const o=c.trim().toLowerCase();o&&!e.has(o)&&(e.add(o),n.push(o))}return n}export async function installIcons(o){const n=resolvePhpXUIIcons(o);try{console.log(chalk.blue("📦 Installing ppicons CLI...")),execSync("npm install -g ppicons",{stdio:"inherit"});const o=n.join(" ");console.log(chalk.blue(`✨ Installing required icons: ${o}`)),execSync(`npx ppicons add ${o}`,{stdio:"inherit"}),console.log(chalk.green("✔ Icons installed in src/Lib/PPIcons"))}catch(o){console.error(chalk.red("✖ Failed to install icons:"),o.message),process.exit(1)}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import fetch from"node-fetch";import{writeComponent}from"./write-component.js";import path from"path";const SINGLE_URL="https://phpxui.tsnc.tech/cli";export async function generateComponent(t,
|
|
1
|
+
import fetch from"node-fetch";import{writeComponent}from"./write-component.js";import{parsePhpXUIComponentPayload}from"./phpxui-component-payload.js";import path from"path";const SINGLE_URL="https://phpxui.tsnc.tech/cli";export async function generateComponent(o,t,n=!1){const e=`${SINGLE_URL}?component=${encodeURIComponent(o)}`,p=await fetch(e);if(!p.ok)throw new Error(`Could not fetch "${o}": ${p.status} – ${e}`);const r=parsePhpXUIComponentPayload(await p.json(),`Component "${o}"`),a=await writeComponent(r,t,n);return[path.relative(process.cwd(),a)]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import fetch from"node-fetch";import{writeComponent}from"./write-component.js";const BULK_URL="https://phpxui.tsnc.tech/cli?component=all";export async function generateAllComponents(t,
|
|
1
|
+
import fetch from"node-fetch";import{parsePhpXUIComponentList}from"./phpxui-component-payload.js";import{writeComponent}from"./write-component.js";const BULK_URL="https://phpxui.tsnc.tech/cli?component=all";export async function generateAllComponents(t,o=!1){const e=await fetch(BULK_URL);if(!e.ok)throw new Error(`Could not fetch component list: ${e.status} – ${BULK_URL}`);const n=parsePhpXUIComponentList(await e.json(),"PHPXUI component catalog");console.log(`➡ Received ${n.length} components. Generating…`);const s=[],p=[];let c=[];for(const e of n){const n=writeComponent(e,t,o).then(t=>{s.push(t)}).catch(t=>{p.push(`${e.name}: ${t.message}`)});c.push(n),c.length>=10&&(await Promise.allSettled(c),c=[])}return await Promise.allSettled(c),{ok:s,fail:p}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const COMPONENT_NAME_PATTERN=/^[A-Za-z][A-Za-z0-9]*$/;function isRecord(n){return"object"==typeof n&&null!==n&&!Array.isArray(n)}export function normalizePhpXUIComponentName(n){const r=n.trim();if(!COMPONENT_NAME_PATTERN.test(r))throw new Error(`Invalid component name "${n}" returned by API.`);return r}export function parsePhpXUIComponentPayload(n,r){if(!isRecord(n))throw new Error(`${r} returned an invalid component payload.`);if("string"!=typeof n.name)throw new Error(`${r} returned an invalid component name.`);if("string"!=typeof n.content)throw new Error(`${r} returned invalid component content.`);return{name:normalizePhpXUIComponentName(n.name),content:n.content}}export function parsePhpXUIComponentList(n,r){if(!Array.isArray(n))throw new Error(`${r} returned an invalid component list.`);return n.map((n,e)=>parsePhpXUIComponentPayload(n,`${r} item ${e+1}`))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import fs from"fs-extra";import path from"path";export async function writeComponent(t,
|
|
1
|
+
import fs from"fs-extra";import path from"path";import{normalizePhpXUIComponentName}from"./phpxui-component-payload.js";export async function writeComponent(t,e,o=!1){const a=normalizePhpXUIComponentName(t.name),r=path.resolve(e),p=path.resolve(r,`${a}.php`),n=path.relative(r,p);if(n.startsWith("..")||path.isAbsolute(n))throw new Error(`Resolved component path escaped target directory for "${a}".`);return!o&&await fs.pathExists(p)||(await fs.ensureDir(path.dirname(p)),await fs.writeFile(p,t.content,"utf8")),p}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import{runCli}from"./cli.js";(async()=>{const s=await runCli(process.argv.slice(2));process.exit(s)})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import fs from"fs";import path from"path";const defaultConfig={style:"new-york",force:!1,outputDir:"src/Lib/PHPXUI",
|
|
1
|
+
import fs from"fs";import path from"path";const defaultConfig={style:"new-york",force:!1,outputDir:"src/Lib/PHPXUI",tailwind:{css:"src/app/globals.css",baseColor:"neutral",cssVariables:!0,prefix:""}};function normalizePhpXUIConfig(i){return{style:i.style??defaultConfig.style,force:i.force??defaultConfig.force,outputDir:i.outputDir??defaultConfig.outputDir,tailwind:{...defaultConfig.tailwind,...i.tailwind},...void 0!==i.manifest?{manifest:i.manifest}:{}}}export function loadPhpXUIConfig(){const i=path.resolve("phpxui.json"),t=fs.existsSync(i);t||(fs.writeFileSync(i,JSON.stringify(defaultConfig,null,2)),console.log("📦 Created default phpxui.json"));const o=fs.readFileSync(i,"utf-8");return{config:normalizePhpXUIConfig(JSON.parse(o)),isFirstRun:!t}}export function savePhpXUIConfig(i){const t=path.resolve("phpxui.json");fs.writeFileSync(t,JSON.stringify(normalizePhpXUIConfig(i),null,2))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import fs from"fs-extra";import path from"node:path";import{PHPXUI_CONFIG_FILE,PHPXUI_INSTRUCTIONS_FILE,PHPXUI_MANIFEST_KEY,buildPhpXUIManifest,writePhpXUIManifestToConfig}from"./phpxui-manifest.js";const START_MARKER="\x3c!-- phpxui:start --\x3e",END_MARKER="\x3c!-- phpxui:end --\x3e";function getPhpXUIInstructionsPath(e){return path.join(e,...PHPXUI_INSTRUCTIONS_FILE.split("/"))}function escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function buildApplyTo(e){return[PHPXUI_CONFIG_FILE,e.project.hostFrameworkConfigFile,e.project.instructionsFile,`${e.project.componentsDirectory}/**`,e.tailwind.css].join(", ")}function buildInstructionScaffold(e){return["---",'description: "Use when working with PHPXUI, PHPXUI CLI-managed Prisma PHP components, phpxui.json manifest metadata, component installation, component updates, or generated component imports."','name: "PHPXUI AI Context"',`applyTo: "${buildApplyTo(e)}"`,"---","","# PHPXUI Instructions","","Keep PHPXUI-specific AI guidance here instead of in `.github/copilot-instructions.md` or `AGENTS.md`.","",`- Use \`${PHPXUI_CONFIG_FILE}\` under the \`${PHPXUI_MANIFEST_KEY}\` key as the source of truth for installed component inventory, configured paths, and registry metadata.`,"- Prefer `phpxui add ...` or `phpxui update` for registry-backed components instead of hand-writing generated PHPXUI classes.",`- Preserve manual content outside the managed PHPXUI block because refreshes replace only the \`${START_MARKER}\` ... \`${END_MARKER}\` section.`].join("\n")}function getPhpUsageExample(e){return["```php","<?php","",`use ${e}\\Alert;`,"","?>","","<Alert>"," <div>Saved changes</div>","</Alert>","```"].join("\n")}function getCatalogResponseExample(){return["```json","{",' "name": "Button",',' "content": "<?php\\n\\nnamespace Lib\\\\PHPXUI;\\n\\nuse PP\\\\PHPX\\\\PHPX;\\nuse Lib\\\\PHPXUI\\\\Slot;\\n\\nclass Button extends PHPX\\n{\\n ...generated PHP source...\\n}"',"}","```"].join("\n")}function buildManagedBlockContent(e){const n=e.components.length>0?e.components.map(e=>`- ${e.name}: \`${e.file}\``):["- No PHPXUI components are installed yet."];return["# PHPXUI AI Context","","This project uses `phpxui` to generate reusable PHPXUI components for Prisma PHP.","",`- Host framework: ${e.project.hostFramework}`,`- Prisma PHP root config: ${e.project.hostFrameworkConfigFile}`,`- PHPXUI instructions file: ${e.project.instructionsFile}`,`- Components directory: ${e.project.componentsDirectory}`,`- Installed component count: ${e.components.length}`,`- Component namespace: ${e.usage.entry}`,`- Tailwind CSS file: ${e.tailwind.css}`,`- Installed component inventory and project metadata: \`${PHPXUI_CONFIG_FILE}\` (under \`${PHPXUI_MANIFEST_KEY}\`)`,"","## Installing Components","","When a requested UI component does not exist yet, install it with `phpxui` instead of hand-writing generated PHPXUI classes.","",`- Add one component: \`${e.commands.addOne}\``,`- Add multiple components: \`${e.commands.addMany}\``,`- Add the full catalogue: \`${e.commands.addAll}\``,`- Refresh installed components: \`${e.commands.updateInstalled}\``,"","## Discovering Available Components","","Use the PHPXUI catalogue API to find component names before installing them.","",`- Fetch all available components: \`${e.catalogApi.listAll.method} ${e.catalogApi.listAll.url}\``,`- Fetch one component by name: \`${e.catalogApi.getOne.method} ${e.catalogApi.getOne.exampleUrl}\``,"- The `component=all` endpoint returns a JSON array of component objects.","- The single-component endpoint returns one JSON object with `name` and `content` fields.","","Single component response example:","",getCatalogResponseExample(),"","## Installed Components","",...n,"","## Using Installed Components","",`Import generated components from the \`${e.usage.entry}\` namespace and render them as PHPX tags.`,"",getPhpUsageExample(e.usage.entry),"","## Notes","",`- Reuse installed components from \`${PHPXUI_CONFIG_FILE}\` before generating new ones.`,`- Generated component files follow this pattern: \`${e.usage.filePattern}\``,"- Manual content outside this managed block is preserved."].join("\n")}function mergeManagedBlock(e,n){const t=e.replace(/\r\n/g,"\n"),o=new RegExp(`${escapeRegExp(START_MARKER)}[\\s\\S]*?${escapeRegExp(END_MARKER)}`);return o.test(t)?t.replace(o,n).trimEnd()+"\n":0===t.trim().length?`${n}\n`:`${t.trimEnd()}\n\n${n}\n`}async function writePhpXUIInstructionFile(e,n){const t=[START_MARKER,buildManagedBlockContent(e),END_MARKER].join("\n");await fs.ensureDir(path.dirname(n));const o=await fs.pathExists(n)?await fs.readFile(n,"utf8"):null,s=null===o||0===o.trim().length?`${buildInstructionScaffold(e)}\n\n${t}\n`:mergeManagedBlock(o,t);return await fs.writeFile(n,s,"utf8"),n}async function writePhpXUIWorkspaceInstructions(e,n){return writePhpXUIInstructionFile(e,getPhpXUIInstructionsPath(n))}export async function writePhpXUIInstructions(e){return writePhpXUIWorkspaceInstructions(await buildPhpXUIManifest(e),e.projectRoot)}export async function writePhpXUICopilotInstructions(e){return writePhpXUIInstructions(e)}export async function writePhpXUIAgentsInstructions(e){return writePhpXUIInstructions(e)}export async function refreshPhpXUIProjectContext(e){const n=await buildPhpXUIManifest(e),t=await writePhpXUIManifestToConfig({projectRoot:e.projectRoot,config:e.config,manifest:n});return await writePhpXUIWorkspaceInstructions(n,e.projectRoot),t}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import fs from"fs-extra";import path from"node:path";import{getInstalledComponents}from"./scan-installed.js";export const PHPXUI_CONFIG_FILE="phpxui.json";export const PHPXUI_MANIFEST_KEY="manifest";export const PRISMA_PHP_CONFIG_FILE="prisma-php.json";export const PHPXUI_INSTRUCTIONS_FILE=".github/instructions/phpxui.instructions.md";const LEGACY_PHPXUI_MANIFEST_FILE="phpxui-manifest.json";function toRelativePath(t,e){const n=path.relative(t,e).replace(/\\/g,"/");return n.length>0?n:"."}function toRelativeConfigPath(t,e,n){const o=e&&e.trim().length>0?e:n;return toRelativePath(t,path.isAbsolute(o)?o:path.resolve(t,o)).replace(/\/+$/,"")||n}function inferNamespaceFromRelativePath(t){const e=t.split("/").filter(Boolean).filter(t=>"."!==t&&".."!==t);return"src"===e[0]?.toLowerCase()&&e.shift(),e.join("\\")||"App"}function getCommands(){return{addOne:"npx phpxui add <component-name>",addMany:"npx phpxui add <component-a> <component-b>",addAll:"npx phpxui add --all",updateInstalled:"npx phpxui update"}}function getCatalogApi(){return{listAll:{method:"GET",url:"https://phpxui.tsnc.tech/cli?component=all",returns:"PhpXUIComponentRecord[]",purpose:"List all available PHPXUI components that can be installed."},getOne:{method:"GET",urlTemplate:"https://phpxui.tsnc.tech/cli?component=<component-name>",exampleUrl:"https://phpxui.tsnc.tech/cli?component=Button",returns:"PhpXUIComponentRecord",purpose:"Fetch one component by name before installing or refreshing it."},responseFields:{name:"string",content:"string"}}}function getUsage(t,e){const n=toRelativePath(t,e);return{componentType:"class",entryStyle:"namespace",entry:inferNamespaceFromRelativePath(n),filePattern:`${n}/<ComponentName>.php`,syntax:"PHPX component tags"}}async function collectInstalledComponents(t,e){return await fs.pathExists(e)?getInstalledComponents(e).sort((t,e)=>t.localeCompare(e)).map(n=>({name:n,file:toRelativePath(t,path.join(e,`${n}.php`))})):[]}async function readConfigDocument(t,e){const n=path.join(t,"phpxui.json");return await fs.pathExists(n)?fs.readJson(n):{...e}}async function removeLegacyManifestFile(t){const e=path.join(t,"phpxui-manifest.json");await fs.pathExists(e)&&await fs.remove(e)}export async function writePhpXUIManifestToConfig({projectRoot:t,config:e,manifest:n}){const o=path.join(t,"phpxui.json"),a=await readConfigDocument(t,e);return await fs.writeJson(o,{...a,[PHPXUI_MANIFEST_KEY]:n},{spaces:2}),await removeLegacyManifestFile(t),o}export async function writePhpXUIManifest({projectRoot:t,targetDir:e,config:n}){return writePhpXUIManifestToConfig({projectRoot:t,config:n,manifest:await buildPhpXUIManifest({projectRoot:t,targetDir:e,config:n})})}export async function buildPhpXUIManifest({projectRoot:t,targetDir:e,config:n}){const o=await collectInstalledComponents(t,e),a=toRelativePath(t,e),i=toRelativeConfigPath(t,n.tailwind?.css,"src/app/globals.css");return{schemaVersion:1,generatedAt:(new Date).toISOString(),project:{framework:"phpxui",hostFramework:"prisma-php",rootDirectory:".",sourceDirectory:"src",hostFrameworkConfigFile:"prisma-php.json",configFile:"phpxui.json",componentsDirectory:a,instructionsFile:PHPXUI_INSTRUCTIONS_FILE},commands:getCommands(),catalogApi:getCatalogApi(),usage:getUsage(t,e),tailwind:{css:i},components:o}}
|