pxengine 0.1.142 → 0.1.144
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/README.md +38 -34
- package/dist/index.cjs +246 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +242 -90
- package/dist/index.mjs.map +1 -1
- package/dist/registry.json +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Shadcn-based UI component library for agent-driven interfaces**
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A UI component library built on top of shadcn/ui, published to npm as **`pxengine`**, designed specifically for agent-driven interfaces: LLM agents emit a JSON schema, and `PXEngineRenderer` maps it to a React component. Components follow Atomic Design principles and are optimized for schema-driven rendering. Primary consumer is `pxengine-builder`, which links it locally (`"pxengine": "file:../@pxengine-ui"`).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -11,10 +11,10 @@ A premium UI component library built on top of shadcn/ui, designed specifically
|
|
|
11
11
|
`@pxengine-ui` provides a robust foundation for building modern dashboard and AI-integrated applications:
|
|
12
12
|
|
|
13
13
|
- **Built on shadcn/ui** - Industry-standard base layer using Radix UI primitives.
|
|
14
|
-
- **
|
|
15
|
-
- **Agent-First** - Fully schema-driven atoms ready for AI generation
|
|
16
|
-
- **Atomic Design** -
|
|
17
|
-
- **Zero Configuration** - Inherits Tailwind config and CSS variables from the host application.
|
|
14
|
+
- **Themeable via CSS variables** - No hardcoded palette. Components read `--px-*`/host CSS custom properties at render time (falling back to internal purple/indigo defaults when a consumer doesn't supply them), so a dark-gold host app (like `pxengine-builder`) and a light one render the same components on entirely different themes with zero component changes.
|
|
15
|
+
- **Agent-First** - Fully schema-driven atoms and molecules ready for AI generation via `PXEngineRenderer`.
|
|
16
|
+
- **Atomic Design** - 52 Atoms and 60+ Composed Molecules across two domains (generic dashboard + creator discovery).
|
|
17
|
+
- **Zero Configuration** - Inherits Tailwind config and CSS variables from the host application; the host just needs `"node_modules/pxengine/**/*.{js,ts,jsx,tsx}"` in its Tailwind `content` array or production builds purge all pxengine classes.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -23,13 +23,17 @@ A premium UI component library built on top of shadcn/ui, designed specifically
|
|
|
23
23
|
```
|
|
24
24
|
@pxengine-ui/
|
|
25
25
|
├── src/
|
|
26
|
-
│ ├── atoms/ # Foundation UI (Button, Input, Slider, etc.)
|
|
26
|
+
│ ├── atoms/ # Foundation UI (Button, Input, Slider, etc.) — 52 components
|
|
27
27
|
│ ├── molecules/ # Composed Patterns
|
|
28
|
-
│ │ ├── generic/ # Cross-domain dashboard widgets
|
|
29
|
-
│ │ └── creator-discovery/ # Niche-specific for creator search
|
|
28
|
+
│ │ ├── generic/ # Cross-domain dashboard widgets (~48 components)
|
|
29
|
+
│ │ └── creator-discovery/ # Niche-specific for creator search (~16 components)
|
|
30
30
|
│ ├── types/ # Schema & Component definitions
|
|
31
31
|
│ └── lib/ # Shared utilities
|
|
32
|
-
|
|
32
|
+
├── scripts/
|
|
33
|
+
│ ├── generate-metadata.ts # Builds dist/registry.json (component catalog for agents)
|
|
34
|
+
│ └── generate-molecule-registry.ts # Builds the molecule-only registry
|
|
35
|
+
├── tailwind-preset.js # Shared Tailwind preset re-exported for consumers
|
|
36
|
+
└── dist/ # Compiled assets (tsup: CJS + ESM + DTS) & Registry
|
|
33
37
|
```
|
|
34
38
|
|
|
35
39
|
---
|
|
@@ -38,7 +42,7 @@ A premium UI component library built on top of shadcn/ui, designed specifically
|
|
|
38
42
|
|
|
39
43
|
### Atoms (Foundation Primitives)
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
52 accessible atoms wrapping Radix-based shadcn components:
|
|
42
46
|
|
|
43
47
|
- **Forms**: Input, Checkbox, RadioGroup, Select, Switch, Slider, Textarea, Toggle.
|
|
44
48
|
- **Navigation**: DropdownMenu, ContextMenu, Pagination, Breadcrumb, Tabs, Command.
|
|
@@ -48,25 +52,25 @@ A premium UI component library built on top of shadcn/ui, designed specifically
|
|
|
48
52
|
|
|
49
53
|
### Molecules (High-Level Patterns)
|
|
50
54
|
|
|
51
|
-
#### Generic Dashboard
|
|
55
|
+
#### Generic Dashboard (`src/molecules/generic/`)
|
|
52
56
|
|
|
53
|
-
-
|
|
54
|
-
- **EmptyState**: Premium placeholder for empty results.
|
|
55
|
-
- **LoadingOverlay**: Glassmorphism backdrop with progress.
|
|
56
|
-
- **FilterBar**: Search + Multi-chip filtering.
|
|
57
|
-
- **FileUpload**: Drag-and-drop zone.
|
|
58
|
-
- **TagCloud**: Interactive interest clusters.
|
|
59
|
-
- **FormCard**: Schema-driven editable forms.
|
|
57
|
+
Cross-domain widgets, including job-card families for long-running agent work (`ResearchReportJobCard`, `PresentationJobCard`, `WebSearchJobCard` — share layout/state conventions via `job-card-shared/`), data/table components (`DataGrid`, `DataTableCard`, `TablePagination`), integration cards (`GitHubConnectCard`, `GitHubRepoHealthCard`, `GoogleSheetsCard`, `GoogleSheetsConnectCard`), and general dashboard primitives:
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
- **StatsGrid** / **KPIStatsCard**: Data visualization with trends & icons.
|
|
60
|
+
- **EmptyState**, **LoadingOverlay**: Placeholder/loading states.
|
|
61
|
+
- **FilterBar**, **FileUpload**, **TagCloud**, **FormCard**, **DynamicFormCard**, **EditableField**: Input & filtering patterns.
|
|
62
|
+
- **ChecklistCard**, **ApprovalCard**, **ConfirmationCard**, **PollCard**: Decision/workflow patterns.
|
|
63
|
+
- **CampaignBriefCard**, **ChannelPlanCard**, **BudgetAllocCard**, **CalendarEventCard**: Campaign-planning widgets.
|
|
64
|
+
|
|
65
|
+
#### Creator Discovery (`src/molecules/creator-discovery/`)
|
|
62
66
|
|
|
63
67
|
- **CreatorGridCard**: Detailed discovery card with banner and metrics.
|
|
64
|
-
- **AudienceMetricCard**:
|
|
68
|
+
- **AudienceMetricCard** / **AudienceDemographicsCard**: Progress-based demographics.
|
|
65
69
|
- **BrandAffinityGroup**: Visual recently associated brand logos.
|
|
66
70
|
- **ContentPreviewGallery**: Video/Image thumbnail grids.
|
|
67
|
-
- **CreatorProfileSummary
|
|
68
|
-
- **PlatformIconGroup**:
|
|
69
|
-
- **SearchSpecCard**:
|
|
71
|
+
- **CreatorProfileSummary**, **CreatorSearchBox**, **CreatorWidget**, **CreatorActionHeader**: Search & profile patterns.
|
|
72
|
+
- **PlatformIconGroup**, **GrowthChartCard**, **TopPostsGrid**: Reach & performance summaries.
|
|
73
|
+
- **SearchSpecCard**, **MCQCard**, **CampaignSeedCard**, **CampaignConceptCard**: Agent-driven intake/spec patterns.
|
|
70
74
|
|
|
71
75
|
---
|
|
72
76
|
|
|
@@ -74,10 +78,10 @@ A premium UI component library built on top of shadcn/ui, designed specifically
|
|
|
74
78
|
|
|
75
79
|
### Registry-Driven Rendering
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
`npm run build` runs `generate-metadata` automatically (`tsup.config.ts`'s `onSuccess` hook), producing `dist/registry.json` — component metadata + schemas so agents (the server's `ui_generator`/`widget_builder` agents) know what they can render. **Never hand-edit `dist/registry.json`** — it's derived output; if a new molecule isn't showing up in agent suggestions, rebuild and confirm the registry picked it up. `PXEngineRenderer` (exported from the package root) maps a schema's `type` field to a React component via a discriminated-union registry — adding a new molecule requires registering its `type` string there or it silently won't render.
|
|
78
82
|
|
|
79
83
|
```tsx
|
|
80
|
-
import { PXEngineRenderer } from "
|
|
84
|
+
import { PXEngineRenderer } from "pxengine";
|
|
81
85
|
|
|
82
86
|
const schema = {
|
|
83
87
|
type: "stats-grid",
|
|
@@ -113,20 +117,20 @@ import { CreatorGridCard } from "pxengine";
|
|
|
113
117
|
|
|
114
118
|
## Design System
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
Components are **theme-agnostic by design** — they read CSS custom properties (`--px-*`, plus the host's own tokens) at render time rather than hardcoding a palette, so `cn()` (re-exported from `pxengine`, not `clsx`, for consumers) composes host classes on top of component defaults. A few atoms (e.g. `AvatarAtom`) fall back to internal indigo/purple/slate defaults (`var(--px-bg-color, var(--purple50))`) when a host doesn't supply theme vars — that fallback palette is NOT what most consumers actually see. `pxengine-builder`, the primary consumer, supplies a dark, gold-accented theme (`--gold: #C0AE82`, `surface.chat #0A0A0A` → `surface.elevated #1E1E1F`, see its README) via `globals.css`, and every pxengine component renders on that theme with zero component-level changes.
|
|
117
121
|
|
|
118
|
-
- **Colors**: Indigo, Purple, and Slate primary hues.
|
|
119
122
|
- **Border Radius**: Generous `rounded-3xl` and `rounded-[32px]` for major components.
|
|
120
|
-
- **Backdrop**:
|
|
123
|
+
- **Backdrop**: Uses `backdrop-blur-md` for overlay/glass surfaces where the host theme calls for it.
|
|
121
124
|
- **Shadows**: Subtle, layered soft shadows for depth.
|
|
122
125
|
|
|
123
126
|
---
|
|
124
127
|
|
|
125
128
|
## Tech Stack
|
|
126
129
|
|
|
127
|
-
- **Framework**: React 18
|
|
128
|
-
- **Styling**: Tailwind CSS + `class-variance-authority`
|
|
129
|
-
- **Primitives**: Radix UI
|
|
130
|
-
- **Animations**: Framer Motion
|
|
131
|
-
- **Icons**: Lucide React
|
|
132
|
-
- **
|
|
130
|
+
- **Framework**: React (peer dep `^18.0.0 || ^19.0.0`; primary consumer runs 19) — built with **tsup** (CJS + ESM + `.d.ts`), not Vite
|
|
131
|
+
- **Styling**: Tailwind CSS + `class-variance-authority` + `tailwind-preset.js` (shared preset re-exported for consumers)
|
|
132
|
+
- **Primitives**: Radix UI (via shadcn/ui)
|
|
133
|
+
- **Animations**: Framer Motion (peer dep, external — not bundled)
|
|
134
|
+
- **Icons**: Lucide React (bundled into the build via `tsup`'s `noExternal`, along with `date-fns`, `@date-fns/tz`, `react-day-picker`)
|
|
135
|
+
- **Charts**: Highcharts + `highcharts-react-official`, and Recharts — all peer deps, external
|
|
136
|
+
- **Data/validation**: Zod
|
package/dist/index.cjs
CHANGED
|
@@ -16779,6 +16779,25 @@ var EditIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)("svg", { wi
|
|
|
16779
16779
|
] });
|
|
16780
16780
|
var ChevronUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("polyline", { points: "18 15 12 9 6 15" }) });
|
|
16781
16781
|
var ChevronDownIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime151.jsx)("polyline", { points: "6 9 12 15 18 9" }) });
|
|
16782
|
+
var PptxIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
16783
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("rect", { x: "3", y: "4", width: "18", height: "13", rx: "2" }),
|
|
16784
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M7 21h10M12 17v4" }),
|
|
16785
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M8 13v-2M12 13V9M16 13v-3" })
|
|
16786
|
+
] });
|
|
16787
|
+
var PdfIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
16788
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
|
|
16789
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M14 3v5h5" }),
|
|
16790
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M9 13h6M9 17h4" })
|
|
16791
|
+
] });
|
|
16792
|
+
var HtmlIcon = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
16793
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
|
|
16794
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M3 9h18" }),
|
|
16795
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M6.5 6.5h.01M9.5 6.5h.01" })
|
|
16796
|
+
] });
|
|
16797
|
+
var Spinner2 = () => /* @__PURE__ */ (0, import_jsx_runtime151.jsxs)("svg", { className: "animate-spin", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
16798
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "3" }),
|
|
16799
|
+
/* @__PURE__ */ (0, import_jsx_runtime151.jsx)("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" })
|
|
16800
|
+
] });
|
|
16782
16801
|
|
|
16783
16802
|
// src/molecules/generic/PresentationJobCard/PresentationJobCard.tsx
|
|
16784
16803
|
var import_jsx_runtime152 = require("react/jsx-runtime");
|
|
@@ -16837,31 +16856,34 @@ var FORMATS = [
|
|
|
16837
16856
|
key: "pptx_url",
|
|
16838
16857
|
label: "PowerPoint",
|
|
16839
16858
|
ext: ".pptx",
|
|
16840
|
-
|
|
16859
|
+
Icon: PptxIcon,
|
|
16841
16860
|
desc: "Editable slides \u2014 open in PowerPoint, Keynote, or Google Slides",
|
|
16842
|
-
|
|
16861
|
+
// Hue lives on the icon tile only; the row itself stays neutral so three
|
|
16862
|
+
// stacked formats don't read as three competing status colours.
|
|
16863
|
+
accent: { icon: "text-orange-300", tile: "bg-orange-500/10 ring-orange-500/20" }
|
|
16843
16864
|
},
|
|
16844
16865
|
{
|
|
16845
16866
|
key: "pdf_url",
|
|
16846
16867
|
label: "PDF",
|
|
16847
16868
|
ext: ".pdf",
|
|
16848
|
-
|
|
16869
|
+
Icon: PdfIcon,
|
|
16849
16870
|
desc: "Print-ready, fixed layout \u2014 open in any PDF viewer",
|
|
16850
|
-
accent: {
|
|
16871
|
+
accent: { icon: "text-red-300", tile: "bg-red-500/10 ring-red-500/20" }
|
|
16851
16872
|
},
|
|
16852
16873
|
{
|
|
16853
16874
|
key: "html_url",
|
|
16854
16875
|
label: "HTML",
|
|
16855
16876
|
ext: ".html",
|
|
16856
|
-
|
|
16877
|
+
Icon: HtmlIcon,
|
|
16857
16878
|
desc: "Interactive web presentation \u2014 download and open in browser",
|
|
16858
|
-
accent: {
|
|
16879
|
+
accent: { icon: "text-sky-300", tile: "bg-sky-500/10 ring-sky-500/20" }
|
|
16859
16880
|
}
|
|
16860
16881
|
];
|
|
16861
16882
|
var ExportModal = ({ formats, title, onClose }) => {
|
|
16862
16883
|
const available = FORMATS.filter((f) => (formats ?? {})[f.key]);
|
|
16863
16884
|
const filename = (title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase();
|
|
16864
16885
|
const [downloadingKey, setDownloadingKey] = (0, import_react82.useState)(null);
|
|
16886
|
+
const panelRef = (0, import_react82.useRef)(null);
|
|
16865
16887
|
const handleDownload = async (fmtKey, url, ext) => {
|
|
16866
16888
|
if (downloadingKey) return;
|
|
16867
16889
|
const downloadName = `${filename}${ext}`;
|
|
@@ -16879,55 +16901,120 @@ var ExportModal = ({ formats, title, onClose }) => {
|
|
|
16879
16901
|
anchor.click();
|
|
16880
16902
|
anchor.remove();
|
|
16881
16903
|
window.URL.revokeObjectURL(objectUrl);
|
|
16904
|
+
onClose();
|
|
16882
16905
|
} catch {
|
|
16883
|
-
window.open(href, "_blank", "noopener,noreferrer");
|
|
16906
|
+
const popup = window.open(href, "_blank", "noopener,noreferrer");
|
|
16907
|
+
if (popup) onClose();
|
|
16884
16908
|
} finally {
|
|
16885
16909
|
setDownloadingKey(null);
|
|
16886
16910
|
}
|
|
16887
16911
|
};
|
|
16912
|
+
(0, import_react82.useEffect)(() => {
|
|
16913
|
+
const onKey = (e) => {
|
|
16914
|
+
if (e.key === "Escape") onClose();
|
|
16915
|
+
};
|
|
16916
|
+
document.addEventListener("keydown", onKey);
|
|
16917
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
16918
|
+
}, [onClose]);
|
|
16919
|
+
(0, import_react82.useEffect)(() => {
|
|
16920
|
+
document.body.style.overflow = "hidden";
|
|
16921
|
+
panelRef.current?.focus();
|
|
16922
|
+
return () => {
|
|
16923
|
+
document.body.style.overflow = "";
|
|
16924
|
+
};
|
|
16925
|
+
}, []);
|
|
16888
16926
|
return (0, import_react_dom.createPortal)(
|
|
16889
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(
|
|
16890
|
-
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
|
|
16895
|
-
|
|
16896
|
-
|
|
16897
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
|
|
16910
|
-
className: cn(
|
|
16911
|
-
"flex w-full items-center gap-3.5 p-3.5 rounded-xl border transition-all text-left",
|
|
16912
|
-
"hover:brightness-110 disabled:opacity-60 disabled:cursor-not-allowed",
|
|
16913
|
-
fmt.accent.bg
|
|
16914
|
-
),
|
|
16915
|
-
children: [
|
|
16916
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("span", { className: "text-xl flex-shrink-0", children: fmt.emoji }),
|
|
16917
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
16918
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: cn("text-sm font-semibold", fmt.accent.text), children: isDownloading ? `Downloading ${fmt.label}...` : fmt.label }),
|
|
16919
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "text-xs text-zinc-500 mt-0.5 leading-relaxed", children: isDownloading ? "Please wait while the file is being prepared." : fmt.desc })
|
|
16927
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(
|
|
16928
|
+
"div",
|
|
16929
|
+
{
|
|
16930
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center p-4",
|
|
16931
|
+
role: "dialog",
|
|
16932
|
+
"aria-modal": "true",
|
|
16933
|
+
"aria-labelledby": "pxe-export-title",
|
|
16934
|
+
children: [
|
|
16935
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "absolute inset-0 bg-black/70 backdrop-blur-sm", onClick: onClose }),
|
|
16936
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(
|
|
16937
|
+
"div",
|
|
16938
|
+
{
|
|
16939
|
+
ref: panelRef,
|
|
16940
|
+
tabIndex: -1,
|
|
16941
|
+
className: "relative z-10 w-full max-w-md overflow-hidden rounded-2xl border border-zinc-800 bg-zinc-900 shadow-2xl outline-none",
|
|
16942
|
+
children: [
|
|
16943
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "flex items-start gap-3 border-b border-zinc-800/80 px-5 py-4", children: [
|
|
16944
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("span", { className: "mt-0.5 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-[#C0AE82]/10 text-[#DCC99B] ring-1 ring-[#C0AE82]/20", children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(SlidesIcon, {}) }),
|
|
16945
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
16946
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("h3", { id: "pxe-export-title", className: "text-sm font-semibold text-zinc-100", children: "Export presentation" }),
|
|
16947
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "mt-0.5 truncate text-xs text-zinc-500", children: title })
|
|
16920
16948
|
] }),
|
|
16921
|
-
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)(
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
|
|
16928
|
-
|
|
16929
|
-
|
|
16930
|
-
|
|
16949
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)(
|
|
16950
|
+
"button",
|
|
16951
|
+
{
|
|
16952
|
+
onClick: onClose,
|
|
16953
|
+
"aria-label": "Close export dialog",
|
|
16954
|
+
className: "-mr-1 mt-0.5 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-200",
|
|
16955
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(CloseIcon, {})
|
|
16956
|
+
}
|
|
16957
|
+
)
|
|
16958
|
+
] }),
|
|
16959
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "space-y-2 p-4", children: available.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "py-8 text-center", children: [
|
|
16960
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "text-sm text-zinc-400", children: "No formats available yet" }),
|
|
16961
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "mt-1 text-xs text-zinc-600", children: "Exports appear here once the deck finishes generating." })
|
|
16962
|
+
] }) : available.map((fmt) => {
|
|
16963
|
+
const url = formats[fmt.key];
|
|
16964
|
+
const isDownloading = downloadingKey === fmt.key;
|
|
16965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime152.jsxs)(
|
|
16966
|
+
"button",
|
|
16967
|
+
{
|
|
16968
|
+
type: "button",
|
|
16969
|
+
disabled: Boolean(downloadingKey),
|
|
16970
|
+
onClick: () => handleDownload(fmt.key, url, fmt.ext),
|
|
16971
|
+
className: cn(
|
|
16972
|
+
"group flex w-full items-center gap-3 rounded-xl border border-zinc-800 bg-zinc-800/40 p-3 text-left transition-all",
|
|
16973
|
+
"hover:border-[#C0AE82]/40 hover:bg-zinc-800/70",
|
|
16974
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-[#C0AE82]/50",
|
|
16975
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-zinc-800 disabled:hover:bg-zinc-800/40"
|
|
16976
|
+
),
|
|
16977
|
+
children: [
|
|
16978
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)(
|
|
16979
|
+
"span",
|
|
16980
|
+
{
|
|
16981
|
+
className: cn(
|
|
16982
|
+
"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg ring-1",
|
|
16983
|
+
fmt.accent.tile,
|
|
16984
|
+
fmt.accent.icon
|
|
16985
|
+
),
|
|
16986
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(fmt.Icon, {})
|
|
16987
|
+
}
|
|
16988
|
+
),
|
|
16989
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
16990
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
16991
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "text-sm font-semibold text-zinc-100", children: fmt.label }),
|
|
16992
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("span", { className: "rounded bg-zinc-800 px-1.5 py-0.5 font-mono text-[10px] text-zinc-500 ring-1 ring-zinc-700/60", children: fmt.ext })
|
|
16993
|
+
] }),
|
|
16994
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "mt-0.5 text-xs leading-relaxed text-zinc-500", children: isDownloading ? "Preparing your file\u2026" : fmt.desc })
|
|
16995
|
+
] }),
|
|
16996
|
+
/* @__PURE__ */ (0, import_jsx_runtime152.jsx)(
|
|
16997
|
+
"span",
|
|
16998
|
+
{
|
|
16999
|
+
className: cn(
|
|
17000
|
+
"flex-shrink-0 transition-colors",
|
|
17001
|
+
isDownloading ? "text-[#DCC99B]" : "text-zinc-600 group-hover:text-[#DCC99B]"
|
|
17002
|
+
),
|
|
17003
|
+
children: isDownloading ? /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(Spinner2, {}) : /* @__PURE__ */ (0, import_jsx_runtime152.jsx)(DownloadIcon, {})
|
|
17004
|
+
}
|
|
17005
|
+
)
|
|
17006
|
+
]
|
|
17007
|
+
},
|
|
17008
|
+
fmt.key
|
|
17009
|
+
);
|
|
17010
|
+
}) }),
|
|
17011
|
+
available.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("div", { className: "border-t border-zinc-800/80 px-5 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime152.jsx)("p", { className: "text-center text-[11px] text-zinc-600", children: "Download links expire in 90 days" }) })
|
|
17012
|
+
]
|
|
17013
|
+
}
|
|
17014
|
+
)
|
|
17015
|
+
]
|
|
17016
|
+
}
|
|
17017
|
+
),
|
|
16931
17018
|
document.body
|
|
16932
17019
|
);
|
|
16933
17020
|
};
|
|
@@ -18081,6 +18168,7 @@ var PresentationJobCard = ({
|
|
|
18081
18168
|
|
|
18082
18169
|
// src/molecules/generic/ResearchReportJobCard/ResearchReportJobCard.tsx
|
|
18083
18170
|
var import_react83 = require("react");
|
|
18171
|
+
var import_react_dom2 = require("react-dom");
|
|
18084
18172
|
var import_jsx_runtime153 = require("react/jsx-runtime");
|
|
18085
18173
|
var DEFAULT_THEME = {
|
|
18086
18174
|
primary: "#C0AE82",
|
|
@@ -18167,17 +18255,19 @@ var REPORT_FORMATS = [
|
|
|
18167
18255
|
key: "pdf",
|
|
18168
18256
|
label: "PDF",
|
|
18169
18257
|
ext: ".pdf",
|
|
18170
|
-
|
|
18258
|
+
Icon: PdfIcon,
|
|
18171
18259
|
desc: "Print-ready, fixed layout \u2014 open in any PDF viewer",
|
|
18172
|
-
|
|
18260
|
+
// Hue lives on the icon tile only; the rows themselves stay neutral so
|
|
18261
|
+
// stacked formats don't read as competing status colours.
|
|
18262
|
+
accent: { icon: "text-red-300", tile: "bg-red-500/10 ring-red-500/20" }
|
|
18173
18263
|
},
|
|
18174
18264
|
{
|
|
18175
18265
|
key: "html",
|
|
18176
18266
|
label: "HTML",
|
|
18177
18267
|
ext: ".html",
|
|
18178
|
-
|
|
18268
|
+
Icon: HtmlIcon,
|
|
18179
18269
|
desc: "Interactive web page \u2014 download and open in a browser",
|
|
18180
|
-
accent: {
|
|
18270
|
+
accent: { icon: "text-sky-300", tile: "bg-sky-500/10 ring-sky-500/20" }
|
|
18181
18271
|
}
|
|
18182
18272
|
];
|
|
18183
18273
|
var ReportExportModal = ({ htmlUrl, pdfUrl, title, onClose }) => {
|
|
@@ -18185,6 +18275,7 @@ var ReportExportModal = ({ htmlUrl, pdfUrl, title, onClose }) => {
|
|
|
18185
18275
|
const available = REPORT_FORMATS.filter((f) => urls[f.key]);
|
|
18186
18276
|
const filename = (title ?? "").replace(/[^a-z0-9]/gi, "-").toLowerCase();
|
|
18187
18277
|
const [downloadingKey, setDownloadingKey] = (0, import_react83.useState)(null);
|
|
18278
|
+
const panelRef = (0, import_react83.useRef)(null);
|
|
18188
18279
|
(0, import_react83.useEffect)(() => {
|
|
18189
18280
|
const onKey = (e) => {
|
|
18190
18281
|
if (e.key === "Escape") onClose();
|
|
@@ -18192,6 +18283,13 @@ var ReportExportModal = ({ htmlUrl, pdfUrl, title, onClose }) => {
|
|
|
18192
18283
|
document.addEventListener("keydown", onKey);
|
|
18193
18284
|
return () => document.removeEventListener("keydown", onKey);
|
|
18194
18285
|
}, [onClose]);
|
|
18286
|
+
(0, import_react83.useEffect)(() => {
|
|
18287
|
+
document.body.style.overflow = "hidden";
|
|
18288
|
+
panelRef.current?.focus();
|
|
18289
|
+
return () => {
|
|
18290
|
+
document.body.style.overflow = "";
|
|
18291
|
+
};
|
|
18292
|
+
}, []);
|
|
18195
18293
|
const handleDownload = async (key, url, ext) => {
|
|
18196
18294
|
if (downloadingKey) return;
|
|
18197
18295
|
const downloadName = `${filename}${ext}`;
|
|
@@ -18209,53 +18307,107 @@ var ReportExportModal = ({ htmlUrl, pdfUrl, title, onClose }) => {
|
|
|
18209
18307
|
anchor.click();
|
|
18210
18308
|
anchor.remove();
|
|
18211
18309
|
window.URL.revokeObjectURL(objectUrl);
|
|
18310
|
+
onClose();
|
|
18212
18311
|
} catch {
|
|
18213
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
18312
|
+
const popup = window.open(url, "_blank", "noopener,noreferrer");
|
|
18313
|
+
if (popup) onClose();
|
|
18214
18314
|
} finally {
|
|
18215
18315
|
setDownloadingKey(null);
|
|
18216
18316
|
}
|
|
18217
18317
|
};
|
|
18218
|
-
return
|
|
18219
|
-
/* @__PURE__ */ (0, import_jsx_runtime153.
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
available.map((fmt) => {
|
|
18231
|
-
const url = urls[fmt.key];
|
|
18232
|
-
const isDownloading = downloadingKey === fmt.key;
|
|
18233
|
-
return /* @__PURE__ */ (0, import_jsx_runtime153.jsxs)(
|
|
18234
|
-
"button",
|
|
18318
|
+
return (0, import_react_dom2.createPortal)(
|
|
18319
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)(
|
|
18320
|
+
"div",
|
|
18321
|
+
{
|
|
18322
|
+
className: "fixed inset-0 z-[100] flex items-center justify-center p-4",
|
|
18323
|
+
role: "dialog",
|
|
18324
|
+
"aria-modal": "true",
|
|
18325
|
+
"aria-labelledby": "pxe-report-export-title",
|
|
18326
|
+
children: [
|
|
18327
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("div", { className: "absolute inset-0 bg-black/70 backdrop-blur-sm", onClick: onClose }),
|
|
18328
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)(
|
|
18329
|
+
"div",
|
|
18235
18330
|
{
|
|
18236
|
-
|
|
18237
|
-
|
|
18238
|
-
|
|
18239
|
-
className: cn(
|
|
18240
|
-
"flex w-full items-center gap-3.5 p-3.5 rounded-xl border transition-all text-left",
|
|
18241
|
-
"hover:brightness-110 disabled:opacity-60 disabled:cursor-not-allowed",
|
|
18242
|
-
fmt.accent.bg
|
|
18243
|
-
),
|
|
18331
|
+
ref: panelRef,
|
|
18332
|
+
tabIndex: -1,
|
|
18333
|
+
className: "relative z-10 w-full max-w-md overflow-hidden rounded-2xl border border-zinc-800 bg-zinc-900 shadow-2xl outline-none",
|
|
18244
18334
|
children: [
|
|
18245
|
-
/* @__PURE__ */ (0, import_jsx_runtime153.
|
|
18246
|
-
|
|
18247
|
-
/* @__PURE__ */ (0, import_jsx_runtime153.
|
|
18248
|
-
|
|
18335
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)("div", { className: "flex items-start gap-3 border-b border-zinc-800/80 px-5 py-4", children: [
|
|
18336
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("span", { className: "mt-0.5 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-[#C0AE82]/10 text-[#DCC99B] ring-1 ring-[#C0AE82]/20", children: /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(DocumentIcon, {}) }),
|
|
18337
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
18338
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("h3", { id: "pxe-report-export-title", className: "text-sm font-semibold text-zinc-100", children: "Download report" }),
|
|
18339
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("p", { className: "mt-0.5 truncate text-xs text-zinc-500", children: title })
|
|
18340
|
+
] }),
|
|
18341
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)(
|
|
18342
|
+
"button",
|
|
18343
|
+
{
|
|
18344
|
+
onClick: onClose,
|
|
18345
|
+
"aria-label": "Close download dialog",
|
|
18346
|
+
className: "-mr-1 mt-0.5 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-200",
|
|
18347
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(CloseIcon, {})
|
|
18348
|
+
}
|
|
18349
|
+
)
|
|
18249
18350
|
] }),
|
|
18250
|
-
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("
|
|
18351
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("div", { className: "space-y-2 p-4", children: available.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime153.jsxs)("div", { className: "py-8 text-center", children: [
|
|
18352
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("p", { className: "text-sm text-zinc-400", children: "No formats available yet" }),
|
|
18353
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("p", { className: "mt-1 text-xs text-zinc-600", children: "Downloads appear here once the report finishes generating." })
|
|
18354
|
+
] }) : available.map((fmt) => {
|
|
18355
|
+
const url = urls[fmt.key];
|
|
18356
|
+
const isDownloading = downloadingKey === fmt.key;
|
|
18357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime153.jsxs)(
|
|
18358
|
+
"button",
|
|
18359
|
+
{
|
|
18360
|
+
type: "button",
|
|
18361
|
+
disabled: Boolean(downloadingKey),
|
|
18362
|
+
onClick: () => handleDownload(fmt.key, url, fmt.ext),
|
|
18363
|
+
className: cn(
|
|
18364
|
+
"group flex w-full items-center gap-3 rounded-xl border border-zinc-800 bg-zinc-800/40 p-3 text-left transition-all",
|
|
18365
|
+
"hover:border-[#C0AE82]/40 hover:bg-zinc-800/70",
|
|
18366
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-[#C0AE82]/50",
|
|
18367
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-zinc-800 disabled:hover:bg-zinc-800/40"
|
|
18368
|
+
),
|
|
18369
|
+
children: [
|
|
18370
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)(
|
|
18371
|
+
"span",
|
|
18372
|
+
{
|
|
18373
|
+
className: cn(
|
|
18374
|
+
"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg ring-1",
|
|
18375
|
+
fmt.accent.tile,
|
|
18376
|
+
fmt.accent.icon
|
|
18377
|
+
),
|
|
18378
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(fmt.Icon, {})
|
|
18379
|
+
}
|
|
18380
|
+
),
|
|
18381
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
18382
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
18383
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("p", { className: "text-sm font-semibold text-zinc-100", children: fmt.label }),
|
|
18384
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("span", { className: "rounded bg-zinc-800 px-1.5 py-0.5 font-mono text-[10px] text-zinc-500 ring-1 ring-zinc-700/60", children: fmt.ext })
|
|
18385
|
+
] }),
|
|
18386
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)("p", { className: "mt-0.5 text-xs leading-relaxed text-zinc-500", children: isDownloading ? "Preparing your file\u2026" : fmt.desc })
|
|
18387
|
+
] }),
|
|
18388
|
+
/* @__PURE__ */ (0, import_jsx_runtime153.jsx)(
|
|
18389
|
+
"span",
|
|
18390
|
+
{
|
|
18391
|
+
className: cn(
|
|
18392
|
+
"flex-shrink-0 transition-colors",
|
|
18393
|
+
isDownloading ? "text-[#DCC99B]" : "text-zinc-600 group-hover:text-[#DCC99B]"
|
|
18394
|
+
),
|
|
18395
|
+
children: isDownloading ? /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(Spinner2, {}) : /* @__PURE__ */ (0, import_jsx_runtime153.jsx)(DownloadIcon, {})
|
|
18396
|
+
}
|
|
18397
|
+
)
|
|
18398
|
+
]
|
|
18399
|
+
},
|
|
18400
|
+
fmt.key
|
|
18401
|
+
);
|
|
18402
|
+
}) })
|
|
18251
18403
|
]
|
|
18252
|
-
}
|
|
18253
|
-
|
|
18254
|
-
|
|
18255
|
-
|
|
18256
|
-
|
|
18257
|
-
|
|
18258
|
-
|
|
18404
|
+
}
|
|
18405
|
+
)
|
|
18406
|
+
]
|
|
18407
|
+
}
|
|
18408
|
+
),
|
|
18409
|
+
document.body
|
|
18410
|
+
);
|
|
18259
18411
|
};
|
|
18260
18412
|
function deriveJobStatusUrl2(newJobId, pollUrl, regenerateUrl) {
|
|
18261
18413
|
const base = pollUrl?.replace(/\/api\/jobs\/[^/]+\/status.*$/, "") ?? regenerateUrl?.replace(/\/api\/reports\/[^/]+\/regenerate.*$/, "") ?? "";
|
|
@@ -22057,7 +22209,7 @@ function CreatorCompactView({
|
|
|
22057
22209
|
|
|
22058
22210
|
// src/molecules/creator-discovery/CreatorWidget/CreatorExpandedPanel.tsx
|
|
22059
22211
|
var import_react96 = require("react");
|
|
22060
|
-
var
|
|
22212
|
+
var import_react_dom4 = __toESM(require("react-dom"), 1);
|
|
22061
22213
|
var import_framer_motion5 = require("framer-motion");
|
|
22062
22214
|
|
|
22063
22215
|
// src/molecules/creator-discovery/CreatorWidget/defaultFetchers.ts
|
|
@@ -22756,7 +22908,7 @@ function PlatformPostsSection({
|
|
|
22756
22908
|
|
|
22757
22909
|
// src/molecules/creator-discovery/CreatorWidget/BrandCollaborationsList.tsx
|
|
22758
22910
|
var import_react94 = require("react");
|
|
22759
|
-
var
|
|
22911
|
+
var import_react_dom3 = __toESM(require("react-dom"), 1);
|
|
22760
22912
|
var import_framer_motion3 = require("framer-motion");
|
|
22761
22913
|
var import_jsx_runtime179 = require("react/jsx-runtime");
|
|
22762
22914
|
var getSentimentRank = (score) => {
|
|
@@ -22802,7 +22954,7 @@ function BrandMentionDetails({
|
|
|
22802
22954
|
(m) => m.normalizedBrand?.toLowerCase() === selectedBrand?.toLowerCase() || m.brand?.toLowerCase() === selectedBrand?.toLowerCase()
|
|
22803
22955
|
) || [];
|
|
22804
22956
|
if (typeof window === "undefined") return null;
|
|
22805
|
-
return
|
|
22957
|
+
return import_react_dom3.default.createPortal(
|
|
22806
22958
|
/* @__PURE__ */ (0, import_jsx_runtime179.jsx)(import_framer_motion3.AnimatePresence, { mode: "sync", children: open && /* @__PURE__ */ (0, import_jsx_runtime179.jsxs)(import_jsx_runtime179.Fragment, { children: [
|
|
22807
22959
|
/* @__PURE__ */ (0, import_jsx_runtime179.jsx)(
|
|
22808
22960
|
import_framer_motion3.motion.div,
|
|
@@ -23777,7 +23929,7 @@ function CreatorExpandedPanel({
|
|
|
23777
23929
|
}
|
|
23778
23930
|
}, [isOpen, loadCreators]);
|
|
23779
23931
|
if (typeof window === "undefined") return null;
|
|
23780
|
-
return
|
|
23932
|
+
return import_react_dom4.default.createPortal(
|
|
23781
23933
|
/* @__PURE__ */ (0, import_jsx_runtime181.jsx)(import_framer_motion5.AnimatePresence, { mode: "sync", children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime181.jsxs)(import_jsx_runtime181.Fragment, { children: [
|
|
23782
23934
|
/* @__PURE__ */ (0, import_jsx_runtime181.jsx)(
|
|
23783
23935
|
import_framer_motion5.motion.div,
|