firebase-os 1.1.4 → 1.1.6

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.
Files changed (124) hide show
  1. package/dist/FirebaseOS.d.ts +15 -0
  2. package/dist/firebase-os.cjs.js +5 -20
  3. package/dist/firebase-os.es.js +95 -90
  4. package/dist/index.d.ts +3 -0
  5. package/dist/lib/ConfigContext.d.ts +12 -0
  6. package/package.json +3 -2
  7. package/scripts/postinstall.js +86 -15
  8. package/src/App.css +184 -0
  9. package/src/App.tsx +214 -0
  10. package/src/FirebaseOS.tsx +81 -0
  11. package/src/assets/hero.png +0 -0
  12. package/src/assets/react.svg +1 -0
  13. package/src/assets/vite.svg +1 -0
  14. package/src/components/AdminNotifications.test.tsx +98 -0
  15. package/src/components/AdminNotifications.tsx +194 -0
  16. package/src/components/Button.test.tsx +22 -0
  17. package/src/components/Button.tsx +53 -0
  18. package/src/components/ConfirmModal.test.tsx +98 -0
  19. package/src/components/ConfirmModal.tsx +73 -0
  20. package/src/components/ContactPopup.test.tsx +98 -0
  21. package/src/components/ContactPopup.tsx +437 -0
  22. package/src/components/CustomSelect.test.tsx +47 -0
  23. package/src/components/CustomSelect.tsx +89 -0
  24. package/src/components/DashboardNav.test.tsx +98 -0
  25. package/src/components/DashboardNav.tsx +281 -0
  26. package/src/components/Input.test.tsx +33 -0
  27. package/src/components/Input.tsx +61 -0
  28. package/src/components/JsonEditor.tsx +579 -0
  29. package/src/components/Navbar.test.tsx +98 -0
  30. package/src/components/Navbar.tsx +563 -0
  31. package/src/configs/forms/contactForm.config.ts +15 -0
  32. package/src/configs/forms/index.ts +29 -0
  33. package/src/configs/forms/pubForm.config.ts +11 -0
  34. package/src/configs/forms/supportForm.config.ts +14 -0
  35. package/src/configs/forms/userForm.config.ts +11 -0
  36. package/src/configs/pages/admin.config.ts +29 -0
  37. package/src/configs/pages/contact.config.ts +6 -0
  38. package/src/configs/pages/home.config.ts +18 -0
  39. package/src/configs/pages/mem.config.ts +2 -0
  40. package/src/configs/pages/menuOrders.config.ts +11 -0
  41. package/src/configs/pages/pub.config.ts +11 -0
  42. package/src/configs/pages/shared.config.ts +29 -0
  43. package/src/configs/pages/support.config.ts +7 -0
  44. package/src/configs/pages/tabOrders.config.ts +33 -0
  45. package/src/configs/pages/user.config.ts +29 -0
  46. package/src/configs/theme.config.ts +93 -0
  47. package/src/index.css +403 -0
  48. package/src/index.ts +22 -0
  49. package/src/lib/AuthContext.test.tsx +88 -0
  50. package/src/lib/AuthContext.tsx +191 -0
  51. package/src/lib/ConfigContext.tsx +45 -0
  52. package/src/lib/ThemeContext.tsx +233 -0
  53. package/src/lib/firebase.ts +91 -0
  54. package/src/main.tsx +22 -0
  55. package/src/microcomponents/AdminExampleContent.tsx +44 -0
  56. package/src/microcomponents/PrivateExampleContent.tsx +39 -0
  57. package/src/microcomponents/Public.tsx +126 -0
  58. package/src/microcomponents/SharedExampleContent.tsx +53 -0
  59. package/src/pages/Dashboard.test.tsx +98 -0
  60. package/src/pages/Dashboard.tsx +60 -0
  61. package/src/pages/DynamicPage.tsx +237 -0
  62. package/src/pages/FormsAdmin.test.tsx +98 -0
  63. package/src/pages/FormsAdmin.tsx +459 -0
  64. package/src/pages/Home.test.tsx +98 -0
  65. package/src/pages/Home.tsx +144 -0
  66. package/src/pages/Login.test.tsx +98 -0
  67. package/src/pages/Login.tsx +108 -0
  68. package/src/pages/PagesAdmin.test.tsx +98 -0
  69. package/src/pages/PagesAdmin.tsx +1022 -0
  70. package/src/pages/Profile.test.tsx +98 -0
  71. package/src/pages/Profile.tsx +319 -0
  72. package/src/pages/Register.test.tsx +98 -0
  73. package/src/pages/Register.tsx +116 -0
  74. package/src/pages/Requests.test.tsx +95 -0
  75. package/src/pages/Requests.tsx +422 -0
  76. package/src/pages/ResetPassword.test.tsx +98 -0
  77. package/src/pages/ResetPassword.tsx +92 -0
  78. package/src/pages/Settings.test.tsx +98 -0
  79. package/src/pages/Settings.tsx +393 -0
  80. package/src/pages/Setup.tsx +407 -0
  81. package/src/pages/StorageAdmin.test.tsx +150 -0
  82. package/src/pages/StorageAdmin.tsx +769 -0
  83. package/src/pages/Submissions.test.tsx +95 -0
  84. package/src/pages/Submissions.tsx +378 -0
  85. package/src/pages/Templates.test.tsx +98 -0
  86. package/src/pages/Templates.tsx +103 -0
  87. package/src/pages/ThemeAdmin.test.tsx +144 -0
  88. package/src/pages/ThemeAdmin.tsx +1000 -0
  89. package/src/pages/Users.test.tsx +95 -0
  90. package/src/pages/Users.tsx +334 -0
  91. package/src/pages/Verify.test.tsx +98 -0
  92. package/src/pages/Verify.tsx +95 -0
  93. package/src/prompts/index.ts +13 -0
  94. package/src/prompts/pages/publicPage.ts +44 -0
  95. package/src/prompts/sharedConstants.ts +12 -0
  96. package/src/prompts/tabs/board/adminboard.ts +32 -0
  97. package/src/prompts/tabs/board/privateboard.ts +36 -0
  98. package/src/prompts/tabs/board/publicboard.ts +36 -0
  99. package/src/prompts/tabs/calendar/admincalendar.ts +32 -0
  100. package/src/prompts/tabs/calendar/privatecalendar.ts +36 -0
  101. package/src/prompts/tabs/calendar/publiccalendar.ts +36 -0
  102. package/src/prompts/tabs/crud/admin.ts +54 -0
  103. package/src/prompts/tabs/crud/private.ts +55 -0
  104. package/src/prompts/tabs/crud/shared.ts +53 -0
  105. package/src/prompts/tabs/table/admintable.ts +32 -0
  106. package/src/prompts/tabs/table/privatetable.ts +36 -0
  107. package/src/prompts/tabs/table/publictable.ts +36 -0
  108. package/src/setupTests.ts +1 -0
  109. package/src/templates/AdminPageTemplate.tsx +678 -0
  110. package/src/templates/PrivatePageTemplate.tsx +594 -0
  111. package/src/templates/PublicPageTemplate.tsx +92 -0
  112. package/src/templates/SharedPageTemplate.tsx +551 -0
  113. package/src/templates/TemplateBoard.test.tsx +106 -0
  114. package/src/templates/TemplateBoard.tsx +642 -0
  115. package/src/templates/TemplateCalendar.test.tsx +106 -0
  116. package/src/templates/TemplateCalendar.tsx +848 -0
  117. package/src/templates/TemplateConfirmation.test.tsx +106 -0
  118. package/src/templates/TemplateConfirmation.tsx +145 -0
  119. package/src/templates/TemplateInlineForm.test.tsx +106 -0
  120. package/src/templates/TemplateInlineForm.tsx +129 -0
  121. package/src/templates/TemplatePopupForm.test.tsx +106 -0
  122. package/src/templates/TemplatePopupForm.tsx +174 -0
  123. package/src/templates/TemplateTable.test.tsx +106 -0
  124. package/src/templates/TemplateTable.tsx +675 -0
