your-ai-workflow-firebase-os 1.2.57 → 1.2.59

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/index.d.ts CHANGED
@@ -6,6 +6,8 @@ export { useConfig } from './lib/ConfigContext';
6
6
  export type { FirebaseOSConfig } from './lib/ConfigContext';
7
7
  export { db, auth, storage } from './lib/firebase';
8
8
  export { ContactPopup } from './components/ContactPopup';
9
+ export { notify } from './lib/notify';
10
+ export type { NotifyInput } from './lib/notify';
9
11
  export { registerMicrocomponent } from './microcomponents/registry';
10
12
  export { Button } from './components/Button';
11
13
  export { Card } from './components/Card';
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,10 +81,10 @@ createRoot(document.getElementById('root')!).render(
81
81
  <App />
82
82
  </StrictMode>,
83
83
  )
84
- `,c=`src/your-ai-workflow-firebase-os`,l=`${c}/configs`,u=`${c}/microcomponents`,d=[`theme.config.ts`,`contactForm.config.ts`,`supportForm.config.ts`,`registry.ts`,`pagesRegistry.ts`,`home.config.ts`],f=e=>e.split(n.sep).join(`/`),p=e=>n.basename(e).replace(`.config.ts`,``).replace(/Tab$/,``).toLowerCase(),m=e=>n.basename(e).replace(`.config.ts`,``).replace(/Page$/,``).toLowerCase(),h=e=>n.basename(e).replace(`.config.ts`,``).replace(/Form$/,``).toLowerCase(),g=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,``)]},_=e=>n.basename(e).replace(/\.config\.ts$/,``);function v(e){let t=n.basename(e);return d.includes(t)?`shipped`:/Tab\.config\.ts$/.test(t)?`tab`:/Page\.config\.ts$/.test(t)?`page`:/Form\.config\.ts$/.test(t)?`form`:`other`}var y=e=>t.existsSync(e)?t.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isFile()).map(e=>e.name).sort():[],b=e=>t.existsSync(n.join(e,c));function x(e,t){let r=f(n.relative(e,t));return r.startsWith(l+`/`)||r.startsWith(u+`/`)}function S(e){let t=[],r=n.join(e,l);for(let e of y(r)){if(!/\.tsx?$/.test(e))continue;let i=n.join(r,e),a=`${l}/${e}`,o=v(e);o===`tab`?t.push({kind:o,file:a,abs:i,name:e,id:p(e)}):o===`page`?t.push({kind:o,file:a,abs:i,name:e,id:m(e)}):o===`form`||/Form\.config\.ts$/.test(e)?t.push({kind:`form`,file:a,abs:i,name:e,id:h(e)}):t.push({kind:`other`,file:a,abs:i,name:e,id:``})}let i=n.join(e,u);for(let e of y(i))e.endsWith(`.tsx`)&&e.replace(/\.tsx$/,``)!==`registry`&&t.push({kind:`microcomponent`,file:`${u}/${e}`,abs:n.join(i,e),name:e,id:g(e)[0]});return t}var C=e=>e.filter(e=>e.kind!==`other`),w=[{test:e=>/Content\.tsx$/.test(e),home:u},{test:e=>/(Tab|Page|Form)\.config\.tsx?$/.test(e),home:l}],ee=new Set([`node_modules`,`.git`,`dist`,`build`]);function T(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=f(n.relative(e,t));if(s.isDirectory()){ee.has(s.name)||a(t,o+1);continue}if(!(c===`${l}/${s.name}`||c===`${u}/${s.name}`)){for(let{test:e,home:t}of w)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 E=e=>{let t=n.basename(e);return w.some(e=>e.test(t))},D=e=>JSON.stringify(e);function O(e){let t=[],n=[],r=[],i=[];return C(e).forEach((e,a)=>{let o=`m${a}`;if(t.push(`import * as ${o} from ${D(f(e.abs))};`),e.kind===`tab`)n.push(`{
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
- const tabId = ${D(e.id)};
87
+ const tabId = ${k(e.id)};
88
88
  const finalTab = {
89
89
  config: {
90
90
  ...configVal,
@@ -102,7 +102,7 @@ createRoot(document.getElementById('root')!).render(
102
102
  }`);else if(e.kind===`page`)n.push(`{
103
103
  const configVal = pickConfig(${o});
104
104
  if (configVal) {
105
- const pageId = ${D(e.id)};
105
+ const pageId = ${k(e.id)};
106
106
  const finalPage = {
107
107
  config: {
108
108
  ...configVal,
@@ -120,20 +120,20 @@ createRoot(document.getElementById('root')!).render(
120
120
  }`);else if(e.kind===`form`)r.push(`{
121
121
  const configVal = pickConfig(${o});
122
122
  if (configVal) {
123
- const formId = ${D(e.id)};
123
+ const formId = ${k(e.id)};
124
124
  // The form's id is its file name; systemName always follows the file.
125
125
  const finalConfig = { ...configVal, systemName: formId };
126
126
  forms[formId] = finalConfig;
127
127
  forms[finalConfig.systemName] = finalConfig;
128
128
  forms[finalConfig.systemName.toLowerCase()] = finalConfig;
129
129
  }
130
- }`);else if(e.kind===`microcomponent`){let[t,n]=g(e.name);i.push(`if (${o} && ${o}.default) {
131
- microcomponents[${D(t)}] = ${o}.default;
130
+ }`);else if(e.kind===`microcomponent`){let[t,n]=y(e.name);i.push(`if (${o} && ${o}.default) {
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
- microcomponents[${D(n)}] = ${o}.default;
136
- }`)}}),`// Generated by Your AI Firebase from the files in ${c}/.
135
+ microcomponents[${k(n)}] = ${o}.default;
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 te=e=>O(S(e)),k=e=>e.split(`/`).pop()||e,A=e=>/\/configs\/[^/]+$/.test(e),j=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),M=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),N=(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},P=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},F=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),I=e=>/export\s+default\b/.test(e),L=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``),R=[`board`,`table`,`calendar`,`drive`],z={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`}},B=/(?: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,V=e=>{let t=N(e,`route`);return{first:t[0],count:t.length}};function H(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(j(t))for(let e of g(k(t)))i.add(e);let a=new Map;for(let{file:t,source:n}of e){if(!A(t))continue;let e=k(t),r=v(e);if(r!==`tab`&&r!==`page`)continue;let i=M(n),o=N(i,`template`)[0],s=o===void 0?null:P(o);if(!s||!R.includes(s))continue;let c=N(i,r===`tab`?`tabName`:`pageName`)[0],l=c===void 0?null:P(c),u=[r===`tab`?p(e):m(e),...l?[L(l),L(l).replace(/_/g,``)]:[]];for(let e of u)a.set(e,s)}for(let{file:o,source:s}of e){let e=k(o),c=M(s);if(A(o)){let n=v(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(!F(c)&&!I(c)&&t.push(`${o}: export one const named ${_(e)}Config.`),n===`tab`||n===`page`){let{first:a,count:s}=V(c),l=a===void 0?null:P(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=N(c,`template`)[0],d=u===void 0?null:P(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?p(e):m(e),a=N(c,n===`tab`?`tabName`:`pageName`)[0],s=a===void 0?null:P(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=_(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=N(c,`pageType`)[0],n=e===void 0?null:P(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=h(e),r=N(c,`systemName`)[0];r!==void 0&&P(r)!==n&&t.push(`${o}: systemName must be '${n}'.`)}continue}if(j(o)){I(c)||t.push(`${o}: add export default.`);let r=g(e).map(e=>a.get(e)).find(Boolean),i=/<form\b/.test(c)||/\bonSubmit\b/.test(c);if(i&&r){let e=z[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.`);/<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 s=c.match(/(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/);s&&n.push(`${o}: ${s[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 l=c.match(B);l&&n.push(`${o}: "${l[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 u=c.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);u&&n.push(`${o}: ${u[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 U=[`--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`],ne=(e,t)=>{let n=[],r=t.replace(/\/\*[\s\S]*?\*\//g,``);for(let[,t,i]of r.matchAll(/([^{}]+)\{([^{}]*)\}/g)){let r=t.trim(),a=U.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 H(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.`,re=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(re(t));for(let{rel:t,source:i}of q)if(r===Y(n.join(e,t)))return i;return null};function ie(){let e=process.cwd(),r=!1,i=`build`,a=X,o=()=>{let r=W(S(e),e=>t.readFileSync(e,`utf8`)),i=n.join(e,`src`,`your-ai-workflow-firebase-os`,`theme.css`),a=t.existsSync(i)?ne(`src/your-ai-workflow-firebase-os/theme.css`,t.readFileSync(i,`utf8`)):[];return{...r,errors:[...r.errors,...T(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=b(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=S(e),{errors:n}=o();return n.length&&this.error(n[0]),O(t)}return $(e,t)},configureServer(t){if(!r)return;let n=()=>{let e=t.moduleGraph.getModuleById(K);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},i=r=>{let i=Y(r);if($(e,i)!==null){Z(e,t.config.logger);return}if(!x(e,i)&&!E(i))return;n();let{errors:a,warnings:s}=o();for(let e of s)t.config.logger.warn(e);for(let e of a)t.config.logger.error(e)};t.watcher.on(`add`,i),t.watcher.on(`unlink`,i),t.watcher.on(`change`,i)}}}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=d,exports.VIRTUAL_ID=G,exports.VIRTUAL_REGISTRIES_ID=i,exports.classifyConfigFile=v,exports.discover=S,exports.ensureConsumerRegistry=Q,exports.fileIdFromConfigFile=_,exports.formIdFromFile=h,exports.isConsumerRoot=b,exports.isDiscoveredPath=x,exports.microcomponentKeysFromFile=g,exports.ownedMessage=J,exports.pageIdFromFile=m,exports.registrable=C,exports.restoreOwnedFiles=Z,exports.stripComments=M,exports.tabIdFromFile=p,exports.validate=H,exports.validateDiscovered=W,exports.virtualModuleSource=te,exports.virtualModuleSourceFor=O,exports.yourAiFirebase=ie;
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;