@fluid-app/portal-sdk 0.1.63 → 0.1.65
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/{AppDownloadScreen-pm8fLxZX.mjs → AppDownloadScreen-BXS02LRb.mjs} +2 -2
- package/dist/AppDownloadScreen-BXS02LRb.mjs.map +1 -0
- package/dist/{AppDownloadScreen-oMzxBETr.cjs → AppDownloadScreen-BXWBVnQM.cjs} +2 -2
- package/dist/AppDownloadScreen-BXWBVnQM.cjs.map +1 -0
- package/dist/{AppDownloadScreen-DW3cBnbI.cjs → AppDownloadScreen-BtwySuUY.cjs} +1 -1
- package/dist/{AppDownloadScreen-DUGGDlr4.mjs → AppDownloadScreen-CC3PVGnN.mjs} +2 -1
- package/dist/{MySiteScreen-bUfo9RDR.mjs → MySiteScreen-DlbGFMlk.mjs} +1 -0
- package/dist/{OrdersScreen-CO9vm0Wj.mjs → OrdersScreen-h7KX2dQo.mjs} +1 -0
- package/dist/{ProductsScreen-GOW8lxiN.mjs → ProductsScreen-D90OAeDM.mjs} +2 -0
- package/dist/{ShareablesScreen-KFwyduqD.mjs → ShareablesScreen-CQbMDEkb.mjs} +2 -0
- package/dist/{ShopScreen-Bg696rqv.mjs → ShopScreen-CSpdAmLo.mjs} +2 -0
- package/dist/{SubscriptionsScreen-BVLHc7-p.mjs → SubscriptionsScreen-DjG0cmvn.mjs} +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +15 -15
- package/dist/vite/index.cjs +92 -76
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.d.cts +6 -11
- package/dist/vite/index.d.cts.map +1 -1
- package/dist/vite/index.d.mts +6 -11
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +92 -76
- package/dist/vite/index.mjs.map +1 -1
- package/package.json +13 -16
- package/dist/AppDownloadScreen-oMzxBETr.cjs.map +0 -1
- package/dist/AppDownloadScreen-pm8fLxZX.mjs.map +0 -1
|
@@ -24,7 +24,7 @@ function GooglePlayIcon() {
|
|
|
24
24
|
}
|
|
25
25
|
function AppStoreButtons({ appstoreUrl, playstoreUrl }) {
|
|
26
26
|
return /* @__PURE__ */ jsxs("div", {
|
|
27
|
-
className: `grid gap-4 ${playstoreUrl ? "grid-cols-2" : "grid-cols-1"}`,
|
|
27
|
+
className: `grid gap-4 ${playstoreUrl ? "grid-cols-1 sm:grid-cols-2" : "grid-cols-1"}`,
|
|
28
28
|
children: [/* @__PURE__ */ jsx("a", {
|
|
29
29
|
href: appstoreUrl,
|
|
30
30
|
target: "_blank",
|
|
@@ -208,4 +208,4 @@ function AppDownloadScreen() {
|
|
|
208
208
|
//#endregion
|
|
209
209
|
export { appDownloadScreenPropertySchema as n, AppDownloadScreen as t };
|
|
210
210
|
|
|
211
|
-
//# sourceMappingURL=AppDownloadScreen-
|
|
211
|
+
//# sourceMappingURL=AppDownloadScreen-BXS02LRb.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppDownloadScreen-BXS02LRb.mjs","names":["AppDownloadScreen","AppDownloadScreenUI"],"sources":["../../app-download-ui/src/components/AppStoreButtons.tsx","../../app-download-ui/src/components/QrCodeSection.tsx","../../app-download-ui/src/components/ImageSection.tsx","../../app-download-ui/src/lib/cn.ts","../../app-download-ui/src/components/AppDownloadScreen.tsx","../src/screens/AppDownloadScreen.tsx"],"sourcesContent":["import { ExternalLink } from \"lucide-react\";\n\nexport interface AppStoreButtonsProps {\n appstoreUrl: string;\n playstoreUrl?: string;\n}\n\nfunction AppleIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-62.1 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z\" />\n </svg>\n );\n}\n\nfunction GooglePlayIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 512 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M325.3 234.3L104.6 13l280.8 161.2-60.1 60.1zM47 0C34 6.8 25.3 19.2 25.3 35.3v441.3c0 16.1 8.7 28.5 21.7 35.3l256.6-256L47 0zm425.2 225.6l-58.9-34.1-65.7 64.5 65.7 64.5 60.1-34.1c18-14.3 18-46.5-1.2-60.8zM104.6 499l280.8-161.2-60.1-60.1L104.6 499z\" />\n </svg>\n );\n}\n\nexport function AppStoreButtons({\n appstoreUrl,\n playstoreUrl,\n}: AppStoreButtonsProps) {\n return (\n <div\n className={`grid gap-4 ${playstoreUrl ? \"grid-cols-1 sm:grid-cols-2\" : \"grid-cols-1\"}`}\n >\n <a\n href={appstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <AppleIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n App Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n\n {playstoreUrl && (\n <a\n href={playstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <GooglePlayIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n Play Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n )}\n </div>\n );\n}\n","export interface QrCodeSectionProps {\n companyId: number;\n}\n\nexport function QrCodeSection({ companyId }: QrCodeSectionProps) {\n const redirectUrl = `https://api.fluid.app/mobile_app_download/${companyId}`;\n const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(redirectUrl)}`;\n\n return (\n <a\n href={redirectUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-end justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Scan to\n <br />\n Download\n </div>\n </div>\n <div className=\"flex\">\n <img\n src={qrCodeUrl}\n alt=\"QR code for mobile app download\"\n className=\"h-16 w-16\"\n loading=\"lazy\"\n />\n </div>\n </div>\n </a>\n );\n}\n","const DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/iphone_product.png\";\n\nconst DEFAULT_LOGO =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport interface ImageSectionProps {\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n}\n\nexport function ImageSection({\n logoUrl,\n screenshotUrl,\n appName = \"App\",\n}: ImageSectionProps) {\n return (\n <div className=\"flex flex-col overflow-hidden\">\n <div className=\"-mb-px flex flex-1 flex-col\">\n <div className=\"flex h-20 px-3 pt-8\">\n <img\n src={logoUrl || DEFAULT_LOGO}\n alt={appName}\n width={150}\n height={150}\n className=\"object-contain\"\n />\n </div>\n <div\n className={\n screenshotUrl\n ? \"mt-8 flex flex-1 items-end justify-start\"\n : \"-ml-20 flex flex-1 items-end justify-start\"\n }\n >\n <img\n src={screenshotUrl || DEFAULT_IMAGE}\n alt=\"Mobile app interface\"\n width={480}\n height={500}\n className=\"h-full w-auto object-cover\"\n />\n </div>\n </div>\n </div>\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { AppStoreButtons } from \"./AppStoreButtons\";\nimport { QrCodeSection } from \"./QrCodeSection\";\nimport { ImageSection } from \"./ImageSection\";\nimport { cn } from \"../lib/cn\";\n\nconst DEFAULT_APPSTORE_URL =\n \"https://apps.apple.com/app/wecommerce/id1564463740\";\nconst DEFAULT_PLAYSTORE_URL =\n \"https://play.google.com/store/apps/details?id=com.fluid.crm\";\n\nexport interface AppDownloadScreenProps {\n companyId: number;\n appstoreUrl?: string | null;\n playstoreUrl?: string | null;\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n className?: string;\n}\n\nexport function AppDownloadScreen({\n companyId,\n appstoreUrl,\n playstoreUrl,\n logoUrl,\n screenshotUrl,\n appName,\n className,\n}: AppDownloadScreenProps) {\n const resolvedAppstoreUrl = appstoreUrl?.trim() || DEFAULT_APPSTORE_URL;\n const resolvedPlaystoreUrl = playstoreUrl?.trim() || DEFAULT_PLAYSTORE_URL;\n\n return (\n <div\n className={cn(\"flex h-full items-center justify-center p-4\", className)}\n >\n <div className=\"bg-muted relative mx-4 w-full max-w-6xl overflow-hidden rounded-xl px-6 shadow-xl\">\n <div className=\"grid h-150 w-full grid-cols-1 lg:grid-cols-2\">\n {/* Left side - Images */}\n <ImageSection\n logoUrl={logoUrl}\n screenshotUrl={screenshotUrl}\n appName={appName}\n />\n\n {/* Right side - Content */}\n <div className=\"space-y-2 pt-8 pb-8\">\n <h1 className=\"mt-12 text-left text-[32px] leading-tight font-black text-gray-900\">\n EVERYTHING YOU NEED\n </h1>\n <h1 className=\"-mt-2 text-left text-[32px] leading-tight font-black text-gray-900\">\n AT YOUR FINGERTIPS\n </h1>\n <p className=\"-mt-1 text-left text-[14px] leading-relaxed text-gray-600\">\n Everything you need at your fingertips. Run your business from one\n place while on the go. Share, message, sell, and manage from the\n mobile app.\n </p>\n\n <h3 className=\"mt-16 text-left text-[12px] font-bold text-gray-900\">\n Download the App\n </h3>\n\n <AppStoreButtons\n appstoreUrl={resolvedAppstoreUrl}\n playstoreUrl={resolvedPlaystoreUrl}\n />\n\n <QrCodeSection companyId={companyId} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { AppDownloadScreen as AppDownloadScreenUI } from \"@fluid-app/portal-app-download-ui\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\n\nexport const appDownloadScreenPropertySchema = {\n widgetType: \"AppDownloadScreen\",\n displayName: \"App Download Screen\",\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\nexport function AppDownloadScreen(): React.JSX.Element {\n const { data: user } = useCurrentUser();\n\n return (\n <AppDownloadScreenUI\n companyId={user?.company?.id ?? 0}\n appstoreUrl={user?.company?.appstore_url}\n playstoreUrl={user?.company?.playstore_url}\n logoUrl={user?.company?.logo_url}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -25,7 +25,7 @@ function GooglePlayIcon() {
|
|
|
25
25
|
}
|
|
26
26
|
function AppStoreButtons({ appstoreUrl, playstoreUrl }) {
|
|
27
27
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
28
|
-
className: `grid gap-4 ${playstoreUrl ? "grid-cols-2" : "grid-cols-1"}`,
|
|
28
|
+
className: `grid gap-4 ${playstoreUrl ? "grid-cols-1 sm:grid-cols-2" : "grid-cols-1"}`,
|
|
29
29
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
30
30
|
href: appstoreUrl,
|
|
31
31
|
target: "_blank",
|
|
@@ -220,4 +220,4 @@ Object.defineProperty(exports, "appDownloadScreenPropertySchema", {
|
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
-
//# sourceMappingURL=AppDownloadScreen-
|
|
223
|
+
//# sourceMappingURL=AppDownloadScreen-BXWBVnQM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppDownloadScreen-BXWBVnQM.cjs","names":["ExternalLink","AppDownloadScreen","useCurrentUser","AppDownloadScreenUI"],"sources":["../../app-download-ui/src/components/AppStoreButtons.tsx","../../app-download-ui/src/components/QrCodeSection.tsx","../../app-download-ui/src/components/ImageSection.tsx","../../app-download-ui/src/lib/cn.ts","../../app-download-ui/src/components/AppDownloadScreen.tsx","../src/screens/AppDownloadScreen.tsx"],"sourcesContent":["import { ExternalLink } from \"lucide-react\";\n\nexport interface AppStoreButtonsProps {\n appstoreUrl: string;\n playstoreUrl?: string;\n}\n\nfunction AppleIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-62.1 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z\" />\n </svg>\n );\n}\n\nfunction GooglePlayIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 512 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M325.3 234.3L104.6 13l280.8 161.2-60.1 60.1zM47 0C34 6.8 25.3 19.2 25.3 35.3v441.3c0 16.1 8.7 28.5 21.7 35.3l256.6-256L47 0zm425.2 225.6l-58.9-34.1-65.7 64.5 65.7 64.5 60.1-34.1c18-14.3 18-46.5-1.2-60.8zM104.6 499l280.8-161.2-60.1-60.1L104.6 499z\" />\n </svg>\n );\n}\n\nexport function AppStoreButtons({\n appstoreUrl,\n playstoreUrl,\n}: AppStoreButtonsProps) {\n return (\n <div\n className={`grid gap-4 ${playstoreUrl ? \"grid-cols-1 sm:grid-cols-2\" : \"grid-cols-1\"}`}\n >\n <a\n href={appstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <AppleIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n App Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n\n {playstoreUrl && (\n <a\n href={playstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <GooglePlayIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n Play Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n )}\n </div>\n );\n}\n","export interface QrCodeSectionProps {\n companyId: number;\n}\n\nexport function QrCodeSection({ companyId }: QrCodeSectionProps) {\n const redirectUrl = `https://api.fluid.app/mobile_app_download/${companyId}`;\n const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(redirectUrl)}`;\n\n return (\n <a\n href={redirectUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-end justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Scan to\n <br />\n Download\n </div>\n </div>\n <div className=\"flex\">\n <img\n src={qrCodeUrl}\n alt=\"QR code for mobile app download\"\n className=\"h-16 w-16\"\n loading=\"lazy\"\n />\n </div>\n </div>\n </a>\n );\n}\n","const DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/iphone_product.png\";\n\nconst DEFAULT_LOGO =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport interface ImageSectionProps {\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n}\n\nexport function ImageSection({\n logoUrl,\n screenshotUrl,\n appName = \"App\",\n}: ImageSectionProps) {\n return (\n <div className=\"flex flex-col overflow-hidden\">\n <div className=\"-mb-px flex flex-1 flex-col\">\n <div className=\"flex h-20 px-3 pt-8\">\n <img\n src={logoUrl || DEFAULT_LOGO}\n alt={appName}\n width={150}\n height={150}\n className=\"object-contain\"\n />\n </div>\n <div\n className={\n screenshotUrl\n ? \"mt-8 flex flex-1 items-end justify-start\"\n : \"-ml-20 flex flex-1 items-end justify-start\"\n }\n >\n <img\n src={screenshotUrl || DEFAULT_IMAGE}\n alt=\"Mobile app interface\"\n width={480}\n height={500}\n className=\"h-full w-auto object-cover\"\n />\n </div>\n </div>\n </div>\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { AppStoreButtons } from \"./AppStoreButtons\";\nimport { QrCodeSection } from \"./QrCodeSection\";\nimport { ImageSection } from \"./ImageSection\";\nimport { cn } from \"../lib/cn\";\n\nconst DEFAULT_APPSTORE_URL =\n \"https://apps.apple.com/app/wecommerce/id1564463740\";\nconst DEFAULT_PLAYSTORE_URL =\n \"https://play.google.com/store/apps/details?id=com.fluid.crm\";\n\nexport interface AppDownloadScreenProps {\n companyId: number;\n appstoreUrl?: string | null;\n playstoreUrl?: string | null;\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n className?: string;\n}\n\nexport function AppDownloadScreen({\n companyId,\n appstoreUrl,\n playstoreUrl,\n logoUrl,\n screenshotUrl,\n appName,\n className,\n}: AppDownloadScreenProps) {\n const resolvedAppstoreUrl = appstoreUrl?.trim() || DEFAULT_APPSTORE_URL;\n const resolvedPlaystoreUrl = playstoreUrl?.trim() || DEFAULT_PLAYSTORE_URL;\n\n return (\n <div\n className={cn(\"flex h-full items-center justify-center p-4\", className)}\n >\n <div className=\"bg-muted relative mx-4 w-full max-w-6xl overflow-hidden rounded-xl px-6 shadow-xl\">\n <div className=\"grid h-150 w-full grid-cols-1 lg:grid-cols-2\">\n {/* Left side - Images */}\n <ImageSection\n logoUrl={logoUrl}\n screenshotUrl={screenshotUrl}\n appName={appName}\n />\n\n {/* Right side - Content */}\n <div className=\"space-y-2 pt-8 pb-8\">\n <h1 className=\"mt-12 text-left text-[32px] leading-tight font-black text-gray-900\">\n EVERYTHING YOU NEED\n </h1>\n <h1 className=\"-mt-2 text-left text-[32px] leading-tight font-black text-gray-900\">\n AT YOUR FINGERTIPS\n </h1>\n <p className=\"-mt-1 text-left text-[14px] leading-relaxed text-gray-600\">\n Everything you need at your fingertips. Run your business from one\n place while on the go. Share, message, sell, and manage from the\n mobile app.\n </p>\n\n <h3 className=\"mt-16 text-left text-[12px] font-bold text-gray-900\">\n Download the App\n </h3>\n\n <AppStoreButtons\n appstoreUrl={resolvedAppstoreUrl}\n playstoreUrl={resolvedPlaystoreUrl}\n />\n\n <QrCodeSection companyId={companyId} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { AppDownloadScreen as AppDownloadScreenUI } from \"@fluid-app/portal-app-download-ui\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\n\nexport const appDownloadScreenPropertySchema = {\n widgetType: \"AppDownloadScreen\",\n displayName: \"App Download Screen\",\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\nexport function AppDownloadScreen(): React.JSX.Element {\n const { data: user } = useCurrentUser();\n\n return (\n <AppDownloadScreenUI\n companyId={user?.company?.id ?? 0}\n appstoreUrl={user?.company?.appstore_url}\n playstoreUrl={user?.company?.playstore_url}\n logoUrl={user?.company?.logo_url}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -32,6 +32,6 @@ require("./SpacerWidget-Br2IZFFv.cjs");
|
|
|
32
32
|
require("./TableWidget-Cri6r5Yr.cjs");
|
|
33
33
|
require("./ToDoWidget-Bv6POc5R.cjs");
|
|
34
34
|
require("./VideoWidget-DnnuIvW1.cjs");
|
|
35
|
-
const require_AppDownloadScreen = require("./AppDownloadScreen-
|
|
35
|
+
const require_AppDownloadScreen = require("./AppDownloadScreen-BXWBVnQM.cjs");
|
|
36
36
|
exports.AppDownloadScreen = require_AppDownloadScreen.AppDownloadScreen;
|
|
37
37
|
exports.appDownloadScreenPropertySchema = require_AppDownloadScreen.appDownloadScreenPropertySchema;
|
|
@@ -31,5 +31,6 @@ import "./SpacerWidget-DgVU58BC.mjs";
|
|
|
31
31
|
import "./TableWidget-DjYabx1e.mjs";
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
|
-
import
|
|
34
|
+
import "./use-current-user-DnBpWIw6.mjs";
|
|
35
|
+
import { n as appDownloadScreenPropertySchema, t as AppDownloadScreen } from "./AppDownloadScreen-BXS02LRb.mjs";
|
|
35
36
|
export { AppDownloadScreen, appDownloadScreenPropertySchema };
|
|
@@ -32,6 +32,7 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
36
|
import "./sortable.esm-DreCqRxJ.mjs";
|
|
36
37
|
import { n as mySiteScreenPropertySchema, t as MySiteScreen } from "./MySiteScreen-DR_xY95c.mjs";
|
|
37
38
|
export { MySiteScreen, mySiteScreenPropertySchema };
|
|
@@ -32,6 +32,7 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
36
|
import "./order-detail-DkMYJvzl.mjs";
|
|
36
37
|
import { n as ordersScreenPropertySchema, t as OrdersScreen } from "./OrdersScreen-CQzgIEa4.mjs";
|
|
37
38
|
export { OrdersScreen, ordersScreenPropertySchema };
|
|
@@ -32,6 +32,8 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./use-current-user-DnBpWIw6.mjs";
|
|
36
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
37
|
import "./dist-CMGXkSgZ.mjs";
|
|
36
38
|
import "./es-CrIkZTQ3.mjs";
|
|
37
39
|
import "./dist-Cl4FsM3V.mjs";
|
|
@@ -32,6 +32,8 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./use-current-user-DnBpWIw6.mjs";
|
|
36
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
37
|
import "./dist-CMGXkSgZ.mjs";
|
|
36
38
|
import "./es-CrIkZTQ3.mjs";
|
|
37
39
|
import "./dist-Cl4FsM3V.mjs";
|
|
@@ -32,5 +32,7 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./use-current-user-DnBpWIw6.mjs";
|
|
36
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
37
|
import { n as shopScreenPropertySchema, t as ShopScreen } from "./ShopScreen-DFWQY8hT.mjs";
|
|
36
38
|
export { ShopScreen, shopScreenPropertySchema };
|
|
@@ -32,6 +32,7 @@ import "./TableWidget-DjYabx1e.mjs";
|
|
|
32
32
|
import "./ToDoWidget-Cmvf7I6c.mjs";
|
|
33
33
|
import "./VideoWidget-51MxnPRZ.mjs";
|
|
34
34
|
import "./ScreenHeaderContext-CrdfLGKk.mjs";
|
|
35
|
+
import "./AppNavigationContext-DJeNcP4Y.mjs";
|
|
35
36
|
import "./order-detail-DkMYJvzl.mjs";
|
|
36
37
|
import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-50w7SgWX.mjs";
|
|
37
38
|
import "./src-BakNjVTk.mjs";
|
package/dist/index.cjs
CHANGED
|
@@ -56,7 +56,7 @@ require("./dist-oTn1xy1Z.cjs");
|
|
|
56
56
|
const require_ProductsScreen = require("./ProductsScreen-BgVl8GAm.cjs");
|
|
57
57
|
const require_MySiteScreen = require("./MySiteScreen-C-cS0_qZ.cjs");
|
|
58
58
|
require("./UpgradeScreen-BbP-0Pcg.cjs");
|
|
59
|
-
require("./AppDownloadScreen-
|
|
59
|
+
require("./AppDownloadScreen-BXWBVnQM.cjs");
|
|
60
60
|
let react = require("react");
|
|
61
61
|
react = require_chunk.__toESM(react);
|
|
62
62
|
let _tanstack_react_query = require("@tanstack/react-query");
|
|
@@ -2414,7 +2414,7 @@ const ProductsScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => requ
|
|
|
2414
2414
|
const ShareablesScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ShareablesScreen-8rSANBth.cjs")).then((m) => ({ default: m.ShareablesScreen })));
|
|
2415
2415
|
const MySiteScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./MySiteScreen-B_UnnWDX.cjs")).then((m) => ({ default: m.MySiteScreen })));
|
|
2416
2416
|
const UpgradeScreen = (0, react.lazy)(() => Promise.resolve().then(() => require("./UpgradeScreen-DJKNVL-O.cjs")).then((m) => ({ default: m.UpgradeScreen })));
|
|
2417
|
-
const AppDownloadScreen = (0, react.lazy)(() => Promise.resolve().then(() => require("./AppDownloadScreen-
|
|
2417
|
+
const AppDownloadScreen = (0, react.lazy)(() => Promise.resolve().then(() => require("./AppDownloadScreen-BtwySuUY.cjs")).then((m) => ({ default: m.AppDownloadScreen })));
|
|
2418
2418
|
const SYSTEM_SLUG_SCREEN_MAP = {
|
|
2419
2419
|
profile: ProfileScreen$1,
|
|
2420
2420
|
orders: OrdersScreen$1,
|
|
@@ -3825,7 +3825,7 @@ const screenPropertySchemas = {
|
|
|
3825
3825
|
ShareablesScreen: () => Promise.resolve().then(() => require("./ShareablesScreen-8rSANBth.cjs")).then((m) => m.shareablesScreenPropertySchema),
|
|
3826
3826
|
ShopScreen: () => Promise.resolve().then(() => require("./ShopScreen-DCKr0cbB.cjs")).then((m) => m.shopScreenPropertySchema),
|
|
3827
3827
|
UpgradeScreen: () => Promise.resolve().then(() => require("./UpgradeScreen-DJKNVL-O.cjs")).then((m) => m.upgradeScreenPropertySchema),
|
|
3828
|
-
AppDownloadScreen: () => Promise.resolve().then(() => require("./AppDownloadScreen-
|
|
3828
|
+
AppDownloadScreen: () => Promise.resolve().then(() => require("./AppDownloadScreen-BtwySuUY.cjs")).then((m) => m.appDownloadScreenPropertySchema)
|
|
3829
3829
|
};
|
|
3830
3830
|
/**
|
|
3831
3831
|
* Core page template IDs
|
package/dist/index.mjs
CHANGED
|
@@ -54,7 +54,7 @@ import "./sortable.esm-DreCqRxJ.mjs";
|
|
|
54
54
|
import { n as productsScreenPropertySchema, t as ProductsScreen } from "./ProductsScreen-B0BsPJth.mjs";
|
|
55
55
|
import { n as mySiteScreenPropertySchema, t as MySiteScreen } from "./MySiteScreen-DR_xY95c.mjs";
|
|
56
56
|
import "./UpgradeScreen-D7LfdVSJ.mjs";
|
|
57
|
-
import "./AppDownloadScreen-
|
|
57
|
+
import "./AppDownloadScreen-BXS02LRb.mjs";
|
|
58
58
|
import * as React$1 from "react";
|
|
59
59
|
import { StrictMode, Suspense, createContext, forwardRef, lazy, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
60
60
|
import { useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
@@ -2402,17 +2402,17 @@ function AccountManageLayout({ children }) {
|
|
|
2402
2402
|
//#endregion
|
|
2403
2403
|
//#region src/shell/system-screen-map.ts
|
|
2404
2404
|
const ProfileScreen$1 = lazy(() => import("./ProfileScreen-Tcqc5WfU.mjs").then((m) => ({ default: m.ProfileScreen })));
|
|
2405
|
-
const OrdersScreen$1 = lazy(() => import("./OrdersScreen-
|
|
2406
|
-
const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-
|
|
2405
|
+
const OrdersScreen$1 = lazy(() => import("./OrdersScreen-h7KX2dQo.mjs").then((m) => ({ default: m.OrdersScreen })));
|
|
2406
|
+
const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-DjG0cmvn.mjs").then((m) => ({ default: m.SubscriptionsScreen })));
|
|
2407
2407
|
const MessagingScreen$1 = lazy(() => import("./MessagingScreen-CGS474IP.mjs").then((m) => ({ default: m.MessagingScreen })));
|
|
2408
2408
|
const ContactsScreen$1 = lazy(() => import("./ContactsScreen-ruymx9Pi.mjs").then((m) => ({ default: m.ContactsScreen })));
|
|
2409
|
-
const ShopScreen$1 = lazy(() => import("./ShopScreen-
|
|
2409
|
+
const ShopScreen$1 = lazy(() => import("./ShopScreen-CSpdAmLo.mjs").then((m) => ({ default: m.ShopScreen })));
|
|
2410
2410
|
const CustomersScreen$1 = lazy(() => import("./CustomersScreen-D22G27ru.mjs").then((n) => n.n).then((m) => ({ default: m.CustomersScreen })));
|
|
2411
|
-
const ProductsScreen$1 = lazy(() => import("./ProductsScreen-
|
|
2412
|
-
const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-
|
|
2413
|
-
const MySiteScreen$1 = lazy(() => import("./MySiteScreen-
|
|
2411
|
+
const ProductsScreen$1 = lazy(() => import("./ProductsScreen-D90OAeDM.mjs").then((m) => ({ default: m.ProductsScreen })));
|
|
2412
|
+
const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-CQbMDEkb.mjs").then((m) => ({ default: m.ShareablesScreen })));
|
|
2413
|
+
const MySiteScreen$1 = lazy(() => import("./MySiteScreen-DlbGFMlk.mjs").then((m) => ({ default: m.MySiteScreen })));
|
|
2414
2414
|
const UpgradeScreen = lazy(() => import("./UpgradeScreen-D7LfdVSJ.mjs").then((n) => n.t).then((m) => ({ default: m.UpgradeScreen })));
|
|
2415
|
-
const AppDownloadScreen = lazy(() => import("./AppDownloadScreen-
|
|
2415
|
+
const AppDownloadScreen = lazy(() => import("./AppDownloadScreen-CC3PVGnN.mjs").then((m) => ({ default: m.AppDownloadScreen })));
|
|
2416
2416
|
const SYSTEM_SLUG_SCREEN_MAP = {
|
|
2417
2417
|
profile: ProfileScreen$1,
|
|
2418
2418
|
orders: OrdersScreen$1,
|
|
@@ -3815,15 +3815,15 @@ const screenPropertySchemas = {
|
|
|
3815
3815
|
ProfileScreen: () => import("./ProfileScreen-Tcqc5WfU.mjs").then((m) => m.profileScreenPropertySchema),
|
|
3816
3816
|
MessagingScreen: () => import("./MessagingScreen-CGS474IP.mjs").then((m) => m.messagingScreenPropertySchema),
|
|
3817
3817
|
ContactsScreen: () => import("./ContactsScreen-ruymx9Pi.mjs").then((m) => m.contactsScreenPropertySchema),
|
|
3818
|
-
OrdersScreen: () => import("./OrdersScreen-
|
|
3819
|
-
SubscriptionsScreen: () => import("./SubscriptionsScreen-
|
|
3818
|
+
OrdersScreen: () => import("./OrdersScreen-h7KX2dQo.mjs").then((m) => m.ordersScreenPropertySchema),
|
|
3819
|
+
SubscriptionsScreen: () => import("./SubscriptionsScreen-DjG0cmvn.mjs").then((m) => m.subscriptionsScreenPropertySchema),
|
|
3820
3820
|
CustomersScreen: () => import("./CustomersScreen-D22G27ru.mjs").then((n) => n.n).then((m) => m.customersScreenPropertySchema),
|
|
3821
|
-
ProductsScreen: () => import("./ProductsScreen-
|
|
3822
|
-
MySiteScreen: () => import("./MySiteScreen-
|
|
3823
|
-
ShareablesScreen: () => import("./ShareablesScreen-
|
|
3824
|
-
ShopScreen: () => import("./ShopScreen-
|
|
3821
|
+
ProductsScreen: () => import("./ProductsScreen-D90OAeDM.mjs").then((m) => m.productsScreenPropertySchema),
|
|
3822
|
+
MySiteScreen: () => import("./MySiteScreen-DlbGFMlk.mjs").then((m) => m.mySiteScreenPropertySchema),
|
|
3823
|
+
ShareablesScreen: () => import("./ShareablesScreen-CQbMDEkb.mjs").then((m) => m.shareablesScreenPropertySchema),
|
|
3824
|
+
ShopScreen: () => import("./ShopScreen-CSpdAmLo.mjs").then((m) => m.shopScreenPropertySchema),
|
|
3825
3825
|
UpgradeScreen: () => import("./UpgradeScreen-D7LfdVSJ.mjs").then((n) => n.t).then((m) => m.upgradeScreenPropertySchema),
|
|
3826
|
-
AppDownloadScreen: () => import("./AppDownloadScreen-
|
|
3826
|
+
AppDownloadScreen: () => import("./AppDownloadScreen-CC3PVGnN.mjs").then((m) => m.appDownloadScreenPropertySchema)
|
|
3827
3827
|
};
|
|
3828
3828
|
/**
|
|
3829
3829
|
* Core page template IDs
|
package/dist/vite/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../chunk-DAgNkxik.cjs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
3
5
|
//#region src/vite/validate-manifest.ts
|
|
4
6
|
/**
|
|
5
7
|
* Lightweight manifest validation for the SDK vite plugin.
|
|
@@ -89,76 +91,6 @@ function validateManifest(input) {
|
|
|
89
91
|
};
|
|
90
92
|
}
|
|
91
93
|
//#endregion
|
|
92
|
-
//#region src/vite/manifest-plugin.ts
|
|
93
|
-
/**
|
|
94
|
-
* Vite plugin that serves widget manifest metadata.
|
|
95
|
-
*
|
|
96
|
-
* Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.
|
|
97
|
-
* Re-reads portal.config.ts on every request (HMR-aware).
|
|
98
|
-
*
|
|
99
|
-
* Build mode: emits an empty __manifests__.json as a static asset in dist/.
|
|
100
|
-
* The CLI extraction utility (extract-manifests.ts) handles build-time
|
|
101
|
-
* extraction via tsx subprocess for `fluid build` and `fluid deploy`.
|
|
102
|
-
*
|
|
103
|
-
* The builder fetches this endpoint/file to discover custom widgets.
|
|
104
|
-
*/
|
|
105
|
-
function fluidManifestPlugin() {
|
|
106
|
-
return {
|
|
107
|
-
name: "fluid-manifest-plugin",
|
|
108
|
-
configureServer(server) {
|
|
109
|
-
server.middlewares.use("/__manifests__", async (_req, res) => {
|
|
110
|
-
try {
|
|
111
|
-
const serializable = await loadManifests(server, server.config.logger);
|
|
112
|
-
res.setHeader("Content-Type", "application/json");
|
|
113
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
114
|
-
res.end(JSON.stringify(serializable));
|
|
115
|
-
} catch (err) {
|
|
116
|
-
server.config.logger.error(`[fluid] Failed to load manifests: ${err}`);
|
|
117
|
-
res.statusCode = 500;
|
|
118
|
-
res.end(JSON.stringify({ error: String(err) }));
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
},
|
|
122
|
-
generateBundle() {
|
|
123
|
-
this.warn("[fluid] fluidManifestPlugin: emitting empty __manifests__.json. Run `fluid build` instead of `vite build` to include widget manifests.");
|
|
124
|
-
this.emitFile({
|
|
125
|
-
type: "asset",
|
|
126
|
-
fileName: "__manifests__.json",
|
|
127
|
-
source: JSON.stringify([])
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.
|
|
134
|
-
* Validates each manifest before stripping the `component` field.
|
|
135
|
-
* Returns an empty array if the config module or export is missing/invalid.
|
|
136
|
-
*/
|
|
137
|
-
async function loadManifests(server, logger) {
|
|
138
|
-
let mod;
|
|
139
|
-
try {
|
|
140
|
-
mod = await server.ssrLoadModule("/src/portal.config.ts");
|
|
141
|
-
} catch (err) {
|
|
142
|
-
logger?.warn(`[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
143
|
-
return [];
|
|
144
|
-
}
|
|
145
|
-
const rawWidgets = mod.customWidgets;
|
|
146
|
-
if (!rawWidgets) return [];
|
|
147
|
-
if (!Array.isArray(rawWidgets)) {
|
|
148
|
-
logger?.warn(`[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`);
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
const manifests = rawWidgets;
|
|
152
|
-
if (logger) for (const manifest of manifests) {
|
|
153
|
-
const result = validateManifest(manifest);
|
|
154
|
-
if (!result.success) {
|
|
155
|
-
const type = manifest.type ?? "unknown";
|
|
156
|
-
logger.warn(`[fluid] Invalid manifest for "${type}":\n` + result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n"));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return manifests.map(({ component: _component, ...rest }) => rest);
|
|
160
|
-
}
|
|
161
|
-
//#endregion
|
|
162
94
|
//#region src/vite/builder-preview-plugin.ts
|
|
163
95
|
const VIRTUAL_ENTRY_ID = "virtual:builder-preview-entry";
|
|
164
96
|
const RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ENTRY_ID;
|
|
@@ -174,8 +106,7 @@ const RAW_HTML = `<!doctype html>
|
|
|
174
106
|
</head>
|
|
175
107
|
<body>
|
|
176
108
|
<div id="builder-preview-root"></div>
|
|
177
|
-
<script type="module" src="/@id/
|
|
178
|
-
<script type="module">import "/src/index.css";<\/script>
|
|
109
|
+
<script type="module" src="/@id/virtual:builder-preview-entry"><\/script>
|
|
179
110
|
</body>
|
|
180
111
|
</html>`;
|
|
181
112
|
/**
|
|
@@ -183,19 +114,29 @@ const RAW_HTML = `<!doctype html>
|
|
|
183
114
|
*
|
|
184
115
|
* Dev mode only. Renders all customWidgets from portal.config.ts with
|
|
185
116
|
* a live preview and property editor — no auth, no iframe, no fluid-admin needed.
|
|
186
|
-
*
|
|
187
|
-
* Uses a virtual module to bridge the user's portal.config.ts into the preview app.
|
|
188
117
|
*/
|
|
189
118
|
function fluidBuilderPreviewPlugin() {
|
|
119
|
+
let configPath;
|
|
120
|
+
let cssPath;
|
|
190
121
|
return {
|
|
191
|
-
name: "fluid-builder-preview
|
|
122
|
+
name: "fluid-builder-preview",
|
|
192
123
|
apply: "serve",
|
|
124
|
+
configResolved(config) {
|
|
125
|
+
const root = config.root;
|
|
126
|
+
configPath = ["src/portal.config.ts", "portal.config.ts"].find((c) => (0, node_fs.existsSync)((0, node_path.join)(root, c))) ?? "src/portal.config.ts";
|
|
127
|
+
cssPath = [
|
|
128
|
+
"src/index.css",
|
|
129
|
+
"src/styles/index.css",
|
|
130
|
+
"index.css"
|
|
131
|
+
].find((c) => (0, node_fs.existsSync)((0, node_path.join)(root, c))) ?? "src/index.css";
|
|
132
|
+
},
|
|
193
133
|
resolveId(id) {
|
|
194
134
|
if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;
|
|
195
135
|
},
|
|
196
136
|
load(id) {
|
|
197
137
|
if (id === RESOLVED_VIRTUAL_ID) return `
|
|
198
|
-
import
|
|
138
|
+
import "/${cssPath}";
|
|
139
|
+
import * as portalConfig from "/${configPath}";
|
|
199
140
|
import { createRoot } from "react-dom/client";
|
|
200
141
|
import { createElement } from "react";
|
|
201
142
|
import { BuilderPreviewApp } from "@fluid-app/portal-preview";
|
|
@@ -227,6 +168,81 @@ if (root) {
|
|
|
227
168
|
};
|
|
228
169
|
}
|
|
229
170
|
//#endregion
|
|
171
|
+
//#region src/vite/manifest-plugin.ts
|
|
172
|
+
/**
|
|
173
|
+
* Vite plugin bundle that serves widget manifest metadata and the builder preview.
|
|
174
|
+
*
|
|
175
|
+
* Returns an array of plugins:
|
|
176
|
+
* 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)
|
|
177
|
+
* 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)
|
|
178
|
+
*
|
|
179
|
+
* Every portal using `fluidManifestPlugin()` automatically gets the builder preview.
|
|
180
|
+
*/
|
|
181
|
+
function fluidManifestPlugin() {
|
|
182
|
+
return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];
|
|
183
|
+
}
|
|
184
|
+
function fluidManifestPluginInternal() {
|
|
185
|
+
let configPath;
|
|
186
|
+
return {
|
|
187
|
+
name: "fluid-manifest-plugin",
|
|
188
|
+
configResolved(config) {
|
|
189
|
+
const root = config.root;
|
|
190
|
+
configPath = "/" + (["src/portal.config.ts", "portal.config.ts"].find((c) => (0, node_fs.existsSync)((0, node_path.join)(root, c))) ?? "src/portal.config.ts");
|
|
191
|
+
},
|
|
192
|
+
configureServer(server) {
|
|
193
|
+
server.middlewares.use("/__manifests__", async (_req, res) => {
|
|
194
|
+
try {
|
|
195
|
+
const serializable = await loadManifests(server, server.config.logger, configPath);
|
|
196
|
+
res.setHeader("Content-Type", "application/json");
|
|
197
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
198
|
+
res.end(JSON.stringify(serializable));
|
|
199
|
+
} catch (err) {
|
|
200
|
+
server.config.logger.error(`[fluid] Failed to load manifests: ${err}`);
|
|
201
|
+
res.statusCode = 500;
|
|
202
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
generateBundle() {
|
|
207
|
+
this.warn("[fluid] fluidManifestPlugin: emitting empty __manifests__.json. Run `fluid build` instead of `vite build` to include widget manifests.");
|
|
208
|
+
this.emitFile({
|
|
209
|
+
type: "asset",
|
|
210
|
+
fileName: "__manifests__.json",
|
|
211
|
+
source: JSON.stringify([])
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.
|
|
218
|
+
* Validates each manifest before stripping the `component` field.
|
|
219
|
+
* Returns an empty array if the config module or export is missing/invalid.
|
|
220
|
+
*/
|
|
221
|
+
async function loadManifests(server, logger, portalConfigPath) {
|
|
222
|
+
let mod;
|
|
223
|
+
try {
|
|
224
|
+
mod = await server.ssrLoadModule(portalConfigPath ?? "/src/portal.config.ts");
|
|
225
|
+
} catch (err) {
|
|
226
|
+
logger?.warn(`[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
const rawWidgets = mod.customWidgets;
|
|
230
|
+
if (!rawWidgets) return [];
|
|
231
|
+
if (!Array.isArray(rawWidgets)) {
|
|
232
|
+
logger?.warn(`[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`);
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
const manifests = rawWidgets;
|
|
236
|
+
if (logger) for (const manifest of manifests) {
|
|
237
|
+
const result = validateManifest(manifest);
|
|
238
|
+
if (!result.success) {
|
|
239
|
+
const type = manifest.type ?? "unknown";
|
|
240
|
+
logger.warn(`[fluid] Invalid manifest for "${type}":\n` + result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n"));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return manifests.map(({ component: _component, ...rest }) => rest);
|
|
244
|
+
}
|
|
245
|
+
//#endregion
|
|
230
246
|
exports.fluidBuilderPreviewPlugin = fluidBuilderPreviewPlugin;
|
|
231
247
|
exports.fluidManifestPlugin = fluidManifestPlugin;
|
|
232
248
|
|
package/dist/vite/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"./validate-manifest\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\";\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n \"/\" +\n (candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\");\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n portalConfigPath?: string,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\n portalConfigPath ?? \"/src/portal.config.ts\",\n );\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n"],"mappings":"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"}
|
package/dist/vite/index.d.cts
CHANGED
|
@@ -2,18 +2,15 @@ import { Plugin } from "vite";
|
|
|
2
2
|
|
|
3
3
|
//#region src/vite/manifest-plugin.d.ts
|
|
4
4
|
/**
|
|
5
|
-
* Vite plugin that serves widget manifest metadata.
|
|
5
|
+
* Vite plugin bundle that serves widget manifest metadata and the builder preview.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Returns an array of plugins:
|
|
8
|
+
* 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)
|
|
9
|
+
* 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
* The CLI extraction utility (extract-manifests.ts) handles build-time
|
|
12
|
-
* extraction via tsx subprocess for `fluid build` and `fluid deploy`.
|
|
13
|
-
*
|
|
14
|
-
* The builder fetches this endpoint/file to discover custom widgets.
|
|
11
|
+
* Every portal using `fluidManifestPlugin()` automatically gets the builder preview.
|
|
15
12
|
*/
|
|
16
|
-
declare function fluidManifestPlugin(): Plugin;
|
|
13
|
+
declare function fluidManifestPlugin(): Plugin[];
|
|
17
14
|
//#endregion
|
|
18
15
|
//#region src/vite/builder-preview-plugin.d.ts
|
|
19
16
|
/**
|
|
@@ -21,8 +18,6 @@ declare function fluidManifestPlugin(): Plugin;
|
|
|
21
18
|
*
|
|
22
19
|
* Dev mode only. Renders all customWidgets from portal.config.ts with
|
|
23
20
|
* a live preview and property editor — no auth, no iframe, no fluid-admin needed.
|
|
24
|
-
*
|
|
25
|
-
* Uses a virtual module to bridge the user's portal.config.ts into the preview app.
|
|
26
21
|
*/
|
|
27
22
|
declare function fluidBuilderPreviewPlugin(): Plugin;
|
|
28
23
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"mappings":";;;;;AAeA
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA"}
|
package/dist/vite/index.d.mts
CHANGED
|
@@ -2,18 +2,15 @@ import { Plugin } from "vite";
|
|
|
2
2
|
|
|
3
3
|
//#region src/vite/manifest-plugin.d.ts
|
|
4
4
|
/**
|
|
5
|
-
* Vite plugin that serves widget manifest metadata.
|
|
5
|
+
* Vite plugin bundle that serves widget manifest metadata and the builder preview.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Returns an array of plugins:
|
|
8
|
+
* 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)
|
|
9
|
+
* 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
* The CLI extraction utility (extract-manifests.ts) handles build-time
|
|
12
|
-
* extraction via tsx subprocess for `fluid build` and `fluid deploy`.
|
|
13
|
-
*
|
|
14
|
-
* The builder fetches this endpoint/file to discover custom widgets.
|
|
11
|
+
* Every portal using `fluidManifestPlugin()` automatically gets the builder preview.
|
|
15
12
|
*/
|
|
16
|
-
declare function fluidManifestPlugin(): Plugin;
|
|
13
|
+
declare function fluidManifestPlugin(): Plugin[];
|
|
17
14
|
//#endregion
|
|
18
15
|
//#region src/vite/builder-preview-plugin.d.ts
|
|
19
16
|
/**
|
|
@@ -21,8 +18,6 @@ declare function fluidManifestPlugin(): Plugin;
|
|
|
21
18
|
*
|
|
22
19
|
* Dev mode only. Renders all customWidgets from portal.config.ts with
|
|
23
20
|
* a live preview and property editor — no auth, no iframe, no fluid-admin needed.
|
|
24
|
-
*
|
|
25
|
-
* Uses a virtual module to bridge the user's portal.config.ts into the preview app.
|
|
26
21
|
*/
|
|
27
22
|
declare function fluidBuilderPreviewPlugin(): Plugin;
|
|
28
23
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"mappings":";;;;;AAeA
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"mappings":";;;;;AAeA;;;;;;;iBAAgB,mBAAA,CAAA,GAAuB,MAAA;;;;;AAAvC;;;;iBCcgB,yBAAA,CAAA,GAA6B,MAAA"}
|
package/dist/vite/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
//#region src/vite/validate-manifest.ts
|
|
2
4
|
/**
|
|
3
5
|
* Lightweight manifest validation for the SDK vite plugin.
|
|
@@ -87,76 +89,6 @@ function validateManifest(input) {
|
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
//#endregion
|
|
90
|
-
//#region src/vite/manifest-plugin.ts
|
|
91
|
-
/**
|
|
92
|
-
* Vite plugin that serves widget manifest metadata.
|
|
93
|
-
*
|
|
94
|
-
* Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.
|
|
95
|
-
* Re-reads portal.config.ts on every request (HMR-aware).
|
|
96
|
-
*
|
|
97
|
-
* Build mode: emits an empty __manifests__.json as a static asset in dist/.
|
|
98
|
-
* The CLI extraction utility (extract-manifests.ts) handles build-time
|
|
99
|
-
* extraction via tsx subprocess for `fluid build` and `fluid deploy`.
|
|
100
|
-
*
|
|
101
|
-
* The builder fetches this endpoint/file to discover custom widgets.
|
|
102
|
-
*/
|
|
103
|
-
function fluidManifestPlugin() {
|
|
104
|
-
return {
|
|
105
|
-
name: "fluid-manifest-plugin",
|
|
106
|
-
configureServer(server) {
|
|
107
|
-
server.middlewares.use("/__manifests__", async (_req, res) => {
|
|
108
|
-
try {
|
|
109
|
-
const serializable = await loadManifests(server, server.config.logger);
|
|
110
|
-
res.setHeader("Content-Type", "application/json");
|
|
111
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
112
|
-
res.end(JSON.stringify(serializable));
|
|
113
|
-
} catch (err) {
|
|
114
|
-
server.config.logger.error(`[fluid] Failed to load manifests: ${err}`);
|
|
115
|
-
res.statusCode = 500;
|
|
116
|
-
res.end(JSON.stringify({ error: String(err) }));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
generateBundle() {
|
|
121
|
-
this.warn("[fluid] fluidManifestPlugin: emitting empty __manifests__.json. Run `fluid build` instead of `vite build` to include widget manifests.");
|
|
122
|
-
this.emitFile({
|
|
123
|
-
type: "asset",
|
|
124
|
-
fileName: "__manifests__.json",
|
|
125
|
-
source: JSON.stringify([])
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.
|
|
132
|
-
* Validates each manifest before stripping the `component` field.
|
|
133
|
-
* Returns an empty array if the config module or export is missing/invalid.
|
|
134
|
-
*/
|
|
135
|
-
async function loadManifests(server, logger) {
|
|
136
|
-
let mod;
|
|
137
|
-
try {
|
|
138
|
-
mod = await server.ssrLoadModule("/src/portal.config.ts");
|
|
139
|
-
} catch (err) {
|
|
140
|
-
logger?.warn(`[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
141
|
-
return [];
|
|
142
|
-
}
|
|
143
|
-
const rawWidgets = mod.customWidgets;
|
|
144
|
-
if (!rawWidgets) return [];
|
|
145
|
-
if (!Array.isArray(rawWidgets)) {
|
|
146
|
-
logger?.warn(`[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`);
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
const manifests = rawWidgets;
|
|
150
|
-
if (logger) for (const manifest of manifests) {
|
|
151
|
-
const result = validateManifest(manifest);
|
|
152
|
-
if (!result.success) {
|
|
153
|
-
const type = manifest.type ?? "unknown";
|
|
154
|
-
logger.warn(`[fluid] Invalid manifest for "${type}":\n` + result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n"));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return manifests.map(({ component: _component, ...rest }) => rest);
|
|
158
|
-
}
|
|
159
|
-
//#endregion
|
|
160
92
|
//#region src/vite/builder-preview-plugin.ts
|
|
161
93
|
const VIRTUAL_ENTRY_ID = "virtual:builder-preview-entry";
|
|
162
94
|
const RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ENTRY_ID;
|
|
@@ -172,8 +104,7 @@ const RAW_HTML = `<!doctype html>
|
|
|
172
104
|
</head>
|
|
173
105
|
<body>
|
|
174
106
|
<div id="builder-preview-root"></div>
|
|
175
|
-
<script type="module" src="/@id/
|
|
176
|
-
<script type="module">import "/src/index.css";<\/script>
|
|
107
|
+
<script type="module" src="/@id/virtual:builder-preview-entry"><\/script>
|
|
177
108
|
</body>
|
|
178
109
|
</html>`;
|
|
179
110
|
/**
|
|
@@ -181,19 +112,29 @@ const RAW_HTML = `<!doctype html>
|
|
|
181
112
|
*
|
|
182
113
|
* Dev mode only. Renders all customWidgets from portal.config.ts with
|
|
183
114
|
* a live preview and property editor — no auth, no iframe, no fluid-admin needed.
|
|
184
|
-
*
|
|
185
|
-
* Uses a virtual module to bridge the user's portal.config.ts into the preview app.
|
|
186
115
|
*/
|
|
187
116
|
function fluidBuilderPreviewPlugin() {
|
|
117
|
+
let configPath;
|
|
118
|
+
let cssPath;
|
|
188
119
|
return {
|
|
189
|
-
name: "fluid-builder-preview
|
|
120
|
+
name: "fluid-builder-preview",
|
|
190
121
|
apply: "serve",
|
|
122
|
+
configResolved(config) {
|
|
123
|
+
const root = config.root;
|
|
124
|
+
configPath = ["src/portal.config.ts", "portal.config.ts"].find((c) => existsSync(join(root, c))) ?? "src/portal.config.ts";
|
|
125
|
+
cssPath = [
|
|
126
|
+
"src/index.css",
|
|
127
|
+
"src/styles/index.css",
|
|
128
|
+
"index.css"
|
|
129
|
+
].find((c) => existsSync(join(root, c))) ?? "src/index.css";
|
|
130
|
+
},
|
|
191
131
|
resolveId(id) {
|
|
192
132
|
if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;
|
|
193
133
|
},
|
|
194
134
|
load(id) {
|
|
195
135
|
if (id === RESOLVED_VIRTUAL_ID) return `
|
|
196
|
-
import
|
|
136
|
+
import "/${cssPath}";
|
|
137
|
+
import * as portalConfig from "/${configPath}";
|
|
197
138
|
import { createRoot } from "react-dom/client";
|
|
198
139
|
import { createElement } from "react";
|
|
199
140
|
import { BuilderPreviewApp } from "@fluid-app/portal-preview";
|
|
@@ -225,6 +166,81 @@ if (root) {
|
|
|
225
166
|
};
|
|
226
167
|
}
|
|
227
168
|
//#endregion
|
|
169
|
+
//#region src/vite/manifest-plugin.ts
|
|
170
|
+
/**
|
|
171
|
+
* Vite plugin bundle that serves widget manifest metadata and the builder preview.
|
|
172
|
+
*
|
|
173
|
+
* Returns an array of plugins:
|
|
174
|
+
* 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)
|
|
175
|
+
* 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)
|
|
176
|
+
*
|
|
177
|
+
* Every portal using `fluidManifestPlugin()` automatically gets the builder preview.
|
|
178
|
+
*/
|
|
179
|
+
function fluidManifestPlugin() {
|
|
180
|
+
return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];
|
|
181
|
+
}
|
|
182
|
+
function fluidManifestPluginInternal() {
|
|
183
|
+
let configPath;
|
|
184
|
+
return {
|
|
185
|
+
name: "fluid-manifest-plugin",
|
|
186
|
+
configResolved(config) {
|
|
187
|
+
const root = config.root;
|
|
188
|
+
configPath = "/" + (["src/portal.config.ts", "portal.config.ts"].find((c) => existsSync(join(root, c))) ?? "src/portal.config.ts");
|
|
189
|
+
},
|
|
190
|
+
configureServer(server) {
|
|
191
|
+
server.middlewares.use("/__manifests__", async (_req, res) => {
|
|
192
|
+
try {
|
|
193
|
+
const serializable = await loadManifests(server, server.config.logger, configPath);
|
|
194
|
+
res.setHeader("Content-Type", "application/json");
|
|
195
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
196
|
+
res.end(JSON.stringify(serializable));
|
|
197
|
+
} catch (err) {
|
|
198
|
+
server.config.logger.error(`[fluid] Failed to load manifests: ${err}`);
|
|
199
|
+
res.statusCode = 500;
|
|
200
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
generateBundle() {
|
|
205
|
+
this.warn("[fluid] fluidManifestPlugin: emitting empty __manifests__.json. Run `fluid build` instead of `vite build` to include widget manifests.");
|
|
206
|
+
this.emitFile({
|
|
207
|
+
type: "asset",
|
|
208
|
+
fileName: "__manifests__.json",
|
|
209
|
+
source: JSON.stringify([])
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.
|
|
216
|
+
* Validates each manifest before stripping the `component` field.
|
|
217
|
+
* Returns an empty array if the config module or export is missing/invalid.
|
|
218
|
+
*/
|
|
219
|
+
async function loadManifests(server, logger, portalConfigPath) {
|
|
220
|
+
let mod;
|
|
221
|
+
try {
|
|
222
|
+
mod = await server.ssrLoadModule(portalConfigPath ?? "/src/portal.config.ts");
|
|
223
|
+
} catch (err) {
|
|
224
|
+
logger?.warn(`[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`);
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
const rawWidgets = mod.customWidgets;
|
|
228
|
+
if (!rawWidgets) return [];
|
|
229
|
+
if (!Array.isArray(rawWidgets)) {
|
|
230
|
+
logger?.warn(`[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`);
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
const manifests = rawWidgets;
|
|
234
|
+
if (logger) for (const manifest of manifests) {
|
|
235
|
+
const result = validateManifest(manifest);
|
|
236
|
+
if (!result.success) {
|
|
237
|
+
const type = manifest.type ?? "unknown";
|
|
238
|
+
logger.warn(`[fluid] Invalid manifest for "${type}":\n` + result.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n"));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return manifests.map(({ component: _component, ...rest }) => rest);
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
228
244
|
export { fluidBuilderPreviewPlugin, fluidManifestPlugin };
|
|
229
245
|
|
|
230
246
|
//# sourceMappingURL=index.mjs.map
|
package/dist/vite/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/manifest-plugin.ts","../../src/vite/builder-preview-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ViteDevServer, Logger } from \"vite\";\nimport { validateManifest } from \"./validate-manifest\";\n\n/**\n * Vite plugin that serves widget manifest metadata.\n *\n * Dev mode: middleware serves /__manifests__ dynamically via ssrLoadModule.\n * Re-reads portal.config.ts on every request (HMR-aware).\n *\n * Build mode: emits an empty __manifests__.json as a static asset in dist/.\n * The CLI extraction utility (extract-manifests.ts) handles build-time\n * extraction via tsx subprocess for `fluid build` and `fluid deploy`.\n *\n * The builder fetches this endpoint/file to discover custom widgets.\n */\nexport function fluidManifestPlugin(): Plugin {\n return {\n name: \"fluid-manifest-plugin\",\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\"/src/portal.config.ts\");\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n","import type { Plugin } from \"vite\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\nconst VIRTUAL_MODULE_URL = \"/@id/__x00__virtual:builder-preview-entry\";\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"${VIRTUAL_MODULE_URL}\"></script>\n <script type=\"module\">import \"/src/index.css\";</script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n *\n * Uses a virtual module to bridge the user's portal.config.ts into the preview app.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n return {\n name: \"fluid-builder-preview-plugin\",\n apply: \"serve\",\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport * as portalConfig from \"/src/portal.config.ts\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n"],"mappings":";;;;;;;;AAQA,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAWD,SAAgB,iBAAiB,OAAkC;CACjE,MAAM,SAA4B,EAAE;CACpC,MAAM,IAAI;AAEV,KAAI,CAAC,KAAK,OAAO,MAAM,SACrB,QAAO;EACL,SAAS;EACT,QAAQ,CAAC;GAAE,MAAM;GAAI,SAAS;GAA8B,CAAC;EAC9D;AAIH,MAAK,MAAM,OAAO;EAChB;EACA;EACA;EACA;EACA;EACD,CACC,KAAI,OAAO,EAAE,SAAS,YAAa,EAAE,KAAgB,WAAW,EAC9D,QAAO,KAAK;EACV,MAAM;EACN,SAAS,GAAG,IAAI;EACjB,CAAC;AAIN,KAAI,OAAO,EAAE,oBAAoB,YAAY,EAAE,kBAAkB,EAC/D,QAAO,KAAK;EACV,MAAM;EACN,SAAS;EACV,CAAC;AAGJ,KAAI,OAAO,EAAE,cAAc,WACzB,QAAO,KAAK;EACV,MAAM;EACN,SAAS;EACV,CAAC;CAIJ,MAAM,SAAS,EAAE;AACjB,KAAI,UAAU,OAAO,WAAW,UAAU;AACxC,MAAI,OAAO,OAAO,eAAe,YAAY,CAAC,OAAO,WACnD,QAAO,KAAK;GACV,MAAM;GACN,SAAS;GACV,CAAC;AAEJ,MAAI,OAAO,EAAE,SAAS,YAAY,OAAO,eAAe,EAAE,KACxD,QAAO,KAAK;GACV,MAAM;GACN,SAAS;GACV,CAAC;AAEJ,MAAI,MAAM,QAAQ,OAAO,OAAO,CAC9B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KAAK;GAC7C,MAAM,QAAQ,OAAO,OAAO;AAC5B,OAAI,CAAC,SAAS,OAAO,MAAM,SAAS,SAAU;AAC9C,OACE,CAAC,kBAAkB,SACjB,MAAM,KACP,CAED,QAAO,KAAK;IACV,MAAM,yBAAyB,EAAE;IACjC,SAAS,uBAAuB,MAAM,KAAK,kBAAkB,kBAAkB,KAAK,KAAK;IAC1F,CAAC;;;AAMV,QAAO,OAAO,WAAW,IAAI,EAAE,SAAS,MAAM,GAAG;EAAE,SAAS;EAAO;EAAQ;;;;;;;;;;;;;;;;ACzG7E,SAAgB,sBAA8B;AAC5C,QAAO;EACL,MAAM;EAEN,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,kBAAkB,OAAO,MAAM,QAAQ;AAC5D,QAAI;KACF,MAAM,eAAe,MAAM,cACzB,QACA,OAAO,OAAO,OACf;AAED,SAAI,UAAU,gBAAgB,mBAAmB;AACjD,SAAI,UAAU,+BAA+B,IAAI;AACjD,SAAI,IAAI,KAAK,UAAU,aAAa,CAAC;aAC9B,KAAK;AACZ,YAAO,OAAO,OAAO,MACnB,qCAAqC,MACtC;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;;KAEjD;;EAGJ,iBAAiB;AAGf,QAAK,KACH,yIAED;AACD,QAAK,SAAS;IACZ,MAAM;IACN,UAAU;IACV,QAAQ,KAAK,UAAU,EAAE,CAAC;IAC3B,CAAC;;EAEL;;;;;;;AAQH,eAAe,cACb,QACA,QACoB;CACpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,wBAAwB;UAClD,KAAK;AACZ,UAAQ,KACN,4CAA4C,eAAe,QAAQ,IAAI,UAAU,MAClF;AACD,SAAO,EAAE;;CAGX,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,WAAY,QAAO,EAAE;AAE1B,KAAI,CAAC,MAAM,QAAQ,WAAW,EAAE;AAC9B,UAAQ,KACN,qDAAqD,OAAO,WAAW,+BACxE;AACD,SAAO,EAAE;;CAGX,MAAM,YAAY;AAGlB,KAAI,OACF,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,SAAS,iBAAiB,SAAS;AACzC,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,OAAQ,SAA+B,QAAQ;AACrD,UAAO,KACL,iCAAiC,KAAK,QACpC,OAAO,OAAO,KAAK,MAAM,OAAO,EAAE,KAAK,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CACrE;;;AAKP,QAAO,UAAU,KACd,EAAE,WAAW,YAAY,GAAG,WAAoC,KAClE;;;;ACrGH,MAAM,mBAAmB;AACzB,MAAM,sBAAsB,OAAO;AAGnC,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;AAyBjB,SAAgB,4BAAoC;AAClD,QAAO;EACL,MAAM;EACN,OAAO;EAEP,UAAU,IAAI;AACZ,OAAI,OAAO,iBAAkB,QAAO;;EAGtC,KAAK,IAAI;AACP,OAAI,OAAO,oBACT,QAAO;;;;;;;;;;;;;;;EAiBX,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IAC/C,MAAM,YAAY,IAAI,OAAO,IAAI,MAAM,IAAI,CAAC;AAC5C,QAAI,aAAa,sBAAsB,aAAa,oBAClD,QAAO,MAAM;AACf,QAAI;KACF,MAAM,cAAc,MAAM,OAAO,mBAC/B,oBACA,SACD;AACD,SAAI,UAAU,gBAAgB,YAAY;AAC1C,SAAI,IAAI,YAAY;aACb,GAAG;AACV,YAAO,OAAO,OAAO,MACnB,4CAA4C,IAC7C;AACD,SAAI,aAAa;AACjB,SAAI,IAAI,iCAAiC;;KAE3C;;EAEL"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vite/validate-manifest.ts","../../src/vite/builder-preview-plugin.ts","../../src/vite/manifest-plugin.ts"],"sourcesContent":["/**\n * Lightweight manifest validation for the SDK vite plugin.\n *\n * Inlined here (rather than imported from @fluid-app/portal-core) because\n * portal-core is private and not published to npm. This avoids a runtime\n * ERR_MODULE_NOT_FOUND for portals installed from npm.\n */\n\nconst VALID_FIELD_TYPES = [\n \"text\",\n \"textarea\",\n \"number\",\n \"boolean\",\n \"select\",\n \"color\",\n \"range\",\n \"dataSource\",\n \"resource\",\n \"image\",\n \"alignment\",\n \"slider\",\n \"colorPicker\",\n \"sectionHeader\",\n \"separator\",\n \"buttonGroup\",\n \"colorSelect\",\n \"sectionLayoutSelect\",\n \"background\",\n \"contentPosition\",\n \"textSizeSelect\",\n \"cssUnit\",\n \"fontPicker\",\n \"stringArray\",\n \"borderRadius\",\n \"screenPicker\",\n] as const;\n\ninterface ValidationError {\n path: string;\n message: string;\n}\n\ntype ValidationResult =\n | { success: true }\n | { success: false; errors: ValidationError[] };\n\nexport function validateManifest(input: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const m = input as Record<string, unknown>;\n\n if (!m || typeof m !== \"object\") {\n return {\n success: false,\n errors: [{ path: \"\", message: \"Manifest must be an object\" }],\n };\n }\n\n // Required string fields\n for (const key of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n ]) {\n if (typeof m[key] !== \"string\" || (m[key] as string).length === 0) {\n errors.push({\n path: key,\n message: `${key} is required and must be a non-empty string`,\n });\n }\n }\n\n if (typeof m.manifestVersion !== \"number\" || m.manifestVersion < 1) {\n errors.push({\n path: \"manifestVersion\",\n message: \"manifestVersion must be a positive integer\",\n });\n }\n\n if (typeof m.component !== \"function\") {\n errors.push({\n path: \"component\",\n message: \"component must be a React component (function)\",\n });\n }\n\n // Property schema validation\n const schema = m.propertySchema as Record<string, unknown> | undefined;\n if (schema && typeof schema === \"object\") {\n if (typeof schema.widgetType !== \"string\" || !schema.widgetType) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"widgetType is required\",\n });\n }\n if (typeof m.type === \"string\" && schema.widgetType !== m.type) {\n errors.push({\n path: \"propertySchema.widgetType\",\n message: \"manifest.type must match manifest.propertySchema.widgetType\",\n });\n }\n if (Array.isArray(schema.fields)) {\n for (let i = 0; i < schema.fields.length; i++) {\n const field = schema.fields[i] as Record<string, unknown>;\n if (!field || typeof field.type !== \"string\") continue;\n if (\n !VALID_FIELD_TYPES.includes(\n field.type as (typeof VALID_FIELD_TYPES)[number],\n )\n ) {\n errors.push({\n path: `propertySchema.fields.${i}.type`,\n message: `Invalid field type \"${field.type}\". Valid types: ${VALID_FIELD_TYPES.join(\", \")}`,\n });\n }\n }\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:builder-preview-entry\";\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ENTRY_ID;\n\nconst RAW_HTML = `<!doctype html>\n<html lang=\"en\" data-theme-mode=\"dark\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Custom Widget Preview</title>\n <style>\n body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }\n </style>\n </head>\n <body>\n <div id=\"builder-preview-root\"></div>\n <script type=\"module\" src=\"/@id/virtual:builder-preview-entry\"></script>\n </body>\n</html>`;\n\n/**\n * Vite plugin that serves a standalone widget preview page at /builder-preview.\n *\n * Dev mode only. Renders all customWidgets from portal.config.ts with\n * a live preview and property editor — no auth, no iframe, no fluid-admin needed.\n */\nexport function fluidBuilderPreviewPlugin(): Plugin {\n let configPath: string;\n let cssPath: string;\n\n return {\n name: \"fluid-builder-preview\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\";\n const cssCandidates = [\n \"src/index.css\",\n \"src/styles/index.css\",\n \"index.css\",\n ];\n cssPath =\n cssCandidates.find((c) => existsSync(join(root, c))) ?? \"src/index.css\";\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_ID) return RESOLVED_VIRTUAL_ID;\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `\nimport \"/${cssPath}\";\nimport * as portalConfig from \"/${configPath}\";\nimport { createRoot } from \"react-dom/client\";\nimport { createElement } from \"react\";\nimport { BuilderPreviewApp } from \"@fluid-app/portal-preview\";\n\nconst widgets = portalConfig.customWidgets || [];\nconst root = document.getElementById(\"builder-preview-root\");\nif (root) {\n createRoot(root).render(\n createElement(BuilderPreviewApp, { widgets })\n );\n}\n`;\n }\n },\n\n configureServer(server) {\n server.middlewares.use(async (req, res, next) => {\n const pathname = (req.url ?? \"\").split(\"?\")[0];\n if (pathname !== \"/builder-preview\" && pathname !== \"/builder-preview/\")\n return next();\n try {\n const transformed = await server.transformIndexHtml(\n \"/builder-preview\",\n RAW_HTML,\n );\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformed);\n } catch (e) {\n server.config.logger.error(\n `[fluid] Failed to serve builder preview: ${e}`,\n );\n res.statusCode = 500;\n res.end(\"Builder preview failed to load\");\n }\n });\n },\n };\n}\n","import type { Plugin, ResolvedConfig, ViteDevServer, Logger } from \"vite\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { validateManifest } from \"./validate-manifest\";\nimport { fluidBuilderPreviewPlugin } from \"./builder-preview-plugin\";\n\n/**\n * Vite plugin bundle that serves widget manifest metadata and the builder preview.\n *\n * Returns an array of plugins:\n * 1. Manifest plugin — serves /__manifests__ (dev) and emits __manifests__.json (build)\n * 2. Builder preview plugin — serves /builder-preview with live widget editing (dev only)\n *\n * Every portal using `fluidManifestPlugin()` automatically gets the builder preview.\n */\nexport function fluidManifestPlugin(): Plugin[] {\n return [fluidManifestPluginInternal(), fluidBuilderPreviewPlugin()];\n}\n\nfunction fluidManifestPluginInternal(): Plugin {\n let configPath: string;\n\n return {\n name: \"fluid-manifest-plugin\",\n\n configResolved(config: ResolvedConfig) {\n const root = config.root;\n const candidates = [\"src/portal.config.ts\", \"portal.config.ts\"];\n configPath =\n \"/\" +\n (candidates.find((c) => existsSync(join(root, c))) ??\n \"src/portal.config.ts\");\n },\n\n configureServer(server) {\n server.middlewares.use(\"/__manifests__\", async (_req, res) => {\n try {\n const serializable = await loadManifests(\n server,\n server.config.logger,\n configPath,\n );\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.end(JSON.stringify(serializable));\n } catch (err) {\n server.config.logger.error(\n `[fluid] Failed to load manifests: ${err}`,\n );\n res.statusCode = 500;\n res.end(JSON.stringify({ error: String(err) }));\n }\n });\n },\n\n generateBundle() {\n // Build mode: emit placeholder. The CLI extraction utility handles\n // actual build-time manifest extraction via tsx subprocess.\n this.warn(\n \"[fluid] fluidManifestPlugin: emitting empty __manifests__.json. \" +\n \"Run `fluid build` instead of `vite build` to include widget manifests.\",\n );\n this.emitFile({\n type: \"asset\",\n fileName: \"__manifests__.json\",\n source: JSON.stringify([]),\n });\n },\n };\n}\n\n/**\n * Load and serialize manifests from portal.config.ts via Vite's ssrLoadModule.\n * Validates each manifest before stripping the `component` field.\n * Returns an empty array if the config module or export is missing/invalid.\n */\nasync function loadManifests(\n server: ViteDevServer,\n logger?: Logger,\n portalConfigPath?: string,\n): Promise<unknown[]> {\n let mod: Record<string, unknown>;\n try {\n mod = await server.ssrLoadModule(\n portalConfigPath ?? \"/src/portal.config.ts\",\n );\n } catch (err) {\n logger?.warn(\n `[fluid] Could not load portal.config.ts: ${err instanceof Error ? err.message : err}`,\n );\n return [];\n }\n\n const rawWidgets = mod.customWidgets;\n if (!rawWidgets) return [];\n\n if (!Array.isArray(rawWidgets)) {\n logger?.warn(\n `[fluid] customWidgets export is not an array (got ${typeof rawWidgets}). Skipping manifest serving.`,\n );\n return [];\n }\n\n const manifests = rawWidgets as Record<string, unknown>[];\n\n // Validate full manifests (with component) before stripping\n if (logger) {\n for (const manifest of manifests) {\n const result = validateManifest(manifest);\n if (!result.success) {\n const type = (manifest as { type?: string }).type ?? \"unknown\";\n logger.warn(\n `[fluid] Invalid manifest for \"${type}\":\\n` +\n result.errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\"),\n );\n }\n }\n }\n\n return manifests.map(\n ({ component: _component, ...rest }: Record<string, unknown>) => rest,\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/portal-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.65",
|
|
4
4
|
"description": "SDK for building custom Fluid portals",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
"recharts": "^2.15.4",
|
|
44
44
|
"tailwind-merge": "^3.0.2",
|
|
45
45
|
"tw-animate-css": "^1.4.0",
|
|
46
|
-
"use-sync-external-store": "^1.4.0"
|
|
46
|
+
"use-sync-external-store": "^1.4.0",
|
|
47
|
+
"@fluid-app/portal-preview": "0.1.0",
|
|
48
|
+
"@fluid-app/portal-core": "0.1.23"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -64,28 +66,27 @@
|
|
|
64
66
|
"tsdown": "^0.21.0",
|
|
65
67
|
"typescript": "^5",
|
|
66
68
|
"zod": "4.3.5",
|
|
67
|
-
"@fluid-app/auth": "0.1.0",
|
|
68
69
|
"@fluid-app/api-client-core": "0.1.0",
|
|
69
|
-
"@fluid-app/company-switcher-ui": "0.1.0",
|
|
70
70
|
"@fluid-app/company-switcher-core": "0.1.0",
|
|
71
|
+
"@fluid-app/auth": "0.1.0",
|
|
72
|
+
"@fluid-app/company-switcher-ui": "0.1.0",
|
|
71
73
|
"@fluid-app/contacts-ui": "0.1.0",
|
|
74
|
+
"@fluid-app/file-picker-api-client": "0.1.0",
|
|
72
75
|
"@fluid-app/fluid-pay-api-client": "0.1.0",
|
|
73
76
|
"@fluid-app/fluidos-api-client": "0.1.0",
|
|
74
77
|
"@fluid-app/messaging-api-client": "0.1.0",
|
|
75
78
|
"@fluid-app/messaging-core": "0.1.0",
|
|
76
79
|
"@fluid-app/messaging-ui": "0.1.0",
|
|
77
|
-
"@fluid-app/file-picker-api-client": "0.1.0",
|
|
78
80
|
"@fluid-app/mysite-ui": "0.1.0",
|
|
79
81
|
"@fluid-app/orders-api-client": "0.1.0",
|
|
80
82
|
"@fluid-app/orders-core": "0.1.0",
|
|
81
83
|
"@fluid-app/orders-ui": "0.1.0",
|
|
82
|
-
"@fluid-app/permissions": "0.1.0",
|
|
83
84
|
"@fluid-app/portal-app-download-ui": "0.1.0",
|
|
85
|
+
"@fluid-app/permissions": "0.1.0",
|
|
84
86
|
"@fluid-app/portal-preview": "0.1.0",
|
|
85
|
-
"@fluid-app/portal-core": "0.1.23",
|
|
86
87
|
"@fluid-app/portal-pro-upgrade-ui": "0.1.0",
|
|
87
|
-
"@fluid-app/portal-react": "0.1.0",
|
|
88
88
|
"@fluid-app/portal-widgets": "0.1.22",
|
|
89
|
+
"@fluid-app/portal-react": "0.1.0",
|
|
89
90
|
"@fluid-app/products-api-client": "0.1.0",
|
|
90
91
|
"@fluid-app/products-core": "0.1.0",
|
|
91
92
|
"@fluid-app/profile-core": "0.1.0",
|
|
@@ -93,13 +94,13 @@
|
|
|
93
94
|
"@fluid-app/query-persister": "0.1.0",
|
|
94
95
|
"@fluid-app/shareables-api-client": "0.1.0",
|
|
95
96
|
"@fluid-app/shareables-core": "0.1.0",
|
|
96
|
-
"@fluid-app/shop-ui": "0.1.0",
|
|
97
97
|
"@fluid-app/shareables-ui": "0.1.0",
|
|
98
98
|
"@fluid-app/subscriptions-api-client": "0.1.0",
|
|
99
|
-
"@fluid-app/
|
|
100
|
-
"@fluid-app/ui-primitives": "0.1.13",
|
|
99
|
+
"@fluid-app/shop-ui": "0.1.0",
|
|
101
100
|
"@fluid-app/subscriptions-core": "0.1.0",
|
|
101
|
+
"@fluid-app/subscriptions-ui": "0.1.0",
|
|
102
102
|
"@fluid-app/typescript-config": "0.0.0",
|
|
103
|
+
"@fluid-app/ui-primitives": "0.1.13",
|
|
103
104
|
"@fluid-app/user-contacts-api-client": "0.1.0",
|
|
104
105
|
"@fluid-app/user-tasks-api-client": "0.1.0",
|
|
105
106
|
"@fluid-app/user-notes-api-client": "0.1.0"
|
|
@@ -112,13 +113,9 @@
|
|
|
112
113
|
"react-dom": ">=18.0.0",
|
|
113
114
|
"react-hook-form": ">=7.0.0",
|
|
114
115
|
"vite": ">=5.0.0",
|
|
115
|
-
"zod": "4.3.5"
|
|
116
|
-
"@fluid-app/portal-core": "0.1.23"
|
|
116
|
+
"zod": "4.3.5"
|
|
117
117
|
},
|
|
118
118
|
"peerDependenciesMeta": {
|
|
119
|
-
"@fluid-app/portal-core": {
|
|
120
|
-
"optional": true
|
|
121
|
-
},
|
|
122
119
|
"vite": {
|
|
123
120
|
"optional": true
|
|
124
121
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppDownloadScreen-oMzxBETr.cjs","names":["ExternalLink","AppDownloadScreen","useCurrentUser","AppDownloadScreenUI"],"sources":["../../app-download-ui/src/components/AppStoreButtons.tsx","../../app-download-ui/src/components/QrCodeSection.tsx","../../app-download-ui/src/components/ImageSection.tsx","../../app-download-ui/src/lib/cn.ts","../../app-download-ui/src/components/AppDownloadScreen.tsx","../src/screens/AppDownloadScreen.tsx"],"sourcesContent":["import { ExternalLink } from \"lucide-react\";\n\nexport interface AppStoreButtonsProps {\n appstoreUrl: string;\n playstoreUrl?: string;\n}\n\nfunction AppleIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-62.1 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z\" />\n </svg>\n );\n}\n\nfunction GooglePlayIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 512 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M325.3 234.3L104.6 13l280.8 161.2-60.1 60.1zM47 0C34 6.8 25.3 19.2 25.3 35.3v441.3c0 16.1 8.7 28.5 21.7 35.3l256.6-256L47 0zm425.2 225.6l-58.9-34.1-65.7 64.5 65.7 64.5 60.1-34.1c18-14.3 18-46.5-1.2-60.8zM104.6 499l280.8-161.2-60.1-60.1L104.6 499z\" />\n </svg>\n );\n}\n\nexport function AppStoreButtons({\n appstoreUrl,\n playstoreUrl,\n}: AppStoreButtonsProps) {\n return (\n <div\n className={`grid gap-4 ${playstoreUrl ? \"grid-cols-2\" : \"grid-cols-1\"}`}\n >\n <a\n href={appstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <AppleIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n App Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n\n {playstoreUrl && (\n <a\n href={playstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <GooglePlayIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n Play Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n )}\n </div>\n );\n}\n","export interface QrCodeSectionProps {\n companyId: number;\n}\n\nexport function QrCodeSection({ companyId }: QrCodeSectionProps) {\n const redirectUrl = `https://api.fluid.app/mobile_app_download/${companyId}`;\n const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(redirectUrl)}`;\n\n return (\n <a\n href={redirectUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-end justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Scan to\n <br />\n Download\n </div>\n </div>\n <div className=\"flex\">\n <img\n src={qrCodeUrl}\n alt=\"QR code for mobile app download\"\n className=\"h-16 w-16\"\n loading=\"lazy\"\n />\n </div>\n </div>\n </a>\n );\n}\n","const DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/iphone_product.png\";\n\nconst DEFAULT_LOGO =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport interface ImageSectionProps {\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n}\n\nexport function ImageSection({\n logoUrl,\n screenshotUrl,\n appName = \"App\",\n}: ImageSectionProps) {\n return (\n <div className=\"flex flex-col overflow-hidden\">\n <div className=\"-mb-px flex flex-1 flex-col\">\n <div className=\"flex h-20 px-3 pt-8\">\n <img\n src={logoUrl || DEFAULT_LOGO}\n alt={appName}\n width={150}\n height={150}\n className=\"object-contain\"\n />\n </div>\n <div\n className={\n screenshotUrl\n ? \"mt-8 flex flex-1 items-end justify-start\"\n : \"-ml-20 flex flex-1 items-end justify-start\"\n }\n >\n <img\n src={screenshotUrl || DEFAULT_IMAGE}\n alt=\"Mobile app interface\"\n width={480}\n height={500}\n className=\"h-full w-auto object-cover\"\n />\n </div>\n </div>\n </div>\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { AppStoreButtons } from \"./AppStoreButtons\";\nimport { QrCodeSection } from \"./QrCodeSection\";\nimport { ImageSection } from \"./ImageSection\";\nimport { cn } from \"../lib/cn\";\n\nconst DEFAULT_APPSTORE_URL =\n \"https://apps.apple.com/app/wecommerce/id1564463740\";\nconst DEFAULT_PLAYSTORE_URL =\n \"https://play.google.com/store/apps/details?id=com.fluid.crm\";\n\nexport interface AppDownloadScreenProps {\n companyId: number;\n appstoreUrl?: string | null;\n playstoreUrl?: string | null;\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n className?: string;\n}\n\nexport function AppDownloadScreen({\n companyId,\n appstoreUrl,\n playstoreUrl,\n logoUrl,\n screenshotUrl,\n appName,\n className,\n}: AppDownloadScreenProps) {\n const resolvedAppstoreUrl = appstoreUrl?.trim() || DEFAULT_APPSTORE_URL;\n const resolvedPlaystoreUrl = playstoreUrl?.trim() || DEFAULT_PLAYSTORE_URL;\n\n return (\n <div\n className={cn(\"flex h-full items-center justify-center p-4\", className)}\n >\n <div className=\"bg-muted relative mx-4 w-full max-w-6xl overflow-hidden rounded-xl px-6 shadow-xl\">\n <div className=\"grid h-150 w-full grid-cols-1 lg:grid-cols-2\">\n {/* Left side - Images */}\n <ImageSection\n logoUrl={logoUrl}\n screenshotUrl={screenshotUrl}\n appName={appName}\n />\n\n {/* Right side - Content */}\n <div className=\"space-y-2 pt-8 pb-8\">\n <h1 className=\"mt-12 text-left text-[32px] leading-tight font-black text-gray-900\">\n EVERYTHING YOU NEED\n </h1>\n <h1 className=\"-mt-2 text-left text-[32px] leading-tight font-black text-gray-900\">\n AT YOUR FINGERTIPS\n </h1>\n <p className=\"-mt-1 text-left text-[14px] leading-relaxed text-gray-600\">\n Everything you need at your fingertips. Run your business from one\n place while on the go. Share, message, sell, and manage from the\n mobile app.\n </p>\n\n <h3 className=\"mt-16 text-left text-[12px] font-bold text-gray-900\">\n Download the App\n </h3>\n\n <AppStoreButtons\n appstoreUrl={resolvedAppstoreUrl}\n playstoreUrl={resolvedPlaystoreUrl}\n />\n\n <QrCodeSection companyId={companyId} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { AppDownloadScreen as AppDownloadScreenUI } from \"@fluid-app/portal-app-download-ui\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\n\nexport const appDownloadScreenPropertySchema = {\n widgetType: \"AppDownloadScreen\",\n displayName: \"App Download Screen\",\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\nexport function AppDownloadScreen(): React.JSX.Element {\n const { data: user } = useCurrentUser();\n\n return (\n <AppDownloadScreenUI\n companyId={user?.company?.id ?? 0}\n appstoreUrl={user?.company?.appstore_url}\n playstoreUrl={user?.company?.playstore_url}\n logoUrl={user?.company?.logo_url}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppDownloadScreen-pm8fLxZX.mjs","names":["AppDownloadScreen","AppDownloadScreenUI"],"sources":["../../app-download-ui/src/components/AppStoreButtons.tsx","../../app-download-ui/src/components/QrCodeSection.tsx","../../app-download-ui/src/components/ImageSection.tsx","../../app-download-ui/src/lib/cn.ts","../../app-download-ui/src/components/AppDownloadScreen.tsx","../src/screens/AppDownloadScreen.tsx"],"sourcesContent":["import { ExternalLink } from \"lucide-react\";\n\nexport interface AppStoreButtonsProps {\n appstoreUrl: string;\n playstoreUrl?: string;\n}\n\nfunction AppleIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 384 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-62.1 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z\" />\n </svg>\n );\n}\n\nfunction GooglePlayIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 512 512\"\n fill=\"currentColor\"\n className=\"size-5\"\n >\n <path d=\"M325.3 234.3L104.6 13l280.8 161.2-60.1 60.1zM47 0C34 6.8 25.3 19.2 25.3 35.3v441.3c0 16.1 8.7 28.5 21.7 35.3l256.6-256L47 0zm425.2 225.6l-58.9-34.1-65.7 64.5 65.7 64.5 60.1-34.1c18-14.3 18-46.5-1.2-60.8zM104.6 499l280.8-161.2-60.1-60.1L104.6 499z\" />\n </svg>\n );\n}\n\nexport function AppStoreButtons({\n appstoreUrl,\n playstoreUrl,\n}: AppStoreButtonsProps) {\n return (\n <div\n className={`grid gap-4 ${playstoreUrl ? \"grid-cols-2\" : \"grid-cols-1\"}`}\n >\n <a\n href={appstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <AppleIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n App Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n\n {playstoreUrl && (\n <a\n href={playstoreUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <GooglePlayIcon />\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Open\n <br />\n Play Store\n </div>\n </div>\n <div className=\"flex items-start\">\n <ExternalLink className=\"size-4\" />\n </div>\n </div>\n </a>\n )}\n </div>\n );\n}\n","export interface QrCodeSectionProps {\n companyId: number;\n}\n\nexport function QrCodeSection({ companyId }: QrCodeSectionProps) {\n const redirectUrl = `https://api.fluid.app/mobile_app_download/${companyId}`;\n const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(redirectUrl)}`;\n\n return (\n <a\n href={redirectUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"block cursor-pointer rounded-sm border bg-white px-4 py-4 text-gray-900 transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-end justify-between\">\n <div className=\"flex flex-col items-baseline\">\n <div className=\"mt-1 text-left text-xs font-semibold\">\n Scan to\n <br />\n Download\n </div>\n </div>\n <div className=\"flex\">\n <img\n src={qrCodeUrl}\n alt=\"QR code for mobile app download\"\n className=\"h-16 w-16\"\n loading=\"lazy\"\n />\n </div>\n </div>\n </a>\n );\n}\n","const DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/iphone_product.png\";\n\nconst DEFAULT_LOGO =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport interface ImageSectionProps {\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n}\n\nexport function ImageSection({\n logoUrl,\n screenshotUrl,\n appName = \"App\",\n}: ImageSectionProps) {\n return (\n <div className=\"flex flex-col overflow-hidden\">\n <div className=\"-mb-px flex flex-1 flex-col\">\n <div className=\"flex h-20 px-3 pt-8\">\n <img\n src={logoUrl || DEFAULT_LOGO}\n alt={appName}\n width={150}\n height={150}\n className=\"object-contain\"\n />\n </div>\n <div\n className={\n screenshotUrl\n ? \"mt-8 flex flex-1 items-end justify-start\"\n : \"-ml-20 flex flex-1 items-end justify-start\"\n }\n >\n <img\n src={screenshotUrl || DEFAULT_IMAGE}\n alt=\"Mobile app interface\"\n width={480}\n height={500}\n className=\"h-full w-auto object-cover\"\n />\n </div>\n </div>\n </div>\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { AppStoreButtons } from \"./AppStoreButtons\";\nimport { QrCodeSection } from \"./QrCodeSection\";\nimport { ImageSection } from \"./ImageSection\";\nimport { cn } from \"../lib/cn\";\n\nconst DEFAULT_APPSTORE_URL =\n \"https://apps.apple.com/app/wecommerce/id1564463740\";\nconst DEFAULT_PLAYSTORE_URL =\n \"https://play.google.com/store/apps/details?id=com.fluid.crm\";\n\nexport interface AppDownloadScreenProps {\n companyId: number;\n appstoreUrl?: string | null;\n playstoreUrl?: string | null;\n logoUrl?: string | null;\n screenshotUrl?: string | null;\n appName?: string;\n className?: string;\n}\n\nexport function AppDownloadScreen({\n companyId,\n appstoreUrl,\n playstoreUrl,\n logoUrl,\n screenshotUrl,\n appName,\n className,\n}: AppDownloadScreenProps) {\n const resolvedAppstoreUrl = appstoreUrl?.trim() || DEFAULT_APPSTORE_URL;\n const resolvedPlaystoreUrl = playstoreUrl?.trim() || DEFAULT_PLAYSTORE_URL;\n\n return (\n <div\n className={cn(\"flex h-full items-center justify-center p-4\", className)}\n >\n <div className=\"bg-muted relative mx-4 w-full max-w-6xl overflow-hidden rounded-xl px-6 shadow-xl\">\n <div className=\"grid h-150 w-full grid-cols-1 lg:grid-cols-2\">\n {/* Left side - Images */}\n <ImageSection\n logoUrl={logoUrl}\n screenshotUrl={screenshotUrl}\n appName={appName}\n />\n\n {/* Right side - Content */}\n <div className=\"space-y-2 pt-8 pb-8\">\n <h1 className=\"mt-12 text-left text-[32px] leading-tight font-black text-gray-900\">\n EVERYTHING YOU NEED\n </h1>\n <h1 className=\"-mt-2 text-left text-[32px] leading-tight font-black text-gray-900\">\n AT YOUR FINGERTIPS\n </h1>\n <p className=\"-mt-1 text-left text-[14px] leading-relaxed text-gray-600\">\n Everything you need at your fingertips. Run your business from one\n place while on the go. Share, message, sell, and manage from the\n mobile app.\n </p>\n\n <h3 className=\"mt-16 text-left text-[12px] font-bold text-gray-900\">\n Download the App\n </h3>\n\n <AppStoreButtons\n appstoreUrl={resolvedAppstoreUrl}\n playstoreUrl={resolvedPlaystoreUrl}\n />\n\n <QrCodeSection companyId={companyId} />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { AppDownloadScreen as AppDownloadScreenUI } from \"@fluid-app/portal-app-download-ui\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\n\nexport const appDownloadScreenPropertySchema = {\n widgetType: \"AppDownloadScreen\",\n displayName: \"App Download Screen\",\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\nexport function AppDownloadScreen(): React.JSX.Element {\n const { data: user } = useCurrentUser();\n\n return (\n <AppDownloadScreenUI\n companyId={user?.company?.id ?? 0}\n appstoreUrl={user?.company?.appstore_url}\n playstoreUrl={user?.company?.playstore_url}\n logoUrl={user?.company?.logo_url}\n />\n );\n}\n"],"mappings":"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"}
|