your-ai-workflow-firebase-os 1.2.39 → 1.2.41
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/ContactPopup-D7dmsL5K.cjs +9 -0
- package/dist/{ContactPopup-Bm3DZkEB.js → ContactPopup-kZk-JP-d.js} +10296 -10296
- package/dist/{PhoneInputField-ChHip6t8.js → PhoneInputField-CaSYHqEc.js} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/renderer.cjs.js +1 -1
- package/dist/renderer.es.js +1 -1
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite.cjs.js +26 -10
- package/dist/vite.es.js +127 -79
- package/dist/your-ai-workflow-firebase-os.cjs.js +13 -12
- package/dist/your-ai-workflow-firebase-os.css +1 -1
- package/dist/your-ai-workflow-firebase-os.es.js +3406 -3333
- package/package.json +1 -1
- package/scripts/postinstall.js +104 -65
- package/src/configs/theme.config.ts +4 -2
- package/src/index.css +22 -12
- package/dist/ContactPopup-DY0VLjCP.cjs +0 -9
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -68,7 +68,12 @@ const loadCanonical = async () => {
|
|
|
68
68
|
try {
|
|
69
69
|
const mod = await import(pathToFileURL(path.join(__dirname, '..', 'dist', 'vite.es.js')).href);
|
|
70
70
|
if (typeof mod.CANONICAL_APP_TSX === 'string' && typeof mod.CANONICAL_MAIN_TSX === 'string') {
|
|
71
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
app: mod.CANONICAL_APP_TSX,
|
|
73
|
+
main: mod.CANONICAL_MAIN_TSX,
|
|
74
|
+
banner: mod.OWNED_BANNER || FALLBACK_OWNED_BANNER,
|
|
75
|
+
registry: typeof mod.CONSUMER_REGISTRY_TS === 'string' ? mod.CONSUMER_REGISTRY_TS : null,
|
|
76
|
+
};
|
|
72
77
|
}
|
|
73
78
|
} catch {
|
|
74
79
|
// dist/vite.es.js missing or not loadable here — fall back to the templates below.
|
|
@@ -185,17 +190,30 @@ copyMachinery('forms/registry.ts', 'registry.ts');
|
|
|
185
190
|
copyMachinery('pages/registry.ts', 'pagesRegistry.ts');
|
|
186
191
|
console.log(' ✓ Copied configuration files and registry to your src/your-ai-workflow-firebase-os/configs directory');
|
|
187
192
|
|
|
188
|
-
//
|
|
189
|
-
|
|
193
|
+
// The page-content list: microcomponents/registry.ts. Written ONCE and never
|
|
194
|
+
// overwritten — since 1.2.41 the assistant appends two lines per page to it
|
|
195
|
+
// (the only path from a content file to its page in a runtime without the
|
|
196
|
+
// Vite plugin, like Google AI Studio), so re-copying it on install would
|
|
197
|
+
// silently un-register every page. Projects created before 1.2.41 keep their
|
|
198
|
+
// glob-based copy: under the plugin it is redundant either way.
|
|
199
|
+
const FALLBACK_CONSUMER_REGISTRY_TS = `// Your AI Firebase — the page-content list.
|
|
200
|
+
// The build finds content files automatically where the Your AI Firebase
|
|
201
|
+
// Vite plugin runs; in a runtime without it (Google AI Studio) the two lines
|
|
202
|
+
// per page below are the ONLY thing that loads a page's content file.
|
|
203
|
+
// Add two lines for every page. Never remove another page's lines.
|
|
204
|
+
//
|
|
205
|
+
// Example, for the page at /pricing filled by PricingContent.tsx:
|
|
206
|
+
// import PricingContent from './PricingContent';
|
|
207
|
+
// registerMicrocomponent('pricing', PricingContent);
|
|
208
|
+
import { registerMicrocomponent } from 'your-ai-workflow-firebase-os';
|
|
209
|
+
export { registerMicrocomponent };
|
|
210
|
+
`;
|
|
190
211
|
const microcomponentsDestDir = path.join(fbosDir, 'microcomponents');
|
|
191
|
-
|
|
212
|
+
{
|
|
192
213
|
if (!fs.existsSync(microcomponentsDestDir)) fs.mkdirSync(microcomponentsDestDir, { recursive: true });
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (file === 'registry.ts') {
|
|
197
|
-
fs.copyFileSync(path.join(microcomponentsSourceDir, file), path.join(microcomponentsDestDir, file));
|
|
198
|
-
}
|
|
214
|
+
const registryDest = path.join(microcomponentsDestDir, 'registry.ts');
|
|
215
|
+
if (!fs.existsSync(registryDest)) {
|
|
216
|
+
fs.writeFileSync(registryDest, canonical?.registry || FALLBACK_CONSUMER_REGISTRY_TS);
|
|
199
217
|
}
|
|
200
218
|
const microReadme = path.join(microcomponentsDestDir, 'README.md');
|
|
201
219
|
if (!fs.existsSync(microReadme)) {
|
|
@@ -209,6 +227,9 @@ if (fs.existsSync(microcomponentsSourceDir)) {
|
|
|
209
227
|
'Requirements:',
|
|
210
228
|
'- DEFAULT export, or the registry ignores the file.',
|
|
211
229
|
'- The name must match the tab/page name in PascalCase + `Content`.',
|
|
230
|
+
'- Two lines in `registry.ts` (import + `registerMicrocomponent`) — the',
|
|
231
|
+
' build also finds the file on its own, but in a runtime without the',
|
|
232
|
+
' Vite plugin (Google AI Studio) those lines are what loads it.',
|
|
212
233
|
'- No `<h1>`, no `<DashboardNav />` — the page already renders both.',
|
|
213
234
|
'- Renders inside an existing panel: no page wrapper, no `min-h-screen`.',
|
|
214
235
|
'',
|
|
@@ -216,7 +237,7 @@ if (fs.existsSync(microcomponentsSourceDir)) {
|
|
|
216
237
|
''
|
|
217
238
|
].join('\n'));
|
|
218
239
|
}
|
|
219
|
-
console.log(' ✓
|
|
240
|
+
console.log(' ✓ Ensured the page-content list (microcomponents/registry.ts) exists');
|
|
220
241
|
}
|
|
221
242
|
|
|
222
243
|
// ── 3b. AI instruction files ─────────────────────────────────────────────
|
|
@@ -241,8 +262,10 @@ change the owner can use and a change they cannot.
|
|
|
241
262
|
1. Every page is ONE file in \`src/your-ai-workflow-firebase-os/configs/\` named
|
|
242
263
|
\`somethingTab.config.ts\` (signed-in users, the team, or admins) or
|
|
243
264
|
\`somethingPage.config.ts\` (anyone), plus ONE content file in
|
|
244
|
-
\`microcomponents
|
|
245
|
-
|
|
265
|
+
\`microcomponents/\` and its TWO lines in \`microcomponents/registry.ts\`
|
|
266
|
+
(import + \`registerMicrocomponent\`). Nothing else. Never a second config
|
|
267
|
+
for the same page. The one exception is the home page: it is \`Home.tsx\`
|
|
268
|
+
and has no config.
|
|
246
269
|
2. "For my team", "team page", "for us", "internal" all mean
|
|
247
270
|
\`pageType: 'internal'\` in a \`*Tab.config.ts\`. A \`*Tab.config.ts\` is never
|
|
248
271
|
\`'public'\` — the app will not honour it.
|
|
@@ -264,12 +287,12 @@ change the owner can use and a change they cannot.
|
|
|
264
287
|
is on adding THINGS, not on doing the asked thing well.
|
|
265
288
|
7. A page the owner added on their Pages screen ALREADY EXISTS. The sentence
|
|
266
289
|
they paste says so, names its web address and names its one content file —
|
|
267
|
-
write exactly that file
|
|
268
|
-
address, nothing to Firestore, and never a reply
|
|
269
|
-
fix anything. Only a page asked for in the chat,
|
|
270
|
-
yet, is YOURS to create, and then it is created as
|
|
271
|
-
Firestore: its config file and its content file, both
|
|
272
|
-
page's name — a page called "Client onboarding" is
|
|
290
|
+
write exactly that file plus its two registry.ts lines and NOTHING else:
|
|
291
|
+
no config file, no second address, nothing to Firestore, and never a reply
|
|
292
|
+
telling them to create or fix anything. Only a page asked for in the chat,
|
|
293
|
+
one that exists nowhere yet, is YOURS to create, and then it is created as
|
|
294
|
+
FILES — never in Firestore: its config file and its content file, both
|
|
295
|
+
named from the page's name — a page called "Client onboarding" is
|
|
273
296
|
\`clientOnboardingTab.config.ts\` plus \`ClientOnboardingContent.tsx\`.
|
|
274
297
|
Nothing else registers a page: no registerPages.ts, no customRoutes, no
|
|
275
298
|
database writes. When a config file already exists for that page or
|
|
@@ -310,7 +333,9 @@ stops the build with one sentence that says the fix.
|
|
|
310
333
|
preset and no matching content file (the page would render empty — the
|
|
311
334
|
error names the exact \`*Content.tsx\` to create); a \`<style>\` tag in a
|
|
312
335
|
content file (global CSS — it restyles and re-pads every page of the app,
|
|
313
|
-
style with theme classes instead).
|
|
336
|
+
style with theme classes instead); a \`*Content.tsx\` or \`*.config.ts\`
|
|
337
|
+
saved anywhere except directly in its own folder — a subfolder, another
|
|
338
|
+
directory — is invisible to the app, so the build stops and names the move.
|
|
314
339
|
- A hand-written form — a \`<form>\` or an \`onSubmit\` in a content file with
|
|
315
340
|
no \`<ContactPopup />\` — prints a warning, because its entries never reach
|
|
316
341
|
the Inbox and never show on the Forms screen. So does a hard-coded colour
|
|
@@ -397,8 +422,19 @@ export default function ProdTimerContent({ config, actions }: { config: any; act
|
|
|
397
422
|
}
|
|
398
423
|
\`\`\`
|
|
399
424
|
|
|
400
|
-
|
|
401
|
-
|
|
425
|
+
Then add TWO lines for it to
|
|
426
|
+
\`src/your-ai-workflow-firebase-os/microcomponents/registry.ts\` (create that
|
|
427
|
+
file if it is missing — its first line is
|
|
428
|
+
\`import { registerMicrocomponent } from 'your-ai-workflow-firebase-os';\`):
|
|
429
|
+
|
|
430
|
+
\`\`\`ts
|
|
431
|
+
import ProdTimerContent from './ProdTimerContent';
|
|
432
|
+
registerMicrocomponent('prod_timer', ProdTimerContent);
|
|
433
|
+
\`\`\`
|
|
434
|
+
|
|
435
|
+
The key is the file's name without \`Content\`, lowercased with \`_\` between
|
|
436
|
+
words. Never remove another page's lines from registry.ts. There is no web
|
|
437
|
+
address to wire — the page finds its content by that key.
|
|
402
438
|
|
|
403
439
|
### The pages that already exist, and the one file each is filled by
|
|
404
440
|
|
|
@@ -422,7 +458,9 @@ and never a new page, a new config file or a new form.
|
|
|
422
458
|
Rules 1-6 apply to microcomponent files only. Rules 7-9 (styling) apply
|
|
423
459
|
everywhere, including the home page.
|
|
424
460
|
|
|
425
|
-
1. Create only that one file
|
|
461
|
+
1. Create only that one file, plus its two lines in
|
|
462
|
+
\`microcomponents/registry.ts\`. Do NOT edit \`src/App.tsx\` or \`src/main.tsx\`,
|
|
463
|
+
and never remove another page's lines from registry.ts.
|
|
426
464
|
2. Do NOT use the \`customRoutes\` prop and do NOT add react-router routes.
|
|
427
465
|
3. Use a DEFAULT export. A named-only export is silently ignored by the registry.
|
|
428
466
|
4. Do NOT render \`<DashboardNav />\`, a navbar, or any menu — the page has one.
|
|
@@ -701,20 +739,19 @@ starting layout, its form), then pasted the row's sentence and finished it:
|
|
|
701
739
|
> already made and it is already in the menu — do not create a page, a web
|
|
702
740
|
> address or a settings file for it, and do not edit any other file.
|
|
703
741
|
> Write one file:
|
|
704
|
-
> src/your-ai-workflow-firebase-os/microcomponents/
|
|
742
|
+
> src/your-ai-workflow-firebase-os/microcomponents/OnboardingContent.tsx,
|
|
705
743
|
> with a default export. That file is what the page shows.
|
|
706
|
-
>
|
|
707
|
-
>
|
|
708
|
-
>
|
|
709
|
-
>
|
|
710
|
-
>
|
|
711
|
-
>
|
|
712
|
-
>
|
|
713
|
-
>
|
|
714
|
-
>
|
|
715
|
-
>
|
|
716
|
-
>
|
|
717
|
-
> code.
|
|
744
|
+
> Then add two lines to
|
|
745
|
+
> src/your-ai-workflow-firebase-os/microcomponents/registry.ts so the app
|
|
746
|
+
> loads your file: import OnboardingContent from './OnboardingContent'; and
|
|
747
|
+
> registerMicrocomponent('onboarding', OnboardingContent); —
|
|
748
|
+
> registerMicrocomponent is imported there from 'your-ai-workflow-firebase-os'.
|
|
749
|
+
> Never remove another page's lines from that file.
|
|
750
|
+
> Make it match the rest of my app: style with the app's own theme styles so
|
|
751
|
+
> it looks right in light and dark. Do not pick your own colors.
|
|
752
|
+
> Until your file exists the page shows a ready-made board layout. Treat it
|
|
753
|
+
> as a reference, not a rule — your file replaces everything inside the
|
|
754
|
+
> page, so build the whole thing the way I describe below.
|
|
718
755
|
> Here is what I would like on it: columns, in this order: To do, In
|
|
719
756
|
> progress, Done. Each card has a client name (short text), a contact email,
|
|
720
757
|
> a start date and a short note (long text). Anyone on the team can add a
|
|
@@ -724,19 +761,20 @@ starting layout, its form), then pasted the row's sentence and finished it:
|
|
|
724
761
|
|
|
725
762
|
Every line is a decision already made. The audience, the web address
|
|
726
763
|
(\`route\`), the name (\`tabName\` / \`pageName\`), the heading (\`tabTitle\` /
|
|
727
|
-
\`pageTitle\`) and
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
the "button at the top" is yours to
|
|
736
|
-
kind in brackets — short text, long
|
|
737
|
-
choose one — is the input you
|
|
738
|
-
\`
|
|
739
|
-
|
|
764
|
+
\`pageTitle\`) and the starting layout (\`template\`) were set on the Pages
|
|
765
|
+
screen — read them from the \`config\` your content file receives, and never
|
|
766
|
+
write them again. Your component also receives \`actions\` (\`records\`,
|
|
767
|
+
\`saveRecord\`, \`updateRecord\`, \`deleteRecord\`, \`uploadFile\`, \`user\`, and the
|
|
768
|
+
rest listed under presets below) — read and change data ONLY through those,
|
|
769
|
+
never by importing firebase yourself. THE FILE NAME RULE: write EXACTLY the
|
|
770
|
+
file path the request names, letter for letter — the page is wired to that
|
|
771
|
+
one name, and a file under any other name or folder is never found. "No file
|
|
772
|
+
uploads" means draw no upload control; the "button at the top" is yours to
|
|
773
|
+
draw inside the content; and a question's kind in brackets — short text, long
|
|
774
|
+
text, email, phone, number, money, yes-no, choose one — is the input you
|
|
775
|
+
render (\`text\`, \`textarea\`, \`email\`, \`tel\`, \`number\`, \`currency\`,
|
|
776
|
+
\`checkbox\`, \`select\`). "Build only this" means exactly that. Ask nothing;
|
|
777
|
+
build it in one turn.
|
|
740
778
|
|
|
741
779
|
### A form
|
|
742
780
|
|
|
@@ -887,19 +925,20 @@ add / upload / delete — you then write the microcomponent that fills it
|
|
|
887
925
|
|
|
888
926
|
A preset (\`board\`, \`calendar\`, \`table\`, \`drive\`) is only a starting point:
|
|
889
927
|
it renders a working board / calendar / table / file library straight away when
|
|
890
|
-
there is NO content file. As soon as you add a content file,
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
\`
|
|
894
|
-
\`
|
|
895
|
-
\`
|
|
896
|
-
\`
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
the finished page — they described what they want, so
|
|
901
|
-
|
|
902
|
-
|
|
928
|
+
there is NO content file. As soon as you add a content file, the page renders
|
|
929
|
+
YOUR file instead of the preset — the whole panel, its buttons included, is
|
|
930
|
+
yours to draw, and your component receives the same \`actions\` as a
|
|
931
|
+
from-scratch page (\`records\`, \`loading\`, \`saveRecord\`, \`updateRecord\`,
|
|
932
|
+
\`deleteRecord\`, \`startEdit\`, \`confirmDelete\`, \`uploadFile\`, \`openAddForm\`,
|
|
933
|
+
\`setAddForm\`, \`setFileHandler\`, \`user\` — every record you save must include
|
|
934
|
+
\`uid: actions.user.uid\`). The preset is a REFERENCE for what to build, never
|
|
935
|
+
a frame you build inside: when the owner wants their own fields, stages or
|
|
936
|
+
views on "something like the board", look at the board for the shape and
|
|
937
|
+
build the whole board UI in the content file with those \`actions\`. Never
|
|
938
|
+
treat a preset as the finished page — they described what they want, so
|
|
939
|
+
build that.
|
|
940
|
+
|
|
941
|
+
Whatever you save through those actions, include \`uid: user.uid\` in the data:
|
|
903
942
|
on Your-team pages only the person who created a record (or an admin) may edit
|
|
904
943
|
or delete it, and a record without \`uid\` can only ever be changed by an admin.
|
|
905
944
|
|
|
@@ -931,12 +970,12 @@ useEffect(() => {
|
|
|
931
970
|
if (userRole !== 'admin') return; // only admins may read the Inbox
|
|
932
971
|
return onSnapshot(collection(db, 'user_submissions'), snap => {
|
|
933
972
|
snap.docs.forEach(d => {
|
|
934
|
-
if (
|
|
973
|
+
if (actions.records.some((r: any) => r.sourceId === d.id)) return; // already imported
|
|
935
974
|
const s = d.data();
|
|
936
|
-
|
|
975
|
+
actions.saveRecord({ ...s, sourceId: d.id, stage: 'Prospect', uid: user!.uid });
|
|
937
976
|
});
|
|
938
977
|
});
|
|
939
|
-
}, [userRole,
|
|
978
|
+
}, [userRole, actions.records.length]);
|
|
940
979
|
\`\`\`
|
|
941
980
|
|
|
942
981
|
Never write to \`user_submissions\` or \`user_requests\` from a page, and never
|
|
@@ -65,8 +65,10 @@ export const themeConfig = {
|
|
|
65
65
|
foreground: '#1e1b4b', // Primary text color
|
|
66
66
|
|
|
67
67
|
accent: '#7c3aed', // Main branding color (Purple)
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// A faint accent wash rather than white-on-white: inner cards were
|
|
69
|
+
// invisible against the page in light mode. Kept quiet on purpose.
|
|
70
|
+
panelBg: 'rgba(124, 58, 237, 0.04)', // Card/Panel background
|
|
71
|
+
panelBorder: 'rgba(124, 58, 237, 0.15)', // Card/Panel borders
|
|
70
72
|
|
|
71
73
|
buttonGradient: ['#5b21b6', '#a78bfa'], // [Start, End]
|
|
72
74
|
secondaryButton: {
|
package/src/index.css
CHANGED
|
@@ -75,8 +75,10 @@ input[type=number] {
|
|
|
75
75
|
--accent-deep: #6d28d9;
|
|
76
76
|
/* violet-700 */
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
/* A faint accent wash, not white-on-white: inner cards were invisible
|
|
79
|
+
against the page in light mode. Kept quiet on purpose. */
|
|
80
|
+
--panel-bg: color-mix(in srgb, var(--accent-color) 4%, transparent);
|
|
81
|
+
--panel-border: color-mix(in srgb, var(--accent-color) 15%, transparent);
|
|
80
82
|
|
|
81
83
|
--button-bg: linear-gradient(135deg, #5b21b6 0%, #a78bfa 100%);
|
|
82
84
|
--button-fg: #ffffff;
|
|
@@ -192,6 +194,13 @@ h3,
|
|
|
192
194
|
0 2px 6px rgba(24, 16, 64, 0.07);
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
/* An open drop-down. Deliberately NOT .glow-focus: the 25px halo read as a
|
|
198
|
+
spotlight on what is just a list being open, and it was sliced off by any
|
|
199
|
+
panel animating its height nearby. An inset ring cannot be clipped. */
|
|
200
|
+
.select-open {
|
|
201
|
+
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent-color) 22%, transparent);
|
|
202
|
+
}
|
|
203
|
+
|
|
195
204
|
.glow-focus:focus-within {
|
|
196
205
|
box-shadow: 0 0 25px var(--glow-color, var(--primary-glow));
|
|
197
206
|
border-color: var(--accent-color);
|
|
@@ -224,27 +233,28 @@ h3,
|
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
/*
|
|
227
|
-
* Fields in the admin consoles keep the accent focus, but tight
|
|
228
|
-
* is panels inside panels, and the ones that open and close
|
|
229
|
-
* height behind overflow: hidden
|
|
230
|
-
* field
|
|
231
|
-
*
|
|
232
|
-
*
|
|
236
|
+
* Fields in the admin consoles keep the accent focus, but tight — and INSET.
|
|
237
|
+
* A console is panels inside panels, and the ones that open and close
|
|
238
|
+
* animate their height behind overflow: hidden, so anything drawn OUTSIDE
|
|
239
|
+
* the field's own box — a halo, even a 3px outer ring — gets sliced off at
|
|
240
|
+
* whichever panel edge is nearest. An inset ring is drawn on the inside of
|
|
241
|
+
* the field's border, so no container can ever clip it, anywhere.
|
|
242
|
+
* `.console-field` goes on the box itself; `.console-select` goes on the
|
|
233
243
|
* CustomSelect wrapper, whose bordered box is the trigger inside it.
|
|
234
244
|
*/
|
|
235
245
|
.console-field:focus,
|
|
236
246
|
.console-field:focus-within,
|
|
237
|
-
.console-select .
|
|
238
|
-
box-shadow: 0 0 0
|
|
247
|
+
.console-select .select-open {
|
|
248
|
+
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent-color) 22%, transparent) !important;
|
|
239
249
|
border-color: var(--accent-color) !important;
|
|
240
250
|
outline: none;
|
|
241
|
-
transform: none;
|
|
251
|
+
transform: none !important;
|
|
242
252
|
}
|
|
243
253
|
|
|
244
254
|
/* A field that is refusing what was typed stays red while it is focused. */
|
|
245
255
|
.console-field.is-invalid:focus,
|
|
246
256
|
.console-field.is-invalid:focus-within {
|
|
247
|
-
box-shadow: 0 0 0
|
|
257
|
+
box-shadow: inset 0 0 0 2px color-mix(in srgb, #ef4444 25%, transparent) !important;
|
|
248
258
|
border-color: #ef4444 !important;
|
|
249
259
|
}
|
|
250
260
|
|