phpxui 0.1.4 → 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 CHANGED
@@ -7,15 +7,18 @@
7
7
 
8
8
  ## ✨ Features
9
9
 
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
- | **Automatic icons** | Core **PPIcons** (`x`, `chevron‑down`, `chevron‑right`, etc.) are installed on the very first run. |
18
- | **Tailwind bootstrap** | Ensures `tw-animate-css` and (on first run / missing file) writes `src/app/globals.css` for a sensible baseline. |
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.
19
22
 
20
23
  ---
21
24
 
@@ -49,10 +52,6 @@ npx phpxui add --all
49
52
  CLI output example:
50
53
 
51
54
  ```bash
52
- 📦 Installing ppicons CLI…
53
- ✨ Installing default icons: x chevron-down chevron-right
54
- ✔ Icons installed in src/Lib/PPIcons
55
-
56
55
  ✔ Alert → src/Lib/PHPXUI/Alert.php
57
56
  ✔ Dialog → src/Lib/PHPXUI/Dialog.php
58
57
  ✔ Badge → src/Lib/PHPXUI/Badge.php
@@ -137,7 +136,7 @@ phpxui <command> [--all] [--force] <component…>
137
136
  | `--all` | Download the full catalogue in one request. Applies to `add`. |
138
137
  | `--force` | Overwrite existing files. Applies to `add`. |
139
138
 
140
- > **Note:** The CLI automatically installs a default set of core icons (such as `x`, `chevron-down`, `chevron-right`) on first use. Extra icons are not yet selectable via `phpxui` directly.
139
+ > **Note:** `phpxui` installs the required baseline `ppicons` automatically. Install any extra icons separately with `npx ppicons add <icon-name>`.
141
140
 
142
141
  ---
143
142
 
@@ -147,15 +146,16 @@ On first run, **phpxui‑cli** creates a `phpxui.json` in your project root. The
147
146
 
148
147
  - `outputDir`: where PHPXUI components are written (default: `src/Lib/PHPXUI`)
149
148
  - `psr4`: mapping hints for components and icons
150
- - `iconsInstalled`: internal flag to avoid reinstalling the default icon set
151
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.
152
153
 
153
154
  Example:
154
155
 
155
156
  ```json
