your-ai-workflow-firebase-os 1.2.58 → 1.2.60
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/vite.cjs.js
CHANGED
|
@@ -57,7 +57,7 @@ export default function App() {
|
|
|
57
57
|
// registerMicrocomponent('pricing', PricingContent);
|
|
58
58
|
import { registerMicrocomponent } from 'your-ai-workflow-firebase-os';
|
|
59
59
|
export { registerMicrocomponent };
|
|
60
|
-
`,s=`${r}
|
|
60
|
+
`,s=`@source "../../node_modules/your-ai-workflow-firebase-os/dist";`,c=e=>{if(e.includes(`@source`)&&e.includes(`your-ai-workflow-firebase-os`))return e;let t=e.match(/@import\s+(['"])tailwindcss\1[^;\n]*;/);if(!t||t.index===void 0)return e;let n=t.index+t[0].length;return`${e.slice(0,n)}\n${s}${e.slice(n)}`},l=`${r}
|
|
61
61
|
import { StrictMode } from 'react'
|
|
62
62
|
import { createRoot } from 'react-dom/client'
|
|
63
63
|
|
|
@@ -81,7 +81,7 @@ createRoot(document.getElementById('root')!).render(
|
|
|
81
81
|
<App />
|
|
82
82
|
</StrictMode>,
|
|
83
83
|
)
|
|
84
|
-
`,
|
|
84
|
+
`,u=`src/your-ai-workflow-firebase-os`,d=`${u}/configs`,f=`${u}/microcomponents`,p=`${u}/Home.tsx`,m=[`theme.config.ts`,`contactForm.config.ts`,`supportForm.config.ts`,`registry.ts`,`pagesRegistry.ts`,`home.config.ts`],h=e=>e.split(n.sep).join(`/`),g=e=>n.basename(e).replace(`.config.ts`,``).replace(/Tab$/,``).toLowerCase(),_=e=>n.basename(e).replace(`.config.ts`,``).replace(/Page$/,``).toLowerCase(),v=e=>n.basename(e).replace(`.config.ts`,``).replace(/Form$/,``).toLowerCase(),y=e=>{let t=n.basename(e).replace(/\.tsx$/,``).replace(/Content$/,``).replace(/([a-z0-9])([A-Z])/g,`$1_$2`).toLowerCase();return[t,t.replace(/_/g,``)]},b=e=>n.basename(e).replace(/\.config\.ts$/,``);function x(e){let t=n.basename(e);return m.includes(t)?`shipped`:/Tab\.config\.ts$/.test(t)?`tab`:/Page\.config\.ts$/.test(t)?`page`:/Form\.config\.ts$/.test(t)?`form`:`other`}var S=e=>t.existsSync(e)?t.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isFile()).map(e=>e.name).sort():[],C=e=>t.existsSync(n.join(e,u));function w(e,t){let r=h(n.relative(e,t));return r.startsWith(d+`/`)||r.startsWith(f+`/`)||r===p}function T(e){let r=[],i=n.join(e,d);for(let e of S(i)){if(!/\.tsx?$/.test(e))continue;let t=n.join(i,e),a=`${d}/${e}`,o=x(e);o===`tab`?r.push({kind:o,file:a,abs:t,name:e,id:g(e)}):o===`page`?r.push({kind:o,file:a,abs:t,name:e,id:_(e)}):o===`form`||/Form\.config\.ts$/.test(e)?r.push({kind:`form`,file:a,abs:t,name:e,id:v(e)}):r.push({kind:`other`,file:a,abs:t,name:e,id:``})}let a=n.join(e,f);for(let e of S(a))e.endsWith(`.tsx`)&&e.replace(/\.tsx$/,``)!==`registry`&&r.push({kind:`microcomponent`,file:`${f}/${e}`,abs:n.join(a,e),name:e,id:y(e)[0]});let o=n.join(e,p);return t.existsSync(o)&&r.push({kind:`other`,file:p,abs:o,name:`Home.tsx`,id:``}),r}var E=e=>e.filter(e=>e.kind!==`other`),D=[{test:e=>/Content\.tsx$/.test(e),home:f},{test:e=>/(Tab|Page|Form)\.config\.tsx?$/.test(e),home:d}],ee=new Set([`node_modules`,`.git`,`dist`,`build`]);function O(e){let r=[],i=n.join(e,`src`),a=(i,o)=>{if(!(o>6||!t.existsSync(i)))for(let s of t.readdirSync(i,{withFileTypes:!0})){let t=n.join(i,s.name),c=h(n.relative(e,t));if(s.isDirectory()){ee.has(s.name)||a(t,o+1);continue}if(!(c===`${d}/${s.name}`||c===`${f}/${s.name}`)){for(let{test:e,home:t}of D)if(e(s.name)){r.push(`${c}: files like this are only found directly in ${t}/ — move it to ${t}/${s.name}.`);break}}}};return a(i,0),r.sort()}var te=e=>{let t=n.basename(e);return D.some(e=>e.test(t))},k=e=>JSON.stringify(e);function A(e){let t=[],n=[],r=[],i=[];return E(e).forEach((e,a)=>{let o=`m${a}`;if(t.push(`import * as ${o} from ${k(h(e.abs))};`),e.kind===`tab`)n.push(`{
|
|
85
85
|
const configVal = pickConfig(${o});
|
|
86
86
|
if (configVal) {
|
|
87
87
|
const tabId = ${k(e.id)};
|
|
@@ -127,13 +127,13 @@ createRoot(document.getElementById('root')!).render(
|
|
|
127
127
|
forms[finalConfig.systemName] = finalConfig;
|
|
128
128
|
forms[finalConfig.systemName.toLowerCase()] = finalConfig;
|
|
129
129
|
}
|
|
130
|
-
}`);else if(e.kind===`microcomponent`){let[t,n]=
|
|
130
|
+
}`);else if(e.kind===`microcomponent`){let[t,n]=y(e.name);i.push(`if (${o} && ${o}.default) {
|
|
131
131
|
microcomponents[${k(t)}] = ${o}.default;
|
|
132
132
|
// Also answer to the squashed form ("mobile_apps" -> "mobileapps"): a page id
|
|
133
133
|
// derived from a file name (mobileAppsPage.config.ts -> "mobileapps") has no
|
|
134
134
|
// underscores, and the page must still find MobileAppsContent.tsx.
|
|
135
135
|
microcomponents[${k(n)}] = ${o}.default;
|
|
136
|
-
}`)}}),`// Generated by Your AI Firebase from the files in ${
|
|
136
|
+
}`)}}),`// Generated by Your AI Firebase from the files in ${u}/.
|
|
137
137
|
// Never written to disk; never edit. Add or remove a file and it updates itself.
|
|
138
138
|
${t.join(`
|
|
139
139
|
`)}
|
|
@@ -173,4 +173,4 @@ if (typeof window !== 'undefined') {
|
|
|
173
173
|
export const pagesRegistry = pages;
|
|
174
174
|
export const formsRegistry = forms;
|
|
175
175
|
export const microcomponentRegistry = microcomponents;
|
|
176
|
-
`}var j=e=>A(C(e)),M=e=>e.split(`/`).pop()||e,N=e=>/\/configs\/[^/]+$/.test(e),P=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),F=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),I=(e,t)=>{let n=RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`,`g`),r=[],i;for(;i=n.exec(e);)r.push(i[1].trim());return r},L=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},R=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),z=e=>/export\s+default\b/.test(e),B=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``),V=[`board`,`table`,`calendar`,`drive`],H={board:{call:`actions.openInlineEditor(categoryId)`,token:`openInlineEditor`},table:{call:`actions.addRow()`,token:`addRow`},calendar:{call:`actions.openAddModal()`,token:`openAddModal`},drive:{call:`the openUploadModal() prop`,token:`openUploadModal`}},ee=/(?:copy (?:this|the sentence|it) (?:in)?to|paste (?:this|it) (?:in)?to|copy for (?:your |the )?(?:ai|assistant)|ask your assistant|your assistant.{0,3}s chat|copy this to change)[^\n]{0,40}/i,te=e=>{let t=I(e,`route`);return{first:t[0],count:t.length}};function U(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(P(t))for(let e of _(M(t)))i.add(e);let a=new Map;for(let{file:t,source:n}of e){if(!N(t))continue;let e=M(t),r=y(e);if(r!==`tab`&&r!==`page`)continue;let i=F(n),o=I(i,`template`)[0],s=o===void 0?null:L(o);if(!s||!V.includes(s))continue;let c=I(i,r===`tab`?`tabName`:`pageName`)[0],l=c===void 0?null:L(c),u=[r===`tab`?m(e):h(e),...l?[B(l),B(l).replace(/_/g,``)]:[]];for(let e of u)a.set(e,s)}for(let{file:o,source:s}of e){let e=M(o),c=F(s);if(N(o)){let n=y(e);if(n===`shipped`)continue;if(n===`other`){t.push(`${o}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);continue}if(!R(c)&&!z(c)&&t.push(`${o}: export one const named ${v(e)}Config.`),n===`tab`||n===`page`){let{first:a,count:s}=te(c),l=a===void 0?null:L(a);if(s===0||l===null||!l.startsWith(`/`))t.push(`${o}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);else{let e=r.get(l);e?t.push(`${o}: route '${l}' is already used by ${e}. One page, one file, one address.`):r.set(l,o)}let u=I(c,`template`)[0],d=u===void 0?null:L(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?m(e):h(e),a=I(c,n===`tab`?`tabName`:`pageName`)[0],s=a===void 0?null:L(a),l=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``);if(![r,...s?[l(s),l(s).replace(/_/g,``)]:[]].some(e=>i.has(e))){let n=v(e).replace(/(Tab|Page)$/,``),r=(s||n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);t.push(`${o}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`)}}}if(n===`tab`){let e=I(c,`pageType`)[0],n=e===void 0?null:L(e);n!==`user`&&n!==`internal`&&n!==`admin`&&t.push(`${o}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`)}if(n===`form`){let n=g(e),r=I(c,`systemName`)[0];r!==void 0&&L(r)!==n&&t.push(`${o}: systemName must be '${n}'.`)}continue}if(P(o)){z(c)||t.push(`${o}: add export default.`);let r=_(e).map(e=>a.get(e)).find(Boolean),i=/<form\b/.test(c)||/\bonSubmit\b/.test(c);if(i&&r){let e=H[r];c.includes(e.token)||n.push(`${o}: this page is a ${r} and already has its own add and edit dialog, themed for light and dark — open it with ${e.call} instead of building a form here.`)}else i&&!/ContactPopup/.test(c)&&n.push(`${o}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`);let s=c.match(/['"`](?:mem|user|admin|pub)_notifications['"`]/);s&&t.push(`${o}: ${s[0]} is the bell's own collection and pages never touch it — delete that code and send the notification with notify({ toUid | toRole, title, body, link }) imported from 'your-ai-workflow-firebase-os'.`),/<style[\s>]/.test(c)&&t.push(`${o}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let l=c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);l&&n.push(`${o}: ${l[0]} makes the page exactly one screen tall and strands everything below it on a phone — the page is already inside a panel that scrolls, so let it be as long as it needs and drop the height and any overflow-hidden around it.`);let u=c.match(ee);u&&n.push(`${o}: "${u[0]}" is wording off the owner's admin screens — the page is for its visitors, not for the owner or for you. Delete it and any prompt or copy-this box on the page.`);let d=c.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);d&&n.push(`${o}: ${d[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`);continue}if(o===d){/<style[\s>]/.test(c)&&t.push(`${o}: a <style> tag is global CSS — while the home page is open it restyles every page of the app, the titles and the menu included — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let e=c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);e&&n.push(`${o}: ${e[0]} makes the page exactly one screen tall and strands everything below it on a phone — let the page be as long as it needs and drop the height and any overflow-hidden around it.`);let r=c.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);r&&n.push(`${o}: ${r[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`)}}return{errors:t,warnings:n}}var ne=[`--bg-color`,`--bg-secondary-color`,`--fg-color`,`--accent-color`,`--panel-bg`,`--panel-border`,`--button-bg`,`--button-fg`,`--button-hover-bg`,`--btn-sec-bg`,`--btn-sec-border`,`--btn-sec-text`,`--text-gradient`,`--glow-color`,`--notification-icon-color`,`--selection-bg`,`--selection-text`],re=(e,t)=>{let n=[],r=t.replace(/\/\*[\s\S]*?\*\//g,``);for(let[,t,i]of r.matchAll(/([^{}]+)\{([^{}]*)\}/g)){let r=t.trim(),a=ne.filter(e=>RegExp(`${e}\\s*:[^;}]*!important`).test(i));if(a.length){n.push(`${e}: \`${a[0]}\` is set with \`!important\` here, and the app writes that colour itself from the owner's theme — forcing it freezes it, so their Theme screen stops working. Delete the \`!important\` and set the colour in configs/theme.config.ts instead, in background, backgroundSecondary, foreground, accent or buttonText; every other colour is derived from those five.`);continue}let o=/(?:^|[\s;])(?:font-size|line-height|font)\s*:/.test(i);if(/\.page-title\b/.test(r)){n.push(`${e}: the rule for \`${r.replace(/\s+/g,` `)}\` styles the app's page title, which is the library's chrome and not this file's to restyle — delete the rule; a page heading you want to size is one you write yourself inside a content file.`);continue}let s=i.match(/(?:^|[\s;])font-family\s*:\s*([^;}]+)/);if(s&&!/^\s*['"]?Inter['"]?\s*(?:,|$)/i.test(s[1])){n.push(`${e}: the rule for \`${r.replace(/\s+/g,` `)}\` sets a \`font-family\`, and the app has one typeface — Inter, everywhere, with no setting for it. Delete the \`font-family\` line; the rest of the rule can stay.`);continue}let c=r.split(`,`).map(e=>e.trim()).filter(e=>/^h[1-3]$/.test(e));c.length&&o&&n.push(`${e}: this rule sets a size on \`${c.join(`, `)}\`, which changes every page title in the app — delete the size (font-weight here is fine) and size the headings you write inside your own content file instead.`)}return n};function W(e,t){return U(e.map(e=>({file:e.file,source:t(e.abs)})))}var G=i,K=`\0`+G,q=[{rel:`src/App.tsx`,source:a},{rel:`src/main.tsx`,source:s}],J=e=>`${e} is owned by Your AI Firebase; your edit was discarded.`,ie=e=>e.replace(/[?#].*$/,``),Y=e=>n.normalize(e).split(n.sep).join(`/`),X={info:e=>console.log(e),warn:e=>console.warn(e),error:e=>console.error(e)};function Z(e,r=X){let i=[];for(let{rel:a,source:o}of q){let s=n.join(e,a),c=null;try{c=t.readFileSync(s,`utf8`)}catch{c=null}c!==o&&(t.mkdirSync(n.dirname(s),{recursive:!0}),t.writeFileSync(s,o),i.push(a),c!==null&&r.warn(J(a)))}return i}function Q(e){let r=n.join(e,u,`registry.ts`);return t.existsSync(r)?!1:(t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,o),!0)}var $=(e,t)=>{let r=Y(ie(t));for(let{rel:t,source:i}of q)if(r===Y(n.join(e,t)))return i;return null};function ae(){let e=process.cwd(),r=!1,i=`build`,a=X,o=()=>{let r=W(C(e),e=>t.readFileSync(e,`utf8`)),i=n.join(e,`src`,`your-ai-workflow-firebase-os`,`theme.css`),a=t.existsSync(i)?re(`src/your-ai-workflow-firebase-os/theme.css`,t.readFileSync(i,`utf8`)):[];return{...r,errors:[...r.errors,...D(e),...a]}};return{name:`your-ai-firebase`,enforce:`pre`,config:()=>({resolve:{dedupe:[`firebase`,`react`,`react-dom`,`react-router-dom`]}}),configResolved(t){e=t.root,i=t.command,a=t.logger,r=x(e),r&&(Z(e,a),Q(e))},resolveId(e){return e===G?K:null},buildStart(){if(!r)return;let{errors:e,warnings:t}=o();for(let e of t)this.warn(e);if(e.length!==0)if(i===`build`)this.error(e[0]);else for(let t of e)a.error(t)},load(t){if(!r)return null;if(t===K){let t=C(e),{errors:n}=o();return n.length&&this.error(n[0]),A(t)}return $(e,t)},configureServer(t){if(!r)return;let i=()=>{let e=t.moduleGraph.getModuleById(K);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},a=r=>{let a=Y(r);if($(e,a)!==null){Z(e,t.config.logger);return}if(!S(e,a)&&!O(a))return;n.relative(e,a).split(n.sep).join(`/`)!==d&&i();let{errors:s,warnings:c}=o();for(let e of c)t.config.logger.warn(e);for(let e of s)t.config.logger.error(e)};t.watcher.on(`add`,a),t.watcher.on(`unlink`,a),t.watcher.on(`change`,a)}}}exports.CANONICAL_APP_TSX=a,exports.CANONICAL_MAIN_TSX=s,exports.CONFIGS_DIR=l,exports.CONSUMER_DIR=c,exports.CONSUMER_REGISTRY_TS=o,exports.MICROCOMPONENTS_DIR=u,exports.OWNED_BANNER=r,exports.RESOLVED_VIRTUAL_ID=K,exports.SHIPPED_CONFIG_FILES=f,exports.VIRTUAL_ID=G,exports.VIRTUAL_REGISTRIES_ID=i,exports.classifyConfigFile=y,exports.discover=C,exports.ensureConsumerRegistry=Q,exports.fileIdFromConfigFile=v,exports.formIdFromFile=g,exports.isConsumerRoot=x,exports.isDiscoveredPath=S,exports.microcomponentKeysFromFile=_,exports.ownedMessage=J,exports.pageIdFromFile=h,exports.registrable=w,exports.restoreOwnedFiles=Z,exports.stripComments=F,exports.tabIdFromFile=m,exports.validate=U,exports.validateDiscovered=W,exports.virtualModuleSource=j,exports.virtualModuleSourceFor=A,exports.yourAiFirebase=ae;
|
|
176
|
+
`}var j=e=>A(T(e)),M=e=>e.split(`/`).pop()||e,N=e=>/\/configs\/[^/]+$/.test(e),P=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),F=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),I=(e,t)=>{let n=RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`,`g`),r=[],i;for(;i=n.exec(e);)r.push(i[1].trim());return r},L=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},ne=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),R=e=>/export\s+default\b/.test(e),z=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``),B=[`board`,`table`,`calendar`,`drive`],re={board:{call:`actions.openInlineEditor(categoryId)`,token:`openInlineEditor`},table:{call:`actions.addRow()`,token:`addRow`},calendar:{call:`actions.openAddModal()`,token:`openAddModal`},drive:{call:`the openUploadModal() prop`,token:`openUploadModal`}},ie=/(?:copy (?:this|the sentence|it) (?:in)?to|paste (?:this|it) (?:in)?to|copy for (?:your |the )?(?:ai|assistant)|ask your assistant|your assistant.{0,3}s chat|copy this to change)[^\n]{0,40}/i,ae=e=>{let t=I(e,`route`);return{first:t[0],count:t.length}};function V(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(P(t))for(let e of y(M(t)))i.add(e);let a=new Map;for(let{file:t,source:n}of e){if(!N(t))continue;let e=M(t),r=x(e);if(r!==`tab`&&r!==`page`)continue;let i=F(n),o=I(i,`template`)[0],s=o===void 0?null:L(o);if(!s||!B.includes(s))continue;let c=I(i,r===`tab`?`tabName`:`pageName`)[0],l=c===void 0?null:L(c),u=[r===`tab`?g(e):_(e),...l?[z(l),z(l).replace(/_/g,``)]:[]];for(let e of u)a.set(e,s)}for(let{file:o,source:s}of e){let e=M(o),c=F(s);if(N(o)){let n=x(e);if(n===`shipped`)continue;if(n===`other`){t.push(`${o}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);continue}if(!ne(c)&&!R(c)&&t.push(`${o}: export one const named ${b(e)}Config.`),n===`tab`||n===`page`){let{first:a,count:s}=ae(c),l=a===void 0?null:L(a);if(s===0||l===null||!l.startsWith(`/`))t.push(`${o}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);else{let e=r.get(l);e?t.push(`${o}: route '${l}' is already used by ${e}. One page, one file, one address.`):r.set(l,o)}let u=I(c,`template`)[0],d=u===void 0?null:L(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?g(e):_(e),a=I(c,n===`tab`?`tabName`:`pageName`)[0],s=a===void 0?null:L(a),l=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``);if(![r,...s?[l(s),l(s).replace(/_/g,``)]:[]].some(e=>i.has(e))){let n=b(e).replace(/(Tab|Page)$/,``),r=(s||n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);t.push(`${o}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`)}}}if(n===`tab`){let e=I(c,`pageType`)[0],n=e===void 0?null:L(e);n!==`user`&&n!==`internal`&&n!==`admin`&&t.push(`${o}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`)}if(n===`form`){let n=v(e),r=I(c,`systemName`)[0];r!==void 0&&L(r)!==n&&t.push(`${o}: systemName must be '${n}'.`)}continue}if(P(o)){R(c)||t.push(`${o}: add export default.`);let r=y(e).map(e=>a.get(e)).find(Boolean),i=/<form\b/.test(c)||/\bonSubmit\b/.test(c);if(i&&r){let e=re[r];c.includes(e.token)||n.push(`${o}: this page is a ${r} and already has its own add and edit dialog, themed for light and dark — open it with ${e.call} instead of building a form here.`)}else i&&!/ContactPopup/.test(c)&&n.push(`${o}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`);let s=c.match(/['"`](?:mem|user|admin|pub)_notifications['"`]/);s&&t.push(`${o}: ${s[0]} is the bell's own collection and pages never touch it — delete that code and send the notification with notify({ toUid | toRole, title, body, link }) imported from 'your-ai-workflow-firebase-os'.`),/<style[\s>]/.test(c)&&t.push(`${o}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let l=c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);l&&n.push(`${o}: ${l[0]} makes the page exactly one screen tall and strands everything below it on a phone — the page is already inside a panel that scrolls, so let it be as long as it needs and drop the height and any overflow-hidden around it.`);let u=c.match(ie);u&&n.push(`${o}: "${u[0]}" is wording off the owner's admin screens — the page is for its visitors, not for the owner or for you. Delete it and any prompt or copy-this box on the page.`);let d=c.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);d&&n.push(`${o}: ${d[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`);continue}if(o===p){/<style[\s>]/.test(c)&&t.push(`${o}: a <style> tag is global CSS — while the home page is open it restyles every page of the app, the titles and the menu included — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let e=c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);e&&n.push(`${o}: ${e[0]} makes the page exactly one screen tall and strands everything below it on a phone — let the page be as long as it needs and drop the height and any overflow-hidden around it.`);let r=c.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);r&&n.push(`${o}: ${r[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`)}}return{errors:t,warnings:n}}var oe=[`--bg-color`,`--bg-secondary-color`,`--fg-color`,`--accent-color`,`--panel-bg`,`--panel-border`,`--button-bg`,`--button-fg`,`--button-hover-bg`,`--btn-sec-bg`,`--btn-sec-border`,`--btn-sec-text`,`--text-gradient`,`--glow-color`,`--notification-icon-color`,`--selection-bg`,`--selection-text`],H=(e,t)=>{let n=[],r=t.replace(/\/\*[\s\S]*?\*\//g,``);for(let[,t,i]of r.matchAll(/([^{}]+)\{([^{}]*)\}/g)){let r=t.trim(),a=oe.filter(e=>RegExp(`${e}\\s*:[^;}]*!important`).test(i));if(a.length){n.push(`${e}: \`${a[0]}\` is set with \`!important\` here, and the app writes that colour itself from the owner's theme — forcing it freezes it, so their Theme screen stops working. Delete the \`!important\` and set the colour in configs/theme.config.ts instead, in background, backgroundSecondary, foreground, accent or buttonText; every other colour is derived from those five.`);continue}let o=/(?:^|[\s;])(?:font-size|line-height|font)\s*:/.test(i);if(/\.page-title\b/.test(r)){n.push(`${e}: the rule for \`${r.replace(/\s+/g,` `)}\` styles the app's page title, which is the library's chrome and not this file's to restyle — delete the rule; a page heading you want to size is one you write yourself inside a content file.`);continue}let s=i.match(/(?:^|[\s;])font-family\s*:\s*([^;}]+)/);if(s&&!/^\s*['"]?Inter['"]?\s*(?:,|$)/i.test(s[1])){n.push(`${e}: the rule for \`${r.replace(/\s+/g,` `)}\` sets a \`font-family\`, and the app has one typeface — Inter, everywhere, with no setting for it. Delete the \`font-family\` line; the rest of the rule can stay.`);continue}let c=r.split(`,`).map(e=>e.trim()).filter(e=>/^h[1-3]$/.test(e));c.length&&o&&n.push(`${e}: this rule sets a size on \`${c.join(`, `)}\`, which changes every page title in the app — delete the size (font-weight here is fine) and size the headings you write inside your own content file instead.`)}return n};function U(e,t){return V(e.map(e=>({file:e.file,source:t(e.abs)})))}var W=i,G=`\0`+W,K=[{rel:`src/App.tsx`,source:a},{rel:`src/main.tsx`,source:l}],q=e=>`${e} is owned by Your AI Firebase; your edit was discarded.`,J=e=>e.replace(/[?#].*$/,``),Y=e=>n.normalize(e).split(n.sep).join(`/`),X={info:e=>console.log(e),warn:e=>console.warn(e),error:e=>console.error(e)};function Z(e,r=X){let i=[];for(let{rel:a,source:o}of K){let s=n.join(e,a),c=null;try{c=t.readFileSync(s,`utf8`)}catch{c=null}c!==o&&(t.mkdirSync(n.dirname(s),{recursive:!0}),t.writeFileSync(s,o),i.push(a),c!==null&&r.warn(q(a)))}return i}function Q(e){let r=n.join(e,f,`registry.ts`);return t.existsSync(r)?!1:(t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,o),!0)}var $=(e,t)=>{let r=Y(J(t));for(let{rel:t,source:i}of K)if(r===Y(n.join(e,t)))return i;return null};function se(){let e=process.cwd(),r=!1,i=`build`,a=X,o=()=>{let r=U(T(e),e=>t.readFileSync(e,`utf8`)),i=n.join(e,`src`,`your-ai-workflow-firebase-os`,`theme.css`),a=t.existsSync(i)?H(`src/your-ai-workflow-firebase-os/theme.css`,t.readFileSync(i,`utf8`)):[];return{...r,errors:[...r.errors,...O(e),...a]}};return{name:`your-ai-firebase`,enforce:`pre`,config:()=>({resolve:{dedupe:[`firebase`,`react`,`react-dom`,`react-router-dom`]}}),configResolved(t){e=t.root,i=t.command,a=t.logger,r=C(e),r&&(Z(e,a),Q(e))},resolveId(e){return e===W?G:null},buildStart(){if(!r)return;let{errors:e,warnings:t}=o();for(let e of t)this.warn(e);if(e.length!==0)if(i===`build`)this.error(e[0]);else for(let t of e)a.error(t)},load(t){if(!r)return null;if(t===G){let t=T(e),{errors:n}=o();return n.length&&this.error(n[0]),A(t)}return $(e,t)},transform(e,t){if(!r||!Y(J(t)).endsWith(`src/your-ai-workflow-firebase-os/theme.css`))return null;let n=c(e);return n===e?null:{code:n,map:null}},configureServer(t){if(!r)return;let i=()=>{let e=t.moduleGraph.getModuleById(G);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},a=r=>{let a=Y(r);if($(e,a)!==null){Z(e,t.config.logger);return}if(!w(e,a)&&!te(a))return;n.relative(e,a).split(n.sep).join(`/`)!==p&&i();let{errors:s,warnings:c}=o();for(let e of c)t.config.logger.warn(e);for(let e of s)t.config.logger.error(e)};t.watcher.on(`add`,a),t.watcher.on(`unlink`,a),t.watcher.on(`change`,a)}}}exports.CANONICAL_APP_TSX=a,exports.CANONICAL_MAIN_TSX=l,exports.CONFIGS_DIR=d,exports.CONSUMER_DIR=u,exports.CONSUMER_REGISTRY_TS=o,exports.MICROCOMPONENTS_DIR=f,exports.OWNED_BANNER=r,exports.RESOLVED_VIRTUAL_ID=G,exports.SHIPPED_CONFIG_FILES=m,exports.VIRTUAL_ID=W,exports.VIRTUAL_REGISTRIES_ID=i,exports.classifyConfigFile=x,exports.discover=T,exports.ensureConsumerRegistry=Q,exports.fileIdFromConfigFile=b,exports.formIdFromFile=v,exports.isConsumerRoot=C,exports.isDiscoveredPath=w,exports.microcomponentKeysFromFile=y,exports.ownedMessage=q,exports.pageIdFromFile=_,exports.registrable=E,exports.restoreOwnedFiles=Z,exports.stripComments=F,exports.tabIdFromFile=g,exports.validate=V,exports.validateDiscovered=U,exports.virtualModuleSource=j,exports.virtualModuleSourceFor=A,exports.yourAiFirebase=se;
|
package/dist/vite.es.js
CHANGED
|
@@ -49,7 +49,13 @@ export default function App() {
|
|
|
49
49
|
/>
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
|
-
`, a = "// Your AI Firebase — the page-content list.\n// The build finds content files automatically where the Your AI Firebase\n// Vite plugin runs; in a runtime without it (Google AI Studio) the two lines\n// per page below are the ONLY thing that loads a page's content file.\n// Add two lines for every page. Never remove another page's lines.\n//\n// Example, for the page at /pricing filled by PricingContent.tsx:\n// import PricingContent from './PricingContent';\n// registerMicrocomponent('pricing', PricingContent);\nimport { registerMicrocomponent } from 'your-ai-workflow-firebase-os';\nexport { registerMicrocomponent };\n", o =
|
|
52
|
+
`, a = "// Your AI Firebase — the page-content list.\n// The build finds content files automatically where the Your AI Firebase\n// Vite plugin runs; in a runtime without it (Google AI Studio) the two lines\n// per page below are the ONLY thing that loads a page's content file.\n// Add two lines for every page. Never remove another page's lines.\n//\n// Example, for the page at /pricing filled by PricingContent.tsx:\n// import PricingContent from './PricingContent';\n// registerMicrocomponent('pricing', PricingContent);\nimport { registerMicrocomponent } from 'your-ai-workflow-firebase-os';\nexport { registerMicrocomponent };\n", o = "@source \"../../node_modules/your-ai-workflow-firebase-os/dist\";", s = (e) => {
|
|
53
|
+
if (e.includes("@source") && e.includes("your-ai-workflow-firebase-os")) return e;
|
|
54
|
+
let t = e.match(/@import\s+(['"])tailwindcss\1[^;\n]*;/);
|
|
55
|
+
if (!t || t.index === void 0) return e;
|
|
56
|
+
let n = t.index + t[0].length;
|
|
57
|
+
return `${e.slice(0, n)}\n${o}${e.slice(n)}`;
|
|
58
|
+
}, c = `${n}
|
|
53
59
|
import { StrictMode } from 'react'
|
|
54
60
|
import { createRoot } from 'react-dom/client'
|
|
55
61
|
|
|
@@ -73,49 +79,49 @@ createRoot(document.getElementById('root')!).render(
|
|
|
73
79
|
<App />
|
|
74
80
|
</StrictMode>,
|
|
75
81
|
)
|
|
76
|
-
`,
|
|
82
|
+
`, l = "src/your-ai-workflow-firebase-os", u = `${l}/configs`, d = `${l}/microcomponents`, f = `${l}/Home.tsx`, p = [
|
|
77
83
|
"theme.config.ts",
|
|
78
84
|
"contactForm.config.ts",
|
|
79
85
|
"supportForm.config.ts",
|
|
80
86
|
"registry.ts",
|
|
81
87
|
"pagesRegistry.ts",
|
|
82
88
|
"home.config.ts"
|
|
83
|
-
],
|
|
89
|
+
], m = (e) => e.split(t.sep).join("/"), h = (e) => t.basename(e).replace(".config.ts", "").replace(/Tab$/, "").toLowerCase(), g = (e) => t.basename(e).replace(".config.ts", "").replace(/Page$/, "").toLowerCase(), _ = (e) => t.basename(e).replace(".config.ts", "").replace(/Form$/, "").toLowerCase(), v = (e) => {
|
|
84
90
|
let n = t.basename(e).replace(/\.tsx$/, "").replace(/Content$/, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
85
91
|
return [n, n.replace(/_/g, "")];
|
|
86
|
-
},
|
|
87
|
-
function
|
|
92
|
+
}, y = (e) => t.basename(e).replace(/\.config\.ts$/, "");
|
|
93
|
+
function b(e) {
|
|
88
94
|
let n = t.basename(e);
|
|
89
|
-
return
|
|
95
|
+
return p.includes(n) ? "shipped" : /Tab\.config\.ts$/.test(n) ? "tab" : /Page\.config\.ts$/.test(n) ? "page" : /Form\.config\.ts$/.test(n) ? "form" : "other";
|
|
90
96
|
}
|
|
91
|
-
var
|
|
92
|
-
function
|
|
93
|
-
let r =
|
|
94
|
-
return r.startsWith(
|
|
97
|
+
var x = (t) => e.existsSync(t) ? e.readdirSync(t, { withFileTypes: !0 }).filter((e) => e.isFile()).map((e) => e.name).sort() : [], S = (n) => e.existsSync(t.join(n, l));
|
|
98
|
+
function C(e, n) {
|
|
99
|
+
let r = m(t.relative(e, n));
|
|
100
|
+
return r.startsWith(u + "/") || r.startsWith(d + "/") || r === f;
|
|
95
101
|
}
|
|
96
|
-
function
|
|
97
|
-
let r = [], i = t.join(n,
|
|
98
|
-
for (let e of
|
|
102
|
+
function w(n) {
|
|
103
|
+
let r = [], i = t.join(n, u);
|
|
104
|
+
for (let e of x(i)) {
|
|
99
105
|
if (!/\.tsx?$/.test(e)) continue;
|
|
100
|
-
let n = t.join(i, e), a = `${
|
|
106
|
+
let n = t.join(i, e), a = `${u}/${e}`, o = b(e);
|
|
101
107
|
o === "tab" ? r.push({
|
|
102
108
|
kind: o,
|
|
103
109
|
file: a,
|
|
104
110
|
abs: n,
|
|
105
111
|
name: e,
|
|
106
|
-
id:
|
|
112
|
+
id: h(e)
|
|
107
113
|
}) : o === "page" ? r.push({
|
|
108
114
|
kind: o,
|
|
109
115
|
file: a,
|
|
110
116
|
abs: n,
|
|
111
117
|
name: e,
|
|
112
|
-
id:
|
|
118
|
+
id: g(e)
|
|
113
119
|
}) : o === "form" || /Form\.config\.ts$/.test(e) ? r.push({
|
|
114
120
|
kind: "form",
|
|
115
121
|
file: a,
|
|
116
122
|
abs: n,
|
|
117
123
|
name: e,
|
|
118
|
-
id:
|
|
124
|
+
id: _(e)
|
|
119
125
|
}) : r.push({
|
|
120
126
|
kind: "other",
|
|
121
127
|
file: a,
|
|
@@ -124,46 +130,46 @@ function S(n) {
|
|
|
124
130
|
id: ""
|
|
125
131
|
});
|
|
126
132
|
}
|
|
127
|
-
let a = t.join(n,
|
|
128
|
-
for (let e of
|
|
133
|
+
let a = t.join(n, d);
|
|
134
|
+
for (let e of x(a)) e.endsWith(".tsx") && e.replace(/\.tsx$/, "") !== "registry" && r.push({
|
|
129
135
|
kind: "microcomponent",
|
|
130
|
-
file: `${
|
|
136
|
+
file: `${d}/${e}`,
|
|
131
137
|
abs: t.join(a, e),
|
|
132
138
|
name: e,
|
|
133
|
-
id:
|
|
139
|
+
id: v(e)[0]
|
|
134
140
|
});
|
|
135
|
-
let o = t.join(n,
|
|
141
|
+
let o = t.join(n, f);
|
|
136
142
|
return e.existsSync(o) && r.push({
|
|
137
143
|
kind: "other",
|
|
138
|
-
file:
|
|
144
|
+
file: f,
|
|
139
145
|
abs: o,
|
|
140
146
|
name: "Home.tsx",
|
|
141
147
|
id: ""
|
|
142
148
|
}), r;
|
|
143
149
|
}
|
|
144
|
-
var
|
|
150
|
+
var T = (e) => e.filter((e) => e.kind !== "other"), E = [{
|
|
145
151
|
test: (e) => /Content\.tsx$/.test(e),
|
|
146
|
-
home:
|
|
152
|
+
home: d
|
|
147
153
|
}, {
|
|
148
154
|
test: (e) => /(Tab|Page|Form)\.config\.tsx?$/.test(e),
|
|
149
|
-
home:
|
|
150
|
-
}],
|
|
155
|
+
home: u
|
|
156
|
+
}], ee = new Set([
|
|
151
157
|
"node_modules",
|
|
152
158
|
".git",
|
|
153
159
|
"dist",
|
|
154
160
|
"build"
|
|
155
161
|
]);
|
|
156
|
-
function
|
|
162
|
+
function te(n) {
|
|
157
163
|
let r = [], i = t.join(n, "src"), a = (i, o) => {
|
|
158
164
|
if (!(o > 6 || !e.existsSync(i))) for (let s of e.readdirSync(i, { withFileTypes: !0 })) {
|
|
159
|
-
let e = t.join(i, s.name),
|
|
165
|
+
let e = t.join(i, s.name), c = m(t.relative(n, e));
|
|
160
166
|
if (s.isDirectory()) {
|
|
161
|
-
|
|
167
|
+
ee.has(s.name) || a(e, o + 1);
|
|
162
168
|
continue;
|
|
163
169
|
}
|
|
164
|
-
if (!(
|
|
165
|
-
for (let { test: e, home: t } of
|
|
166
|
-
r.push(`${
|
|
170
|
+
if (!(c === `${u}/${s.name}` || c === `${d}/${s.name}`)) {
|
|
171
|
+
for (let { test: e, home: t } of E) if (e(s.name)) {
|
|
172
|
+
r.push(`${c}: files like this are only found directly in ${t}/ — move it to ${t}/${s.name}.`);
|
|
167
173
|
break;
|
|
168
174
|
}
|
|
169
175
|
}
|
|
@@ -171,15 +177,15 @@ function E(n) {
|
|
|
171
177
|
};
|
|
172
178
|
return a(i, 0), r.sort();
|
|
173
179
|
}
|
|
174
|
-
var
|
|
180
|
+
var ne = (e) => {
|
|
175
181
|
let n = t.basename(e);
|
|
176
|
-
return
|
|
182
|
+
return E.some((e) => e.test(n));
|
|
177
183
|
}, D = (e) => JSON.stringify(e);
|
|
178
184
|
function O(e) {
|
|
179
185
|
let t = [], n = [], r = [], i = [];
|
|
180
|
-
return
|
|
186
|
+
return T(e).forEach((e, a) => {
|
|
181
187
|
let o = `m${a}`;
|
|
182
|
-
if (t.push(`import * as ${o} from ${D(
|
|
188
|
+
if (t.push(`import * as ${o} from ${D(m(e.abs))};`), e.kind === "tab") n.push(`{
|
|
183
189
|
const configVal = pickConfig(${o});
|
|
184
190
|
if (configVal) {
|
|
185
191
|
const tabId = ${D(e.id)};
|
|
@@ -229,7 +235,7 @@ function O(e) {
|
|
|
229
235
|
}
|
|
230
236
|
}`);
|
|
231
237
|
else if (e.kind === "microcomponent") {
|
|
232
|
-
let [t, n] =
|
|
238
|
+
let [t, n] = v(e.name);
|
|
233
239
|
i.push(`if (${o} && ${o}.default) {
|
|
234
240
|
microcomponents[${D(t)}] = ${o}.default;
|
|
235
241
|
// Also answer to the squashed form ("mobile_apps" -> "mobileapps"): a page id
|
|
@@ -238,7 +244,7 @@ function O(e) {
|
|
|
238
244
|
microcomponents[${D(n)}] = ${o}.default;
|
|
239
245
|
}`);
|
|
240
246
|
}
|
|
241
|
-
}), `// Generated by Your AI Firebase from the files in ${
|
|
247
|
+
}), `// Generated by Your AI Firebase from the files in ${l}/.
|
|
242
248
|
// Never written to disk; never edit. Add or remove a file and it updates itself.
|
|
243
249
|
${t.join("\n")}
|
|
244
250
|
|
|
@@ -276,7 +282,7 @@ export const formsRegistry = forms;
|
|
|
276
282
|
export const microcomponentRegistry = microcomponents;
|
|
277
283
|
`;
|
|
278
284
|
}
|
|
279
|
-
var k = (e) => O(
|
|
285
|
+
var k = (e) => O(w(e)), A = (e) => e.split("/").pop() || e, j = (e) => /\/configs\/[^/]+$/.test(e), M = (e) => /\/microcomponents\/[^/]+\.tsx$/.test(e), N = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\'"`])\/\/.*$/gm, "$1"), P = (e, t) => {
|
|
280
286
|
let n = RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`, "g"), r = [], i;
|
|
281
287
|
for (; i = n.exec(e);) r.push(i[1].trim());
|
|
282
288
|
return r;
|
|
@@ -305,7 +311,7 @@ var k = (e) => O(S(e)), A = (e) => e.split("/").pop() || e, j = (e) => /\/config
|
|
|
305
311
|
call: "the openUploadModal() prop",
|
|
306
312
|
token: "openUploadModal"
|
|
307
313
|
}
|
|
308
|
-
}, V = /(?:copy (?:this|the sentence|it) (?:in)?to|paste (?:this|it) (?:in)?to|copy for (?:your |the )?(?:ai|assistant)|ask your assistant|your assistant.{0,3}s chat|copy this to change)[^\n]{0,40}/i,
|
|
314
|
+
}, V = /(?:copy (?:this|the sentence|it) (?:in)?to|paste (?:this|it) (?:in)?to|copy for (?:your |the )?(?:ai|assistant)|ask your assistant|your assistant.{0,3}s chat|copy this to change)[^\n]{0,40}/i, re = (e) => {
|
|
309
315
|
let t = P(e, "route");
|
|
310
316
|
return {
|
|
311
317
|
first: t[0],
|
|
@@ -314,28 +320,28 @@ var k = (e) => O(S(e)), A = (e) => e.split("/").pop() || e, j = (e) => /\/config
|
|
|
314
320
|
};
|
|
315
321
|
function H(e) {
|
|
316
322
|
let t = [], n = [], r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Set();
|
|
317
|
-
for (let { file: t } of e) if (M(t)) for (let e of
|
|
323
|
+
for (let { file: t } of e) if (M(t)) for (let e of v(A(t))) i.add(e);
|
|
318
324
|
let a = /* @__PURE__ */ new Map();
|
|
319
325
|
for (let { file: t, source: n } of e) {
|
|
320
326
|
if (!j(t)) continue;
|
|
321
|
-
let e = A(t), r =
|
|
327
|
+
let e = A(t), r = b(e);
|
|
322
328
|
if (r !== "tab" && r !== "page") continue;
|
|
323
329
|
let i = N(n), o = P(i, "template")[0], s = o === void 0 ? null : F(o);
|
|
324
330
|
if (!s || !z.includes(s)) continue;
|
|
325
|
-
let c = P(i, r === "tab" ? "tabName" : "pageName")[0], l = c === void 0 ? null : F(c), u = [r === "tab" ?
|
|
331
|
+
let c = P(i, r === "tab" ? "tabName" : "pageName")[0], l = c === void 0 ? null : F(c), u = [r === "tab" ? h(e) : g(e), ...l ? [R(l), R(l).replace(/_/g, "")] : []];
|
|
326
332
|
for (let e of u) a.set(e, s);
|
|
327
333
|
}
|
|
328
334
|
for (let { file: o, source: s } of e) {
|
|
329
335
|
let e = A(o), c = N(s);
|
|
330
336
|
if (j(o)) {
|
|
331
|
-
let n =
|
|
337
|
+
let n = b(e);
|
|
332
338
|
if (n === "shipped") continue;
|
|
333
339
|
if (n === "other") {
|
|
334
340
|
t.push(`${o}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);
|
|
335
341
|
continue;
|
|
336
342
|
}
|
|
337
|
-
if (!I(c) && !L(c) && t.push(`${o}: export one const named ${
|
|
338
|
-
let { first: a, count: s } =
|
|
343
|
+
if (!I(c) && !L(c) && t.push(`${o}: export one const named ${y(e)}Config.`), n === "tab" || n === "page") {
|
|
344
|
+
let { first: a, count: s } = re(c), l = a === void 0 ? null : F(a);
|
|
339
345
|
if (s === 0 || l === null || !l.startsWith("/")) t.push(`${o}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);
|
|
340
346
|
else {
|
|
341
347
|
let e = r.get(l);
|
|
@@ -350,9 +356,9 @@ function H(e) {
|
|
|
350
356
|
"inline_form",
|
|
351
357
|
"popup_form"
|
|
352
358
|
].includes(d)) {
|
|
353
|
-
let r = n === "tab" ?
|
|
359
|
+
let r = n === "tab" ? h(e) : g(e), a = P(c, n === "tab" ? "tabName" : "pageName")[0], s = a === void 0 ? null : F(a), l = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
354
360
|
if (![r, ...s ? [l(s), l(s).replace(/_/g, "")] : []].some((e) => i.has(e))) {
|
|
355
|
-
let n =
|
|
361
|
+
let n = y(e).replace(/(Tab|Page)$/, ""), r = (s || n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("");
|
|
356
362
|
t.push(`${o}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`);
|
|
357
363
|
}
|
|
358
364
|
}
|
|
@@ -362,14 +368,14 @@ function H(e) {
|
|
|
362
368
|
n !== "user" && n !== "internal" && n !== "admin" && t.push(`${o}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`);
|
|
363
369
|
}
|
|
364
370
|
if (n === "form") {
|
|
365
|
-
let n =
|
|
371
|
+
let n = _(e), r = P(c, "systemName")[0];
|
|
366
372
|
r !== void 0 && F(r) !== n && t.push(`${o}: systemName must be '${n}'.`);
|
|
367
373
|
}
|
|
368
374
|
continue;
|
|
369
375
|
}
|
|
370
376
|
if (M(o)) {
|
|
371
377
|
L(c) || t.push(`${o}: add export default.`);
|
|
372
|
-
let r =
|
|
378
|
+
let r = v(e).map((e) => a.get(e)).find(Boolean), i = /<form\b/.test(c) || /\bonSubmit\b/.test(c);
|
|
373
379
|
if (i && r) {
|
|
374
380
|
let e = B[r];
|
|
375
381
|
c.includes(e.token) || n.push(`${o}: this page is a ${r} and already has its own add and edit dialog, themed for light and dark — open it with ${e.call} instead of building a form here.`);
|
|
@@ -384,7 +390,7 @@ function H(e) {
|
|
|
384
390
|
d && n.push(`${o}: ${d[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`);
|
|
385
391
|
continue;
|
|
386
392
|
}
|
|
387
|
-
if (o ===
|
|
393
|
+
if (o === f) {
|
|
388
394
|
/<style[\s>]/.test(c) && t.push(`${o}: a <style> tag is global CSS — while the home page is open it restyles every page of the app, the titles and the menu included — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);
|
|
389
395
|
let e = c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);
|
|
390
396
|
e && n.push(`${o}: ${e[0]} makes the page exactly one screen tall and strands everything below it on a phone — let the page be as long as it needs and drop the height and any overflow-hidden around it.`);
|
|
@@ -397,7 +403,7 @@ function H(e) {
|
|
|
397
403
|
warnings: n
|
|
398
404
|
};
|
|
399
405
|
}
|
|
400
|
-
var
|
|
406
|
+
var ie = [
|
|
401
407
|
"--bg-color",
|
|
402
408
|
"--bg-secondary-color",
|
|
403
409
|
"--fg-color",
|
|
@@ -415,10 +421,10 @@ var ne = [
|
|
|
415
421
|
"--notification-icon-color",
|
|
416
422
|
"--selection-bg",
|
|
417
423
|
"--selection-text"
|
|
418
|
-
],
|
|
424
|
+
], ae = (e, t) => {
|
|
419
425
|
let n = [], r = t.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
420
426
|
for (let [, t, i] of r.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
|
421
|
-
let r = t.trim(), a =
|
|
427
|
+
let r = t.trim(), a = ie.filter((e) => RegExp(`${e}\\s*:[^;}]*!important`).test(i));
|
|
422
428
|
if (a.length) {
|
|
423
429
|
n.push(`${e}: \`${a[0]}\` is set with \`!important\` here, and the app writes that colour itself from the owner's theme — forcing it freezes it, so their Theme screen stops working. Delete the \`!important\` and set the colour in configs/theme.config.ts instead, in background, backgroundSecondary, foreground, accent or buttonText; every other colour is derived from those five.`);
|
|
424
430
|
continue;
|
|
@@ -451,7 +457,7 @@ var W = r, G = "\0" + W, K = [{
|
|
|
451
457
|
source: i
|
|
452
458
|
}, {
|
|
453
459
|
rel: "src/main.tsx",
|
|
454
|
-
source:
|
|
460
|
+
source: c
|
|
455
461
|
}], q = (e) => `${e} is owned by Your AI Firebase; your edit was discarded.`, J = (e) => e.replace(/[?#].*$/, ""), Y = (e) => t.normalize(e).split(t.sep).join("/"), X = {
|
|
456
462
|
info: (e) => console.log(e),
|
|
457
463
|
warn: (e) => console.warn(e),
|
|
@@ -471,7 +477,7 @@ function Z(n, r = X) {
|
|
|
471
477
|
return i;
|
|
472
478
|
}
|
|
473
479
|
function Q(n) {
|
|
474
|
-
let r = t.join(n,
|
|
480
|
+
let r = t.join(n, d, "registry.ts");
|
|
475
481
|
return e.existsSync(r) ? !1 : (e.mkdirSync(t.dirname(r), { recursive: !0 }), e.writeFileSync(r, a), !0);
|
|
476
482
|
}
|
|
477
483
|
var $ = (e, n) => {
|
|
@@ -479,14 +485,14 @@ var $ = (e, n) => {
|
|
|
479
485
|
for (let { rel: n, source: i } of K) if (r === Y(t.join(e, n))) return i;
|
|
480
486
|
return null;
|
|
481
487
|
};
|
|
482
|
-
function
|
|
488
|
+
function oe() {
|
|
483
489
|
let n = process.cwd(), r = !1, i = "build", a = X, o = () => {
|
|
484
|
-
let r = U(
|
|
490
|
+
let r = U(w(n), (t) => e.readFileSync(t, "utf8")), i = t.join(n, "src", "your-ai-workflow-firebase-os", "theme.css"), a = e.existsSync(i) ? ae("src/your-ai-workflow-firebase-os/theme.css", e.readFileSync(i, "utf8")) : [];
|
|
485
491
|
return {
|
|
486
492
|
...r,
|
|
487
493
|
errors: [
|
|
488
494
|
...r.errors,
|
|
489
|
-
...
|
|
495
|
+
...te(n),
|
|
490
496
|
...a
|
|
491
497
|
]
|
|
492
498
|
};
|
|
@@ -501,7 +507,7 @@ function ie() {
|
|
|
501
507
|
"react-router-dom"
|
|
502
508
|
] } }),
|
|
503
509
|
configResolved(e) {
|
|
504
|
-
n = e.root, i = e.command, a = e.logger, r =
|
|
510
|
+
n = e.root, i = e.command, a = e.logger, r = S(n), r && (Z(n, a), Q(n));
|
|
505
511
|
},
|
|
506
512
|
resolveId(e) {
|
|
507
513
|
return e === W ? G : null;
|
|
@@ -516,11 +522,19 @@ function ie() {
|
|
|
516
522
|
load(e) {
|
|
517
523
|
if (!r) return null;
|
|
518
524
|
if (e === G) {
|
|
519
|
-
let e =
|
|
525
|
+
let e = w(n), { errors: t } = o();
|
|
520
526
|
return t.length && this.error(t[0]), O(e);
|
|
521
527
|
}
|
|
522
528
|
return $(n, e);
|
|
523
529
|
},
|
|
530
|
+
transform(e, t) {
|
|
531
|
+
if (!r || !Y(J(t)).endsWith("src/your-ai-workflow-firebase-os/theme.css")) return null;
|
|
532
|
+
let n = s(e);
|
|
533
|
+
return n === e ? null : {
|
|
534
|
+
code: n,
|
|
535
|
+
map: null
|
|
536
|
+
};
|
|
537
|
+
},
|
|
524
538
|
configureServer(e) {
|
|
525
539
|
if (!r) return;
|
|
526
540
|
let i = () => {
|
|
@@ -535,8 +549,8 @@ function ie() {
|
|
|
535
549
|
Z(n, e.config.logger);
|
|
536
550
|
return;
|
|
537
551
|
}
|
|
538
|
-
if (!
|
|
539
|
-
t.relative(n, a).split(t.sep).join("/") !==
|
|
552
|
+
if (!C(n, a) && !ne(a)) return;
|
|
553
|
+
t.relative(n, a).split(t.sep).join("/") !== f && i();
|
|
540
554
|
let { errors: s, warnings: c } = o();
|
|
541
555
|
for (let t of c) e.config.logger.warn(t);
|
|
542
556
|
for (let t of s) e.config.logger.error(t);
|
|
@@ -546,4 +560,4 @@ function ie() {
|
|
|
546
560
|
};
|
|
547
561
|
}
|
|
548
562
|
//#endregion
|
|
549
|
-
export { i as CANONICAL_APP_TSX,
|
|
563
|
+
export { i as CANONICAL_APP_TSX, c as CANONICAL_MAIN_TSX, u as CONFIGS_DIR, l as CONSUMER_DIR, a as CONSUMER_REGISTRY_TS, d as MICROCOMPONENTS_DIR, n as OWNED_BANNER, G as RESOLVED_VIRTUAL_ID, p as SHIPPED_CONFIG_FILES, W as VIRTUAL_ID, r as VIRTUAL_REGISTRIES_ID, b as classifyConfigFile, w as discover, Q as ensureConsumerRegistry, y as fileIdFromConfigFile, _ as formIdFromFile, S as isConsumerRoot, C as isDiscoveredPath, v as microcomponentKeysFromFile, q as ownedMessage, g as pageIdFromFile, T as registrable, Z as restoreOwnedFiles, N as stripComments, h as tabIdFromFile, H as validate, U as validateDiscovered, k as virtualModuleSource, O as virtualModuleSourceFor, oe as yourAiFirebase };
|