@@ -129,9 +129,11 @@ function qe({ children: t, scopeSelector: n }) {
129
129
  return r === "system" ? (o(t.matches ? "light" : "dark"), t.addEventListener("change", e)) : o(r), () => t.removeEventListener("change", e);
130
130
  }, [r]), m(() => {
131
131
  let e = (n ? document.querySelector(n) : document.documentElement) || document.documentElement, t = a === "dark" ? s.colors.dark : s.colors.light;
132
- e.style.setProperty("--bg-color", t.background), t.backgroundSecondary && e.style.setProperty("--bg-secondary-color", t.backgroundSecondary), e.style.setProperty("--fg-color", t.foreground), e.style.setProperty("--accent-color", t.accent), e.style.setProperty("--panel-bg", t.panelBg), e.style.setProperty("--panel-border", t.panelBorder);
133
- let i = (e) => !e || e.length === 0 ? "transparent" : e.length === 1 ? e[0] : `linear-gradient(135deg, ${e.map((t, n) => `${t} ${Math.round(n / (e.length - 1) * 100)}%`).join(", ")})`;
134
- e.style.setProperty("--button-bg", i(t.buttonGradient)), e.style.setProperty("--button-fg", t.buttonText || "#ffffff"), e.style.setProperty("--button-hover-bg", i(t.buttonHoverGradient || t.buttonGradient)), e.style.setProperty("--text-gradient", i(t.textGradient)), e.style.setProperty("--glow-color", t.glowColor || "rgba(124, 58, 237, 0.3)"), e.style.setProperty("--notification-icon-color", t.notificationIconColor || t.accent), e.style.setProperty("--btn-sec-bg", t.secondaryButton?.bg || "rgba(255, 255, 255, 0.05)"), e.style.setProperty("--btn-sec-border", t.secondaryButton?.border || "transparent"), e.style.setProperty("--btn-sec-text", t.secondaryButton?.text || t.foreground), e.style.setProperty("--selection-bg", t.selectionBg || "color-mix(in srgb, var(--accent-color) 40%, transparent)"), e.style.setProperty("--selection-text", t.selectionText || "#ffffff"), e.setAttribute("data-theme", a), localStorage.setItem("theme-mode", r), s.cornerRounding === "custom" && s.cornerRadii ? (e.style.setProperty("--btn-radius", `${parseFloat(String(s.cornerRadii.buttonRadius)) || 0}rem`), e.style.setProperty("--card-radius", `${parseFloat(String(s.cornerRadii.cardRadius)) || 0}rem`), e.style.setProperty("--input-radius", `${parseFloat(String(s.cornerRadii.inputRadius)) || 0}rem`), e.style.setProperty("--modal-radius", `${parseFloat(String(s.cornerRadii.modalRadius)) || 0}rem`)) : (e.style.setProperty("--btn-radius", "0.75rem"), e.style.setProperty("--card-radius", "1.5rem"), e.style.setProperty("--input-radius", "0.75rem"), e.style.setProperty("--modal-radius", "1.5rem")), ((t) => {
132
+ e.style.setProperty("--bg-color", t.background), e.style.setProperty("--bg-secondary-color", t.backgroundSecondary || t.background), e.style.setProperty("--fg-color", t.foreground), e.style.setProperty("--accent-color", t.accent), e.style.setProperty("--panel-bg", t.panelBg), e.style.setProperty("--panel-border", t.panelBorder);
133
+ let i = (e, t) => !e || e.length === 0 ? t || "transparent" : e.length === 1 ? e[0] : `linear-gradient(135deg, ${e.map((t, n) => `${t} ${Math.round(n / (e.length - 1) * 100)}%`).join(", ")})`, o = a === "dark" ? "linear-gradient(135deg, #6d28d9 0%, #a78bfa 100%)" : "linear-gradient(135deg, #5b21b6 0%, #a78bfa 100%)";
134
+ e.style.setProperty("--button-bg", i(t.buttonGradient, o)), e.style.setProperty("--button-fg", t.buttonText || "#ffffff"), e.style.setProperty("--button-hover-bg", i(t.buttonHoverGradient || t.buttonGradient, o)), e.style.setProperty("--text-gradient", i(t.textGradient, `linear-gradient(135deg, ${t.foreground} 0%, ${t.accent} 100%)`)), e.style.setProperty("--glow-color", t.glowColor || "rgba(124, 58, 237, 0.3)"), e.style.setProperty("--notification-icon-color", t.notificationIconColor || t.accent);
135
+ let c = t.secondaryButton?.bg || (a === "dark" ? "rgba(255, 255, 255, 0.05)" : "rgba(0, 0, 0, 0.03)"), l = t.secondaryButton?.border || (a === "dark" ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.06)"), u = t.secondaryButton?.text || t.foreground;
136
+ e.style.setProperty("--btn-sec-bg", c), e.style.setProperty("--btn-sec-border", l), e.style.setProperty("--btn-sec-text", u), e.style.setProperty("--selection-bg", t.selectionBg || "color-mix(in srgb, var(--accent-color) 40%, transparent)"), e.style.setProperty("--selection-text", t.selectionText || "#ffffff"), e.setAttribute("data-theme", a), localStorage.setItem("theme-mode", r), s.cornerRounding === "custom" && s.cornerRadii ? (e.style.setProperty("--btn-radius", `${parseFloat(String(s.cornerRadii.buttonRadius)) || 0}rem`), e.style.setProperty("--card-radius", `${parseFloat(String(s.cornerRadii.cardRadius)) || 0}rem`), e.style.setProperty("--input-radius", `${parseFloat(String(s.cornerRadii.inputRadius)) || 0}rem`), e.style.setProperty("--modal-radius", `${parseFloat(String(s.cornerRadii.modalRadius)) || 0}rem`)) : (e.style.setProperty("--btn-radius", "0.75rem"), e.style.setProperty("--card-radius", "1.5rem"), e.style.setProperty("--input-radius", "0.75rem"), e.style.setProperty("--modal-radius", "1.5rem")), ((t) => {
135
137
  let n = {
136
138
  Inter: "\"Inter\", sans-serif",
137
139
  Outfit: "\"Outfit\", sans-serif",
@@ -144,8 +146,8 @@ function qe({ children: t, scopeSelector: n }) {
144
146
  let r = document.getElementById("dynamic-google-font");
145
147
  t === "System UI" ? r && r.remove() : (r || (r = document.createElement("link"), r.id = "dynamic-google-font", r.rel = "stylesheet", document.head.appendChild(r)), r.href = `https://fonts.googleapis.com/css2?family=${t.replace(/ /g, "+")}:wght@400;500;600;700;800;900&display=swap`), e.style.fontSize = "16px", e.style.setProperty("--weight-h1", "800"), e.style.setProperty("--weight-h2", "700"), e.style.setProperty("--weight-h3", "700"), e.style.setProperty("--leading-normal", "1.5");
146
148
  })(s.fontFamily || "Inter");
147
- let o = document.querySelector("link[rel*='icon']");
148
- o || (o = document.createElement("link"), o.rel = "icon", document.head.appendChild(o)), s.faviconUrl && (o.href = s.faviconUrl);
149
+ let d = document.querySelector("link[rel*='icon']");
150
+ d || (d = document.createElement("link"), d.rel = "icon", document.head.appendChild(d)), s.faviconUrl && (d.href = s.faviconUrl);
149
151
  }, [
150
152
  a,
151
153
  r,
@@ -78857,9 +78859,14 @@ function OVn() {
78857
78859
  },
78858
78860
  transition: { delay: t * .05 },
78859
78861
  className: "glass-panel border border-[var(--panel-border)] rounded-2xl flex flex-col overflow-hidden hover:border-accent/40 transition-all duration-300",
78860
- children: [/* @__PURE__ */ V("button", {
78862
+ children: [/* @__PURE__ */ V("div", {
78863
+ role: "button",
78864
+ tabIndex: 0,
78865
+ onKeyDown: (t) => {
78866
+ (t.key === "Enter" || t.key === " ") && d(e.id);
78867
+ },
78861
78868
  onClick: () => d(e.id),
78862
- className: "w-full flex items-center justify-between p-4 md:px-6 hover:bg-foreground/[0.02] transition-colors text-left",
78869
+ className: "w-full flex items-center justify-between p-4 md:px-6 hover:bg-foreground/[0.02] transition-colors text-left cursor-pointer",
78863
78870
  children: [/* @__PURE__ */ V("div", {
78864
78871
  className: "flex items-center gap-4 flex-1 min-w-0",
78865
78872
  children: [/* @__PURE__ */ B("div", {
@@ -78999,9 +79006,14 @@ function OVn() {
78999
79006
  },
79000
79007
  transition: { delay: t * .05 },
79001
79008
  className: "glass-panel border border-[var(--panel-border)] rounded-2xl flex flex-col overflow-hidden opacity-50 grayscale hover:opacity-100 hover:grayscale-0 hover:border-accent/40 transition-all duration-300",
79002
- children: [/* @__PURE__ */ V("button", {
79009
+ children: [/* @__PURE__ */ V("div", {
79010
+ role: "button",
79011
+ tabIndex: 0,
79012
+ onKeyDown: (t) => {
79013
+ (t.key === "Enter" || t.key === " ") && d(e.id);
79014
+ },
79003
79015
  onClick: () => d(e.id),
79004
- className: "w-full flex items-center justify-between p-4 md:px-6 hover:bg-foreground/[0.02] transition-colors text-left",
79016
+ className: "w-full flex items-center justify-between p-4 md:px-6 hover:bg-foreground/[0.02] transition-colors text-left cursor-pointer",
79005
79017
  children: [/* @__PURE__ */ V("div", {
79006
79018
  className: "flex items-center gap-4 flex-1 min-w-0",
79007
79019
  children: [/* @__PURE__ */ B("div", {
@@ -89363,62 +89375,62 @@ function vUn({ config: e }) {
89363
89375
  //#endregion
89364
89376
  //#region src/pages/DynamicPage.tsx
89365
89377
  function yUn() {
89366
- let e = Se(), { user: t } = $e(), [n, r] = x(null), [i, a] = x(!0);
89378
+ let e = Se(), { user: t } = $e(), n = Ze(), [r, i] = x(null), [a, o] = x(!0);
89367
89379
  m(() => {
89368
89380
  (async () => {
89369
- a(!0);
89381
+ o(!0);
89370
89382
  try {
89371
- let [t, n] = await Promise.all([ee(C(W, "sys_pages")), ee(C(W, "sys_tabs"))]), i = null, a = null, o = (t, n) => {
89383
+ let [t, n] = await Promise.all([ee(C(W, "sys_pages")), ee(C(W, "sys_tabs"))]), r = null, a = null, o = (t, n) => {
89372
89384
  t.forEach((t) => {
89373
- let r = t.data(), o = r.route;
89385
+ let i = t.data(), o = i.route;
89374
89386
  o && !o.startsWith("/") && (o = "/" + o);
89375
89387
  let s = "/" + t.id === e.pathname;
89376
- (o === e.pathname || s) && (i = {
89377
- ...r,
89388
+ (o === e.pathname || s) && (r = {
89389
+ ...i,
89378
89390
  isPrivate: n,
89379
- isAdmin: r.pageType === "admin",
89380
- isShared: r.pageType === "shared"
89391
+ isAdmin: i.pageType === "admin",
89392
+ isShared: i.pageType === "shared"
89381
89393
  }, a = t.id);
89382
89394
  });
89383
89395
  };
89384
- if (o(t, !1), i || o(n, !0), i || (e.pathname === "/contact" ? (i = {
89396
+ if (o(t, !1), r || o(n, !0), r || (e.pathname === "/contact" ? (r = {
89385
89397
  route: "/contact",
89386
89398
  template: "popup_form",
89387
89399
  form: "contact"
89388
- }, a = "contact") : e.pathname === "/support" && (i = {
89400
+ }, a = "contact") : e.pathname === "/support" && (r = {
89389
89401
  route: "/support",
89390
89402
  template: "popup_form",
89391
89403
  form: "support"
89392
- }, a = "support")), i) {
89393
- if ((a === "contact" || a === "support") && !i.form && (i.form = a), i.pageId = a, i.form) {
89394
- let e = await E(T(W, "sys_forms", i.form));
89395
- if (e.exists()) i.formConfigOverride = {
89404
+ }, a = "support")), r) {
89405
+ if ((a === "contact" || a === "support") && !r.form && (r.form = a), r.pageId = a, r.form) {
89406
+ let e = await E(T(W, "sys_forms", r.form));
89407
+ if (e.exists()) r.formConfigOverride = {
89396
89408
  ...e.data(),
89397
89409
  id: e.id
89398
89410
  };
89399
- else if (i.form === "contact") {
89411
+ else if (r.form === "contact") {
89400
89412
  let { contactFormConfig: e } = await import("./contactForm.config-DLeI_ZEj.js");
89401
- i.formConfigOverride = e;
89402
- } else if (i.form === "support") {
89413
+ r.formConfigOverride = e;
89414
+ } else if (r.form === "support") {
89403
89415
  let { supportFormConfig: e } = await import("./supportForm.config-74T-eSSH.js");
89404
- i.formConfigOverride = e;
89416
+ r.formConfigOverride = e;
89405
89417
  }
89406
89418
  }
89407
- let e = i.pageName || i.tabName || i.title;
89408
- e && (document.title = e), r(i);
89409
- } else r("not-found");
89419
+ let e = r.pageName || r.tabName || r.title;
89420
+ e && (document.title = e), i(r);
89421
+ } else i("not-found");
89410
89422
  } catch {
89411
- r("not-found");
89423
+ i("not-found");
89412
89424
  } finally {
89413
- a(!1);
89425
+ o(!1);
89414
89426
  }
89415
89427
  })();
89416
89428
  }, [e.pathname]);
89417
- let [o, s] = x(!1);
89429
+ let [s, c] = x(!1);
89418
89430
  if (m(() => {
89419
89431
  let e;
89420
- return i ? e = setTimeout(() => s(!0), 150) : s(!1), () => clearTimeout(e);
89421
- }, [i]), i) return o ? t ? /* @__PURE__ */ V("main", {
89432
+ return a ? e = setTimeout(() => c(!0), 150) : c(!1), () => clearTimeout(e);
89433
+ }, [a]), a) return s ? t ? /* @__PURE__ */ V("main", {
89422
89434
  className: "flex-1 w-full max-w-7xl mx-auto p-4 md:p-8 z-10 flex flex-col",
89423
89435
  children: [/* @__PURE__ */ V("div", {
89424
89436
  className: "mb-6",
@@ -89434,7 +89446,7 @@ function yUn() {
89434
89446
  className: "flex-1 flex items-center justify-center min-h-[60vh] z-10",
89435
89447
  children: /* @__PURE__ */ B(K, { className: "w-6 h-6 animate-spin text-accent" })
89436
89448
  }) : null;
89437
- if (n === "not-found") return /* @__PURE__ */ B("main", {
89449
+ if (r === "not-found") return /* @__PURE__ */ B("main", {
89438
89450
  className: "flex-1 w-full max-w-7xl mx-auto p-4 md:p-8 z-10 flex flex-col pt-12 min-h-screen",
89439
89451
  children: /* @__PURE__ */ V("div", {
89440
89452
  className: "flex flex-col items-center justify-center flex-1 text-center",
@@ -89481,53 +89493,53 @@ function yUn() {
89481
89493
  ]
89482
89494
  })
89483
89495
  });
89484
- if ((n.pageId === "support" || n.isPrivate) && !t) return /* @__PURE__ */ B(be, {
89496
+ if ((r.pageId === "support" || r.isPrivate) && !t) return /* @__PURE__ */ B(be, {
89485
89497
  to: "/login",
89486
89498
  state: { from: e }
89487
89499
  });
89488
- let c = n.pageName?.toLowerCase().replace(/\s+/g, "_") || n.pageId || "custom_page", l = `pub_${c}`;
89489
- if (n.template === "none") return n.isAdmin ? /* @__PURE__ */ B(_Un, { config: n }) : n.isShared ? /* @__PURE__ */ B(vUn, { config: n }) : n.isPrivate ? /* @__PURE__ */ B(gUn, { config: n }) : /* @__PURE__ */ B(hUn, { config: n });
89490
- if (n.template === "board") {
89491
- let e = n.isAdmin ? `admin_${n.pageId || c}` : n.isShared ? `mem_${n.pageId || c}` : n.isPrivate ? `user_${n.pageId || c}` : l;
89492
- return /* @__PURE__ */ B(JVn, {
89493
- title: n.tabTitle || n.tabName || n.title || n.pageName || "Board",
89494
- subtitle: n.route,
89500
+ let l = r.pageName?.toLowerCase().replace(/\s+/g, "_") || r.pageId || "custom_page", u = `pub_${l}`;
89501
+ if (r.template === "none") return r.isAdmin ? /* @__PURE__ */ B(_Un, { config: r }) : r.isShared ? /* @__PURE__ */ B(vUn, { config: r }) : r.isPrivate ? /* @__PURE__ */ B(gUn, { config: r }) : /* @__PURE__ */ B(hUn, { config: r });
89502
+ if (r.template === "board") {
89503
+ let e = r.isAdmin ? `admin_${r.pageId || l}` : r.isShared ? `mem_${r.pageId || l}` : r.isPrivate ? `user_${r.pageId || l}` : u;
89504
+ return /* @__PURE__ */ B(n.components?.TemplateBoard || JVn, {
89505
+ title: r.tabTitle || r.tabName || r.title || r.pageName || "Board",
89506
+ subtitle: r.route,
89495
89507
  tasksCollection: `${e}_tasks`,
89496
89508
  categoriesCollection: `${e}_categories`,
89497
- config: n
89509
+ config: r
89498
89510
  });
89499
89511
  }
89500
- if (n.template === "table") {
89501
- let e = n.isAdmin ? `admin_${n.pageId || c}` : n.isShared ? `mem_${n.pageId || c}` : n.isPrivate ? `user_${n.pageId || c}` : l;
89502
- return /* @__PURE__ */ B($Hn, {
89503
- title: n.tabTitle || n.tabName || n.title || n.pageName || "Table",
89504
- subtitle: n.route,
89512
+ if (r.template === "table") {
89513
+ let e = r.isAdmin ? `admin_${r.pageId || l}` : r.isShared ? `mem_${r.pageId || l}` : r.isPrivate ? `user_${r.pageId || l}` : u;
89514
+ return /* @__PURE__ */ B(n.components?.TemplateTable || $Hn, {
89515
+ title: r.tabTitle || r.tabName || r.title || r.pageName || "Table",
89516
+ subtitle: r.route,
89505
89517
  tableCollection: `${e}_table`,
89506
- config: n
89518
+ config: r
89507
89519
  });
89508
89520
  }
89509
- if (n.template === "calendar") {
89510
- let e = n.isAdmin ? `admin_${n.pageId || c}` : n.isShared ? `mem_${n.pageId || c}` : n.isPrivate ? `user_${n.pageId || c}` : l;
89511
- return /* @__PURE__ */ B(tUn, {
89512
- title: n.tabTitle || n.tabName || n.title || n.pageName || "Calendar",
89513
- subtitle: n.route,
89521
+ if (r.template === "calendar") {
89522
+ let e = r.isAdmin ? `admin_${r.pageId || l}` : r.isShared ? `mem_${r.pageId || l}` : r.isPrivate ? `user_${r.pageId || l}` : u;
89523
+ return /* @__PURE__ */ B(n.components?.TemplateCalendar || tUn, {
89524
+ title: r.tabTitle || r.tabName || r.title || r.pageName || "Calendar",
89525
+ subtitle: r.route,
89514
89526
  eventsCollection: `${e}_events`,
89515
- defaultTimeFormat: n.defaultTimeFormat || n.defaulttimeformat || "12h",
89516
- config: n
89527
+ defaultTimeFormat: r.defaultTimeFormat || r.defaulttimeformat || "12h",
89528
+ config: r
89517
89529
  });
89518
89530
  }
89519
- return n.template === "inline_form" ? /* @__PURE__ */ B("div", {
89531
+ return r.template === "inline_form" ? /* @__PURE__ */ B("div", {
89520
89532
  className: "flex-1 flex flex-col items-center justify-start mt-8 relative z-10 w-full min-h-[60vh]",
89521
89533
  children: /* @__PURE__ */ B(Es, {
89522
89534
  isInline: !0,
89523
- formId: n.form,
89524
- formConfig: n.formConfigOverride
89535
+ formId: r.form,
89536
+ formConfig: r.formConfigOverride
89525
89537
  })
89526
89538
  }) : /* @__PURE__ */ B("div", {
89527
89539
  className: "flex-1 flex flex-col items-center justify-center relative z-10 w-full min-h-[60vh]",
89528
89540
  children: /* @__PURE__ */ B(Es, {
89529
- formId: n.form,
89530
- formConfig: n.formConfigOverride,
89541
+ formId: r.form,
89542
+ formConfig: r.formConfigOverride,
89531
89543
  onClose: () => window.history.back()
89532
89544
  })
89533
89545
  });
@@ -89609,10 +89621,10 @@ function wUn() {
89609
89621
  let e = [];
89610
89622
  for (let { key: t, label: n } of SUn) {
89611
89623
  let r = CUn(t);
89612
- (!r || r.trim() === "") && e.push(n);
89624
+ !r || r.trim() === "" ? (console.log("Missing key:", t, r), e.push(n)) : console.log("Found key:", t, r);
89613
89625
  }
89614
89626
  let t = CUn("VITE_ADMIN_EMAILS"), n = !t || t.trim() === "";
89615
- return {
89627
+ return console.log("Setup complete?", e.length === 0, e), {
89616
89628
  allSet: e.length === 0,
89617
89629
  missing: e,
89618
89630
  missingAdminEmails: n
@@ -90265,6 +90277,7 @@ function PUn() {
90265
90277
  });
90266
90278
  }
90267
90279
  function FUn() {
90280
+ let e = Ze().components?.Home || fce;
90268
90281
  return /* @__PURE__ */ B(eee, { children: /* @__PURE__ */ V("div", {
90269
90282
  className: "min-h-screen relative flex flex-col overflow-x-hidden",
90270
90283
  children: [
@@ -90272,7 +90285,7 @@ function FUn() {
90272
90285
  /* @__PURE__ */ V(xe, { children: [
90273
90286
  /* @__PURE__ */ B(U, {
90274
90287
  path: "/",
90275
- element: /* @__PURE__ */ B(Q9, { children: /* @__PURE__ */ B(fce, {}) })
90288
+ element: /* @__PURE__ */ B(Q9, { children: /* @__PURE__ */ B(e, {}) })
90276
90289
  }),
90277
90290
  /* @__PURE__ */ B(U, {
90278
90291
  path: "/register",
@@ -90364,7 +90377,10 @@ function FUn() {
90364
90377
  }),
90365
90378
  /* @__PURE__ */ B(U, {
90366
90379
  path: "/setup",
90367
- element: /* @__PURE__ */ B(jUn, {})
90380
+ element: /* @__PURE__ */ B(be, {
90381
+ to: "/",
90382
+ replace: !0
90383
+ })
90368
90384
  }),
90369
90385
  /* @__PURE__ */ B(U, {
90370
90386
  path: "*",
@@ -90397,30 +90413,19 @@ function RUn(e) {
90397
90413
  firebaseConfig: e.firebaseConfig,
90398
90414
  adminEmails: e.adminEmails,
90399
90415
  basename: e.basename,
90400
- onAuthChange: e.onAuthChange
90416
+ onAuthChange: e.onAuthChange,
90417
+ themeConfig: e.themeConfig,
90418
+ components: e.components
90401
90419
  },
90402
- children: /* @__PURE__ */ B(qe, {
90403
- scopeSelector: ".firebase-os",
90404
- children: /* @__PURE__ */ V("div", {
90405
- className: "firebase-os",
90406
- style: {
90407
- position: "fixed",
90408
- inset: 0,
90409
- width: "100vw",
90410
- height: "100vh",
90411
- zIndex: 9999,
90412
- overflowY: "auto",
90413
- overflowX: "hidden",
90414
- textAlign: "left",
90415
- margin: 0,
90416
- padding: 0,
90417
- background: "var(--bg-gradient)",
90418
- color: "var(--fg-color)"
90419
- },
90420
- children: [/* @__PURE__ */ B("style", { children: "\n /* Defensive Typography Reset: Specificity 0,0,0 */\n /* This ensures we provide base styles without overriding ANY utility classes */\n :where(.firebase-os) :where(h1, h2, h3, h4, h5, h6, p, span, li, label, a, td, th) {\n color: var(--fg-color);\n font-family: var(--font-family, 'Inter', system-ui, sans-serif);\n }\n :where(.firebase-os) :where(h1, h2, h3, h4, h5, h6, p) {\n margin: 0;\n }\n :where(.firebase-os) :where(code, pre) {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n background: transparent;\n border: none;\n }\n " }), /* @__PURE__ */ B(IUn, {})]
90421
- })
90422
- })
90420
+ children: /* @__PURE__ */ B(qe, { children: /* @__PURE__ */ B("div", {
90421
+ className: "firebase-os flex-1 flex flex-col min-h-screen",
90422
+ style: {
90423
+ background: "var(--bg-gradient)",
90424
+ color: "var(--fg-color)"
90425
+ },
90426
+ children: /* @__PURE__ */ B(IUn, {})
90427
+ }) })
90423
90428
  });
90424
90429
  }
90425
90430
  //#endregion
90426
- export { RUn as FirebaseOS, $e as useAuth, Ze as useConfig, Je as useTheme };
90431
+ export { Es as ContactPopup, RUn as FirebaseOS, Ie as auth, W as db, Ns as defaultHomeConfig, Le as storage, $e as useAuth, Ze as useConfig, Je as useTheme };
package/dist/index.d.ts CHANGED
@@ -4,3 +4,6 @@ export { useAuth } from './lib/AuthContext';
4
4
  export { useTheme } from './lib/ThemeContext';
5
5
  export { useConfig } from './lib/ConfigContext';
6
6
  export type { FirebaseOSConfig } from './lib/ConfigContext';
7
+ export { db, auth, storage } from './lib/firebase';
8
+ export { ContactPopup } from './components/ContactPopup';
9
+ export { defaultHomeConfig } from './configs/pages/home.config';
@@ -11,6 +11,18 @@ export interface FirebaseOSConfig {
11
11
  adminEmails?: string[];
12
12
  basename?: string;
13
13
  onAuthChange?: (user: any) => void;
14
+ themeConfig?: any;
15
+ components?: {
16
+ Home?: React.ComponentType<any>;
17
+ TemplateBoard?: React.ComponentType<any>;
18
+ TemplateTable?: React.ComponentType<any>;
19
+ TemplateCalendar?: React.ComponentType<any>;
20
+ TemplatePopupForm?: React.ComponentType<any>;
21
+ TemplateInlineForm?: React.ComponentType<any>;
22
+ TemplatePages?: React.ComponentType<any>;
23
+ TemplateStorage?: React.ComponentType<any>;
24
+ TemplateNone?: React.ComponentType<any>;
25
+ };
14
26
  }
15
27
  export declare function ConfigProvider({ config, children }: {
16
28
  config: FirebaseOSConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-os",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "A complete Firebase-powered admin OS — one React component.",
5
5
  "type": "module",
6
6
  "main": "dist/firebase-os.cjs.js",
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "files": [
23
23
  "dist",
24
- "scripts"
24
+ "scripts",
25
+ "src"
25
26
  ],
26
27
  "scripts": {
27
28
  "dev": "vite",
@@ -81,22 +81,54 @@ createRoot(document.getElementById('root')!).render(
81
81
  }
82
82
  }
83
83
 
84
- // ── 3. Reveal the CSS by copying it to the consumer's src directory ──────────
85
- const sourceCssPath = path.join(__dirname, '..', 'dist', 'firebase-os.css');
86
- const targetCssPath = path.join(srcDir, 'firebase-os.css');
84
+ // ── 3. Hybrid Architecture: Create local overrides directory ─────────────
85
+ const fbosDir = path.join(srcDir, 'firebase-os');
86
+ if (!fs.existsSync(fbosDir)) fs.mkdirSync(fbosDir, { recursive: true });
87
+
88
+ // Copy raw index.css to give user full control over Tailwind and variables
89
+ const sourceCssPath = path.join(__dirname, '..', 'src', 'index.css');
87
90
  if (fs.existsSync(sourceCssPath)) {
88
- fs.copyFileSync(sourceCssPath, targetCssPath);
89
- console.log(' ✓ Copied firebase-os.css to your src directory');
91
+ fs.copyFileSync(sourceCssPath, path.join(fbosDir, 'theme.css'));
92
+ console.log(' ✓ Copied theme.css to your src/firebase-os directory');
93
+ }
94
+
95
+ // Copy configurations
96
+ const configsDir = path.join(fbosDir, 'configs');
97
+ if (!fs.existsSync(configsDir)) fs.mkdirSync(configsDir, { recursive: true });
98
+
99
+ const copyConfig = (srcPath, destName) => {
100
+ const fullSrc = path.join(__dirname, '..', 'src', 'configs', srcPath);
101
+ if (fs.existsSync(fullSrc)) {
102
+ fs.copyFileSync(fullSrc, path.join(configsDir, destName));
103
+ }
104
+ };
105
+ copyConfig('theme.config.ts', 'theme.config.ts');
106
+ copyConfig('pages/home.config.ts', 'home.config.ts');
107
+ copyConfig('forms/contactForm.config.ts', 'contactForm.config.ts');
108
+ copyConfig('forms/supportForm.config.ts', 'supportForm.config.ts');
109
+ console.log(' ✓ Copied configuration files to your src/firebase-os/configs directory');
110
+
111
+ // Read and rewrite Home.tsx
112
+ const sourceHomePath = path.join(__dirname, '..', 'src', 'pages', 'Home.tsx');
113
+ if (fs.existsSync(sourceHomePath)) {
114
+ let homeContent = fs.readFileSync(sourceHomePath, 'utf8');
115
+ homeContent = homeContent.replace(/from '\.\.\/components\/ContactPopup'/g, "from 'firebase-os'");
116
+ homeContent = homeContent.replace(/from '\.\.\/lib\/firebase'/g, "from 'firebase-os'");
117
+ homeContent = homeContent.replace(/from '\.\.\/configs\/pages\/home\.config'/g, "from './configs/home.config'");
118
+ fs.writeFileSync(path.join(fbosDir, 'Home.tsx'), homeContent);
119
+ console.log(' ✓ Created local Home.tsx template override');
90
120
  }
91
121
 
92
- // ── 4. Write App.tsx with FirebaseOS already wired up ───────────────────────
122
+ // ── 4. Write App.tsx with Hybrid Overrides Wired Up ───────────────────────
93
123
  const appPath = path.join(srcDir, 'App.tsx');
94
124
  const appContent = `import { FirebaseOS } from 'firebase-os';
95
- import './firebase-os.css';
125
+ import './firebase-os/theme.css';
126
+
127
+ // Local Hybrid Overrides
128
+ import { Home } from './firebase-os/Home';
129
+ import { themeConfig } from './firebase-os/configs/theme.config';
96
130
 
97
131
  // Vite injects these from your .env file at dev/build time.
98
- // The library can't read them directly since it's pre-built,
99
- // so we pass them as props.
100
132
  const firebaseConfig = {
101
133
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY || '',
102
134
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN || '',
@@ -116,21 +148,57 @@ export default function App() {
116
148
  <FirebaseOS
117
149
  firebaseConfig={firebaseConfig}
118
150
  adminEmails={adminEmails}
151
+ themeConfig={themeConfig}
152
+ components={{
153
+ Home: Home
154
+ }}
119
155
  />
120
156
  );
121
157
  }
122
158
  `;
123
159
 
124
- // Only write if the file doesn't already import FirebaseOS
125
160
  const existingApp = fs.existsSync(appPath) ? fs.readFileSync(appPath, 'utf8') : '';
126
161
  if (!existingApp.includes('firebase-os')) {
127
162
  fs.writeFileSync(appPath, appContent);
128
- console.log(' ✓ Wrote App.tsx with <FirebaseOS /> ready to go');
163
+ console.log(' ✓ Wrote App.tsx with <FirebaseOS /> hybrid architecture ready to go');
129
164
  } else {
130
165
  console.log(' ℹ App.tsx already uses firebase-os — skipped');
131
166
  }
132
167
 
133
- // ── 4. Create .env file with placeholders ──────────────────────────────────
168
+ // ── 5. Setup Tailwind V4 in host app ───────────────────────────────────────
169
+ // We'll update the user's package.json to include tailwindcss dependencies
170
+ const pkgPath = path.join(consumerRoot, 'package.json');
171
+ if (fs.existsSync(pkgPath)) {
172
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
173
+ let pkgModified = false;
174
+ pkg.devDependencies = pkg.devDependencies || {};
175
+
176
+ if (!pkg.devDependencies['tailwindcss']) {
177
+ pkg.devDependencies['tailwindcss'] = '^4.0.0';
178
+ pkgModified = true;
179
+ }
180
+ if (!pkg.devDependencies['@tailwindcss/vite']) {
181
+ pkg.devDependencies['@tailwindcss/vite'] = '^4.0.0';
182
+ pkgModified = true;
183
+ }
184
+ if (pkgModified) {
185
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
186
+ console.log(' ✓ Added tailwindcss and @tailwindcss/vite to your package.json');
187
+ }
188
+ }
189
+
190
+ const viteConfigPath = path.join(consumerRoot, 'vite.config.ts');
191
+ if (fs.existsSync(viteConfigPath)) {
192
+ let viteConfig = fs.readFileSync(viteConfigPath, 'utf8');
193
+ if (!viteConfig.includes('@tailwindcss/vite')) {
194
+ viteConfig = "import tailwindcss from '@tailwindcss/vite';\\n" + viteConfig;
195
+ viteConfig = viteConfig.replace('plugins: [', 'plugins: [\\n tailwindcss(),');
196
+ fs.writeFileSync(viteConfigPath, viteConfig);
197
+ console.log(' ✓ Patched vite.config.ts to enable Tailwind CSS v4');
198
+ }
199
+ }
200
+
201
+ // ── 6. Create .env file with placeholders ──────────────────────────────────
134
202
  const envPath = path.join(consumerRoot, '.env');
135
203
  if (!fs.existsSync(envPath)) {
136
204
  const envContent = `# 🔥 Firebase OS Configuration
@@ -145,10 +213,13 @@ VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
145
213
  VITE_FIREBASE_APP_ID=your_app_id
146
214
 
147
215
  # Comma-separated list of admin emails
148
- VITE_ADMIN_EMAILS=your_email@example.com
216
+ VITE_ADMIN_EMAILS=admin@example.com
149
217
  `;
150
218
  fs.writeFileSync(envPath, envContent);
151
- console.log(' ✓ Created .env file with configuration placeholders');
219
+ console.log(' ✓ Created .env template');
152
220
  }
153
221
 
154
- console.log('\n[firebase-os] Setup complete! Run `npm run dev` to start.\n');
222
+ console.log('\n[firebase-os] 🎉 Hybrid Setup complete! You now have full control over styling and configs.\n');
223
+ console.log('🚨 IMPORTANT NEXT STEPS:');
224
+ console.log('1. Run `npm install` again to install the required Tailwind CSS dependencies.');
225
+ console.log('2. Run `npm run dev` to start your app.\n');