156
157
  {
157
158
  "outputDir": "src/Lib/PHPXUI",
158
- "iconsInstalled": false,
159
159
  "tailwind": {
160
160
  "css": "src/app/globals.css",
161
161
  "baseColor": "neutral",
@@ -172,8 +172,22 @@ Example:
172
172
 
173
173
  ---
174
174
 
175
+ ## 🤖 AI Context
176
+
177
+ After `add` or `update`, **phpxui-cli** refreshes these files in your project root:
178
+
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.
184
+
185
+ ---
186
+
175
187
  ## 🎨 Using Additional Icons
176
188
 
189
+ `phpxui add ...` and `phpxui update` already install the baseline icon set used across PHPXUI components.
190
+
177
191
  Need more icons? Use the **PPIcons** CLI directly:
178
192
 
179
193
  ```bash
@@ -187,7 +201,7 @@ Browse the complete icon catalogue and usage docs at **https://ppicons.tsnc.tech
187
201
 
188
202
  ## 📚 Documentation
189
203
 
190
- Full guides and examples live at the PHPXUI documentation site: **https://phpxui.tsnc.tech/**
204
+ Full guides and examples live at the PHPXUI documentation site: [https://phpxui.tsnc.tech/](https://phpxui.tsnc.tech/)
191
205
 
192
206
  ---
193
207
 
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}}
@@ -1 +1 @@
1
- import{execSync}from"child_process";import chalk from"chalk";import{savePhpXUIConfig}from"../utils/load-config.js";export async function installIcons(o,n){try{console.log(chalk.blue("📦 Installing ppicons CLI...")),execSync("npm install -g ppicons",{stdio:"inherit"});const c=o.join(" ");console.log(chalk.blue(`✨ Installing default icons: ${c}`)),execSync(`npx ppicons add ${c}`,{stdio:"inherit"}),console.log(chalk.green("✔ Icons installed in src/Lib/PPIcons")),n.iconsInstalled=!0,savePhpXUIConfig(n)}catch(o){console.error(chalk.red("✖ Failed to install icons:"),o.message),process.exit(1)}}
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,o,n=!1){const e=`${SINGLE_URL}?component=${encodeURIComponent(t)}`,r=await fetch(e);if(!r.ok)throw new Error(`Could not fetch "${t}": ${r.status} – ${e}`);const c=await r.json(),p=Array.isArray(c)?c:[c],s=[];for(const t of p){const e=await writeComponent(t,o,n);s.push(path.relative(process.cwd(),e))}return s}
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,e=!1){const o=await fetch(BULK_URL);if(!o.ok)throw new Error(`Could not fetch component list: ${o.status} – ${BULK_URL}`);const n=await o.json();console.log(`➡ Received ${n.length} components. Generating…`);const s=[],c=[];let a=[];for(const o of n){const n=writeComponent(o,t,e).then(t=>s.push(t)).catch(t=>c.push(`${o.name}: ${t.message}`));a.push(n),a.length>=10&&(await Promise.allSettled(a),a=[])}return await Promise.allSettled(a),{ok:s,fail:c}}
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,a,i=!1){const n=path.join(a,`${t.name}.php`);return!i&&await fs.pathExists(n)||(await fs.ensureDir(path.dirname(n)),await fs.writeFile(n,t.content,"utf8")),n}
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 prompts from"prompts";import chalk from"chalk";import path from"path";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{installIcons}from"./commands/icons.js";import{getInstalledComponents}from"./utils/scan-installed.js";const CORE_COMPONENTS=["Slot","Portal"];(async()=>{const o=process.argv.slice(2),[e,...s]=o;"add"!==e&&"update"!==e&&(console.log(chalk.blue("Usage: phpxui <add|update> [--all] [--force] <component…>")),process.exit(0));const t={all:!1,force:!1},n=[];for(let o=0;o<s.length;o++){const e=s[o];switch(e){case"--all":t.all=!0;break;case"--force":t.force=!0;break;default:n.push(e)}}const{config:a,isFirstRun:l}=loadPhpXUIConfig();a.iconsInstalled||await installIcons(["chevron-down","x","chevron-right","ellipsis","chevron-left","arrow-left","arrow-right","check","chevrons-up-down","search","circle","calendar","minus","chevron-up","panel-left"],a),ensurePackageInstalled("tw-animate-css");if(copyTailwindCss(l)){const o=path.relative(process.cwd(),"src/app/globals.css").replace(/\\/g,"/");console.log(chalk.green(l?`✔ Installed base Tailwind CSS → ${o}`:`✔ Added Tailwind CSS (missing) → ${o}`))}const r=path.resolve(a.outputDir||"src/Lib/PHPXUI");try{if("update"===e){console.log(chalk.blue(`🔎 Scanning for installed components in ${a.outputDir}...`));const o=getInstalledComponents(r);0===o.length&&(console.log(chalk.yellow("⚠ No components found to update.")),process.exit(0)),console.log(chalk.blue(`✨ Found ${o.length} components. Updating...`));const e=[],s=[];for(const t of o)try{const o=await generateComponent(t,r,!0);(Array.isArray(o)?o:[o]).forEach(o=>e.push(path.basename(o))),console.log(chalk.green(`✔ Updated ${t}`))}catch(o){s.push(`${t}: ${o.message}`),console.log(chalk.red(`✖ Failed to update ${t}`))}console.log(chalk.green(`\n✔ Successfully updated ${e.length} components.`)),s.length>0&&(console.log(chalk.red(`✖ ${s.length} failures:`)),s.forEach(o=>console.log(` - ${o}`))),process.exit(0)}const o=l||t.force;if(t.all){const{fail:e}=await generateAllComponents(r,o);process.exit(e.length?1:0)}if(0===n.length){const{componentList:o}=await prompts({type:"text",name:"componentList",message:"Which components do you want to add? (space- or comma-separated)",validate:o=>!!o.trim()||"Enter at least one name"});n.push(...o.split(/[\s,]+/))}if(l&&!t.all)for(const o of CORE_COMPONENTS){n.some(e=>e.toLowerCase()===o.toLowerCase())||n.unshift(o)}for(const e of n){const s=await generateComponent(e,r,o),t=Array.isArray(s)?s:[s];for(const o of t){const s=path.relative(process.cwd(),o).replace(/\\/g,"/");console.log(chalk.green(`✔ ${e} → ${s}`))}}}catch(o){console.error(chalk.red("✖ Error:"),o.message),process.exit(1)}})();
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",iconsInstalled:!1,tailwind:{css:"src/app/globals.css",baseColor:"neutral",cssVariables:!0,prefix:""},psr4:{Components:"src/Lib/PHPXUI/",Icons:"src/Lib/PPIcons/"},iconLibrary:"ppicons"};export function loadPhpXUIConfig(){const s=path.resolve("phpxui.json"),o=fs.existsSync(s);o||(fs.writeFileSync(s,JSON.stringify(defaultConfig,null,2)),console.log("📦 Created default phpxui.json"));const n=fs.readFileSync(s,"utf-8");return{config:JSON.parse(n),isFirstRun:!o}}export function savePhpXUIConfig(s){const o=path.resolve("phpxui.json");fs.writeFileSync(o,JSON.stringify(s,null,2))}
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}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phpxui",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A package for generating Prisma PHP components with a CLI interface.",
5
5
  "main": "index.js",
6
6
  "scripts": {