@particle-academy/fancy-slides 0.5.0 → 0.6.0

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.
@@ -0,0 +1,30 @@
1
+ import { useIsDarkSlide } from './chunk-WIUXPQAK.js';
2
+ import { PeerMissing } from './chunk-4HHEWTDW.js';
3
+ import { lazy, Suspense } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var ChartInner = lazy(async () => {
7
+ try {
8
+ const mod = await import('@particle-academy/fancy-echarts');
9
+ const EChart = mod.EChart;
10
+ if (!EChart) {
11
+ return { default: () => /* @__PURE__ */ jsx(PeerMissing, { label: "Chart", install: "npm i @particle-academy/fancy-echarts" }) };
12
+ }
13
+ mod.registerAll?.();
14
+ mod.registerBuiltinThemes?.();
15
+ return {
16
+ default: ({ option, theme }) => /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(EChart, { option, theme }) })
17
+ };
18
+ } catch {
19
+ return { default: () => /* @__PURE__ */ jsx(PeerMissing, { label: "Chart", install: "npm i @particle-academy/fancy-echarts" }) };
20
+ }
21
+ });
22
+ function ChartHost({ element }) {
23
+ const isDarkSlide = useIsDarkSlide();
24
+ const theme = element.chartTheme ?? (isDarkSlide ? "dark" : void 0);
25
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(ChartInner, { option: element.option, theme }) });
26
+ }
27
+
28
+ export { ChartHost as default };
29
+ //# sourceMappingURL=chart-host-ZJE7WAHE.js.map
30
+ //# sourceMappingURL=chart-host-ZJE7WAHE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/element-renderers/chart-host.tsx"],"names":[],"mappings":";;;;;AAqBA,IAAM,UAAA,GAAwC,KAAK,YAAY;AAC3D,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAA+B,MAAM,OAAO,iCAAiC,CAAA;AACnF,IAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AAGnB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA,OAAO,EAAE,SAAS,sBAAM,GAAA,CAAC,eAAY,KAAA,EAAM,OAAA,EAAQ,OAAA,EAAQ,uCAAA,EAAwC,CAAA,EAAG;AAAA,IAC1G;AAKA,IAAC,IAAI,WAAA,IAA2C;AAChD,IAAC,IAAI,qBAAA,IAAqD;AAC1D,IAAA,OAAO;AAAA,MACH,SAAS,CAAC,EAAE,QAAQ,KAAA,EAAM,yBACrB,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAO,EACxC,8BAAC,MAAA,EAAA,EAAO,MAAA,EAAgB,OAAc,CAAA,EAC1C;AAAA,KAER;AAAA,EACJ,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,EAAE,SAAS,sBAAM,GAAA,CAAC,eAAY,KAAA,EAAM,OAAA,EAAQ,OAAA,EAAQ,uCAAA,EAAwC,CAAA,EAAG;AAAA,EAC1G;AACJ,CAAC,CAAA;AAEc,SAAR,SAAA,CAA2B,EAAE,OAAA,EAAQ,EAAoD;AAI5F,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,UAAA,KAAe,WAAA,GAAc,MAAA,GAAS,MAAA,CAAA;AAC5D,EAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,EAAU,IAAA,EAChB,QAAA,kBAAA,GAAA,CAAC,cAAW,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAQ,KAAA,EAAc,CAAA,EACtD,CAAA;AAER","file":"chart-host-ZJE7WAHE.js","sourcesContent":["import { lazy, Suspense, type ComponentType } from \"react\";\nimport type { ChartElement } from \"../../types\";\nimport { useIsDarkSlide } from \"../../components/Slide/slide-context\";\nimport { PeerMissing } from \"./peer-missing\";\n\n/**\n * fancy-echarts is an OPTIONAL peer. A static `import { EChart } from\n * \"@particle-academy/fancy-echarts\"` would make Rollup statically resolve the\n * peer when a CONSUMER builds — and blow up with MISSING_EXPORT if they never\n * installed it. So instead we load the peer via a DYNAMIC import and read its\n * members off the resolved module at RUNTIME, guarding for the stub case.\n *\n * The dynamic `import().then(m => …)` resolves to the optional-peer stub when\n * the peer is absent (`m.EChart` is `undefined`), which we detect and swap for\n * a placeholder — no build-time named binding to break.\n */\ninterface InnerProps {\n option: unknown;\n theme: string | undefined;\n}\n\nconst ChartInner: ComponentType<InnerProps> = lazy(async () => {\n try {\n const mod: Record<string, unknown> = await import(\"@particle-academy/fancy-echarts\");\n const EChart = mod.EChart as\n | ComponentType<{ option: unknown; theme?: string }>\n | undefined;\n if (!EChart) {\n return { default: () => <PeerMissing label=\"Chart\" install=\"npm i @particle-academy/fancy-echarts\" /> };\n }\n // fancy-echarts ships its chart types as opt-in tree-shake-friendly\n // modules, so the consumer normally calls `registerAll()` somewhere\n // global. We wire it here, but only once the peer has actually loaded —\n // both calls are idempotent on echarts' side, so re-imports are safe.\n (mod.registerAll as (() => void) | undefined)?.();\n (mod.registerBuiltinThemes as (() => void) | undefined)?.();\n return {\n default: ({ option, theme }: InnerProps) => (\n <div style={{ width: \"100%\", height: \"100%\" }}>\n <EChart option={option} theme={theme} />\n </div>\n ),\n };\n } catch {\n return { default: () => <PeerMissing label=\"Chart\" install=\"npm i @particle-academy/fancy-echarts\" /> };\n }\n});\n\nexport default function ChartHost({ element }: { element: ChartElement; slideWidthPx: number }) {\n // Hooks stay in the OUTER component (Rules of Hooks); resolved values are\n // passed into the lazily-loaded inner. If the slide background reads as dark\n // and the chart doesn't pin a theme, fall back to echarts' built-in \"dark\".\n const isDarkSlide = useIsDarkSlide();\n const theme = element.chartTheme ?? (isDarkSlide ? \"dark\" : undefined);\n return (\n <Suspense fallback={null}>\n <ChartInner option={element.option} theme={theme} />\n </Suspense>\n );\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+
3
+ // src/registry/element-renderers/peer-missing.tsx
4
+ function PeerMissing({ label, install }) {
5
+ return /* @__PURE__ */ jsxs(
6
+ "div",
7
+ {
8
+ style: {
9
+ width: "100%",
10
+ height: "100%",
11
+ display: "flex",
12
+ flexDirection: "column",
13
+ alignItems: "center",
14
+ justifyContent: "center",
15
+ gap: "0.4em",
16
+ textAlign: "center",
17
+ padding: "0.6em",
18
+ boxSizing: "border-box",
19
+ border: "1px dashed currentColor",
20
+ borderRadius: 8,
21
+ opacity: 0.6,
22
+ overflow: "hidden",
23
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
24
+ },
25
+ children: [
26
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600 }, children: label }),
27
+ /* @__PURE__ */ jsx(
28
+ "code",
29
+ {
30
+ style: {
31
+ fontSize: "0.72em",
32
+ opacity: 0.85,
33
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
34
+ },
35
+ children: install
36
+ }
37
+ )
38
+ ]
39
+ }
40
+ );
41
+ }
42
+
43
+ export { PeerMissing };
44
+ //# sourceMappingURL=chunk-4HHEWTDW.js.map
45
+ //# sourceMappingURL=chunk-4HHEWTDW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/element-renderers/peer-missing.tsx"],"names":[],"mappings":";;;AAWO,SAAS,WAAA,CAAY,EAAE,KAAA,EAAO,OAAA,EAAQ,EAAuC;AAChF,EAAA,uBACI,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACG,KAAA,EAAO;AAAA,QACH,KAAA,EAAO,MAAA;AAAA,QACP,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,cAAA,EAAgB,QAAA;AAAA,QAChB,GAAA,EAAK,OAAA;AAAA,QACL,SAAA,EAAW,QAAA;AAAA,QACX,OAAA,EAAS,OAAA;AAAA,QACT,SAAA,EAAW,YAAA;AAAA,QACX,MAAA,EAAQ,yBAAA;AAAA,QACR,YAAA,EAAc,CAAA;AAAA,QACd,OAAA,EAAS,GAAA;AAAA,QACT,QAAA,EAAU,QAAA;AAAA,QACV,UAAA,EAAY;AAAA,OAChB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAA,EAAY,GAAA,IAAQ,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACzC,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACG,KAAA,EAAO;AAAA,cACH,QAAA,EAAU,QAAA;AAAA,cACV,OAAA,EAAS,IAAA;AAAA,cACT,UAAA,EAAY;AAAA,aAChB;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA;AACL;AAAA;AAAA,GACJ;AAER","file":"chunk-4HHEWTDW.js","sourcesContent":["/**\n * Placeholder shown when an OPTIONAL peer package isn't installed. The\n * chart / code hosts load their peer (`fancy-echarts` / `fancy-code`) via a\n * guarded dynamic import; if that import resolves to a stub (because the\n * consumer never installed the peer), the host renders this instead of\n * crashing — and, crucially, the consumer's build never fails on a missing\n * static import.\n *\n * Inline styles only (no Tailwind), fills 100%×100% so it drops into the\n * element box exactly like the real widget would.\n */\nexport function PeerMissing({ label, install }: { label: string; install: string }) {\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"0.4em\",\n textAlign: \"center\",\n padding: \"0.6em\",\n boxSizing: \"border-box\",\n border: \"1px dashed currentColor\",\n borderRadius: 8,\n opacity: 0.6,\n overflow: \"hidden\",\n fontFamily: \"ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n <span style={{ fontWeight: 600 }}>{label}</span>\n <code\n style={{\n fontSize: \"0.72em\",\n opacity: 0.85,\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\n }}\n >\n {install}\n </code>\n </div>\n );\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { lazy, Suspense } from 'react';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+
4
+ // src/registry/index.tsx
5
+ var ChartHost = lazy(() => import('./chart-host-ZJE7WAHE.js'));
6
+ var CodeHost = lazy(() => import('./code-host-ZLDVJP2X.js'));
7
+ var TableHost = lazy(() => import('./table-host-LWPOQHTL.js'));
8
+ var EmbedHost = lazy(() => import('./embed-host-ZECUEAOU.js'));
9
+ var Loading = ({ label }) => /* @__PURE__ */ jsxs(
10
+ "div",
11
+ {
12
+ style: {
13
+ display: "grid",
14
+ placeItems: "center",
15
+ width: "100%",
16
+ height: "100%",
17
+ color: "rgba(0,0,0,0.4)",
18
+ fontSize: 12,
19
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
20
+ },
21
+ children: [
22
+ label,
23
+ "\u2026"
24
+ ]
25
+ }
26
+ );
27
+ function defaultElementRegistry(element, slideWidthPx) {
28
+ switch (element.type) {
29
+ case "chart":
30
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, { label: "Loading chart" }), children: /* @__PURE__ */ jsx(ChartHost, { element, slideWidthPx }) });
31
+ case "code":
32
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, { label: "Loading code" }), children: /* @__PURE__ */ jsx(CodeHost, { element }) });
33
+ case "table":
34
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, { label: "Loading table" }), children: /* @__PURE__ */ jsx(TableHost, { element }) });
35
+ case "embed":
36
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, { label: "Loading embed" }), children: /* @__PURE__ */ jsx(EmbedHost, { element }) });
37
+ default:
38
+ return void 0;
39
+ }
40
+ }
41
+
42
+ export { defaultElementRegistry };
43
+ //# sourceMappingURL=chunk-YEJZYKVB.js.map
44
+ //# sourceMappingURL=chunk-YEJZYKVB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/index.tsx"],"names":[],"mappings":";;;;AAkBA,IAAM,SAAA,GAAY,IAAA,CAAK,MAAM,OAAO,0BAAgC,CAAC,CAAA;AACrE,IAAM,QAAA,GAAW,IAAA,CAAK,MAAM,OAAO,yBAA+B,CAAC,CAAA;AACnE,IAAM,SAAA,GAAY,IAAA,CAAK,MAAM,OAAO,0BAAgC,CAAC,CAAA;AACrE,IAAM,SAAA,GAAY,IAAA,CAAK,MAAM,OAAO,0BAAgC,CAAC,CAAA;AAErE,IAAM,OAAA,GAAU,CAAC,EAAE,KAAA,EAAM,qBACrB,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACG,KAAA,EAAO;AAAA,MACH,OAAA,EAAS,MAAA;AAAA,MACT,UAAA,EAAY,QAAA;AAAA,MACZ,KAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQ,MAAA;AAAA,MACR,KAAA,EAAO,iBAAA;AAAA,MACP,QAAA,EAAU,EAAA;AAAA,MACV,UAAA,EAAY;AAAA,KAChB;AAAA,IAEC,QAAA,EAAA;AAAA,MAAA,KAAA;AAAA,MAAM;AAAA;AAAA;AACX,CAAA;AAQG,SAAS,sBAAA,CAAuB,SAAuB,YAAA,EAAsB;AAChF,EAAA,QAAQ,QAAQ,IAAA;AAAM,IAClB,KAAK,OAAA;AACD,MAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,eAAA,EAAgB,CAAA,EAC/C,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,OAAA,EAAkC,YAAA,EAA4B,CAAA,EAC7E,CAAA;AAAA,IAER,KAAK,MAAA;AACD,MAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,cAAA,EAAe,CAAA,EAC9C,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAS,OAAA,EAAiC,CAAA,EAC/C,CAAA;AAAA,IAER,KAAK,OAAA;AACD,MAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,eAAA,EAAgB,CAAA,EAC/C,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,OAAA,EAAkC,CAAA,EACjD,CAAA;AAAA,IAER,KAAK,OAAA;AACD,MAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAU,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,eAAA,EAAgB,CAAA,EAC/C,QAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAU,OAAA,EAAkC,CAAA,EACjD,CAAA;AAAA,IAER;AACI,MAAA,OAAO,MAAA;AAAA;AAEnB","file":"chunk-YEJZYKVB.js","sourcesContent":["/**\n * Default element registry. Wires up the element types fancy-slides doesn't\n * render natively (chart / code / table / embed) by composing the other\n * fancy packages as optional peer deps.\n *\n * Hosts opt into this by importing from the `/registry` subpath:\n *\n * import { defaultElementRegistry } from \"@particle-academy/fancy-slides/registry\";\n * <DeckEditor renderElement={defaultElementRegistry} … />\n *\n * Keeping it behind a subpath means consumers who only render decks with\n * built-in element types (text / image / shape) never pull fancy-echarts,\n * fancy-code, or react-fancy Table into their bundle.\n */\n\nimport { lazy, Suspense } from \"react\";\nimport type { ChartElement, CodeElement, SlideElement, EmbedElement, TableElement } from \"../types\";\n\nconst ChartHost = lazy(() => import(\"./element-renderers/chart-host\"));\nconst CodeHost = lazy(() => import(\"./element-renderers/code-host\"));\nconst TableHost = lazy(() => import(\"./element-renderers/table-host\"));\nconst EmbedHost = lazy(() => import(\"./element-renderers/embed-host\"));\n\nconst Loading = ({ label }: { label: string }) => (\n <div\n style={{\n display: \"grid\",\n placeItems: \"center\",\n width: \"100%\",\n height: \"100%\",\n color: \"rgba(0,0,0,0.4)\",\n fontSize: 12,\n fontFamily: \"ui-sans-serif, system-ui, sans-serif\",\n }}\n >\n {label}…\n </div>\n);\n\n/**\n * Renderer signature compatible with `Slide`'s `renderElement` prop. Returns\n * `undefined` for element types fancy-slides handles itself, so the call\n * site falls back to the built-in renderer.\n */\nexport function defaultElementRegistry(element: SlideElement, slideWidthPx: number) {\n switch (element.type) {\n case \"chart\":\n return (\n <Suspense fallback={<Loading label=\"Loading chart\" />}>\n <ChartHost element={element as ChartElement} slideWidthPx={slideWidthPx} />\n </Suspense>\n );\n case \"code\":\n return (\n <Suspense fallback={<Loading label=\"Loading code\" />}>\n <CodeHost element={element as CodeElement} />\n </Suspense>\n );\n case \"table\":\n return (\n <Suspense fallback={<Loading label=\"Loading table\" />}>\n <TableHost element={element as TableElement} />\n </Suspense>\n );\n case \"embed\":\n return (\n <Suspense fallback={<Loading label=\"Loading embed\" />}>\n <EmbedHost element={element as EmbedElement} />\n </Suspense>\n );\n default:\n return undefined;\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { PeerMissing } from './chunk-4HHEWTDW.js';
2
+ import { lazy, Suspense } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var CodeInner = lazy(async () => {
6
+ try {
7
+ const mod = await import('@particle-academy/fancy-code');
8
+ const CodeEditor = mod.CodeEditor;
9
+ if (!CodeEditor) {
10
+ return { default: () => /* @__PURE__ */ jsx(PeerMissing, { label: "Code", install: "npm i @particle-academy/fancy-code" }) };
11
+ }
12
+ return {
13
+ default: ({ code, language, codeTheme, lineNumbers }) => /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%", overflow: "hidden", borderRadius: 8 }, children: /* @__PURE__ */ jsx(CodeEditor, { value: code, language, theme: codeTheme, readOnly: true, lineNumbers, children: /* @__PURE__ */ jsx(CodeEditor.Panel, {}) }) })
14
+ };
15
+ } catch {
16
+ return { default: () => /* @__PURE__ */ jsx(PeerMissing, { label: "Code", install: "npm i @particle-academy/fancy-code" }) };
17
+ }
18
+ });
19
+ function CodeHost({ element }) {
20
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
21
+ CodeInner,
22
+ {
23
+ code: element.code,
24
+ language: element.language ?? "javascript",
25
+ codeTheme: element.codeTheme ?? "dark",
26
+ lineNumbers: element.lineNumbers ?? true
27
+ }
28
+ ) });
29
+ }
30
+
31
+ export { CodeHost as default };
32
+ //# sourceMappingURL=code-host-ZLDVJP2X.js.map
33
+ //# sourceMappingURL=code-host-ZLDVJP2X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/element-renderers/code-host.tsx"],"names":[],"mappings":";;;;AAkBA,IAAM,SAAA,GAAuC,KAAK,YAAY;AAC1D,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAA+B,MAAM,OAAO,8BAA8B,CAAA;AAEhF,IAAA,MAAM,aAAa,GAAA,CAAI,UAAA;AAUvB,IAAA,IAAI,CAAC,UAAA,EAAY;AACb,MAAA,OAAO,EAAE,SAAS,sBAAM,GAAA,CAAC,eAAY,KAAA,EAAM,MAAA,EAAO,OAAA,EAAQ,oCAAA,EAAqC,CAAA,EAAG;AAAA,IACtG;AACA,IAAA,OAAO;AAAA,MACH,SAAS,CAAC,EAAE,IAAA,EAAM,QAAA,EAAU,WAAW,WAAA,EAAY,qBAC/C,GAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,QAAQ,QAAA,EAAU,QAAA,EAAU,YAAA,EAAc,CAAA,IAC3E,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAO,IAAA,EAAM,UAAoB,KAAA,EAAO,SAAA,EAAW,QAAA,EAAQ,IAAA,EAAC,aACpE,QAAA,kBAAA,GAAA,CAAC,UAAA,CAAW,KAAA,EAAX,EAAiB,GACtB,CAAA,EACJ;AAAA,KAER;AAAA,EACJ,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,EAAE,SAAS,sBAAM,GAAA,CAAC,eAAY,KAAA,EAAM,MAAA,EAAO,OAAA,EAAQ,oCAAA,EAAqC,CAAA,EAAG;AAAA,EACtG;AACJ,CAAC,CAAA;AAEc,SAAR,QAAA,CAA0B,EAAE,OAAA,EAAQ,EAA6B;AACpE,EAAA,uBACI,GAAA,CAAC,QAAA,EAAA,EAAS,QAAA,EAAU,IAAA,EAChB,QAAA,kBAAA,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACG,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,QAAA,EAAU,QAAQ,QAAA,IAAY,YAAA;AAAA,MAC9B,SAAA,EAAW,QAAQ,SAAA,IAAa,MAAA;AAAA,MAChC,WAAA,EAAa,QAAQ,WAAA,IAAe;AAAA;AAAA,GACxC,EACJ,CAAA;AAER","file":"code-host-ZLDVJP2X.js","sourcesContent":["import { lazy, Suspense, type ComponentType } from \"react\";\nimport type { CodeElement } from \"../../types\";\nimport { PeerMissing } from \"./peer-missing\";\n\n/**\n * fancy-code is an OPTIONAL peer. As with chart-host, a static\n * `import { CodeEditor } from \"@particle-academy/fancy-code\"` would make\n * Rollup statically resolve the peer at CONSUMER build time and fail with\n * MISSING_EXPORT when it isn't installed. We load it via a DYNAMIC import and\n * read `CodeEditor` off the resolved module at RUNTIME, guarding the stub case.\n */\ninterface InnerProps {\n code: string;\n language: string;\n codeTheme: string;\n lineNumbers: boolean;\n}\n\nconst CodeInner: ComponentType<InnerProps> = lazy(async () => {\n try {\n const mod: Record<string, unknown> = await import(\"@particle-academy/fancy-code\");\n // CodeEditor is a component with a static `.Panel` sub-component.\n const CodeEditor = mod.CodeEditor as\n | (ComponentType<{\n value: string;\n language: string;\n theme: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n children?: React.ReactNode;\n }> & { Panel: ComponentType })\n | undefined;\n if (!CodeEditor) {\n return { default: () => <PeerMissing label=\"Code\" install=\"npm i @particle-academy/fancy-code\" /> };\n }\n return {\n default: ({ code, language, codeTheme, lineNumbers }: InnerProps) => (\n <div style={{ width: \"100%\", height: \"100%\", overflow: \"hidden\", borderRadius: 8 }}>\n <CodeEditor value={code} language={language} theme={codeTheme} readOnly lineNumbers={lineNumbers}>\n <CodeEditor.Panel />\n </CodeEditor>\n </div>\n ),\n };\n } catch {\n return { default: () => <PeerMissing label=\"Code\" install=\"npm i @particle-academy/fancy-code\" /> };\n }\n});\n\nexport default function CodeHost({ element }: { element: CodeElement }) {\n return (\n <Suspense fallback={null}>\n <CodeInner\n code={element.code}\n language={element.language ?? \"javascript\"}\n codeTheme={element.codeTheme ?? \"dark\"}\n lineNumbers={element.lineNumbers ?? true}\n />\n </Suspense>\n );\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1,10 +1,218 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
- var reactFancy = require('@particle-academy/react-fancy');
5
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var reactFancy = require('@particle-academy/react-fancy');
6
6
 
7
- // src/components/Slide/Slide.tsx
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ function useSlideContext() {
17
+ return react.useContext(SlideContext);
18
+ }
19
+ function useSlideTheme() {
20
+ return react.useContext(SlideContext)?.theme;
21
+ }
22
+ function useIsDarkSlide() {
23
+ return react.useContext(SlideContext)?.isDark ?? false;
24
+ }
25
+ function isDarkColor(color) {
26
+ const m = color.match(/^#([0-9a-f]{3,8})$/i);
27
+ if (m) {
28
+ let hex = m[1];
29
+ if (hex.length === 3) {
30
+ hex = hex.split("").map((c) => c + c).join("");
31
+ }
32
+ if (hex.length >= 6) {
33
+ const r = parseInt(hex.slice(0, 2), 16);
34
+ const g = parseInt(hex.slice(2, 4), 16);
35
+ const b = parseInt(hex.slice(4, 6), 16);
36
+ return relativeLuminance(r, g, b) < 0.5;
37
+ }
38
+ }
39
+ const rgb = color.match(/rgba?\(([^)]+)\)/i);
40
+ if (rgb) {
41
+ const [r, g, b] = rgb[1].split(",").map((s) => parseInt(s.trim(), 10));
42
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
43
+ return relativeLuminance(r, g, b) < 0.5;
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ function relativeLuminance(r, g, b) {
49
+ const toLinear = (c) => {
50
+ const v = c / 255;
51
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
52
+ };
53
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
54
+ }
55
+ var SlideContext;
56
+ var init_slide_context = __esm({
57
+ "src/components/Slide/slide-context.ts"() {
58
+ SlideContext = react.createContext(null);
59
+ }
60
+ });
61
+ function PeerMissing({ label, install }) {
62
+ return /* @__PURE__ */ jsxRuntime.jsxs(
63
+ "div",
64
+ {
65
+ style: {
66
+ width: "100%",
67
+ height: "100%",
68
+ display: "flex",
69
+ flexDirection: "column",
70
+ alignItems: "center",
71
+ justifyContent: "center",
72
+ gap: "0.4em",
73
+ textAlign: "center",
74
+ padding: "0.6em",
75
+ boxSizing: "border-box",
76
+ border: "1px dashed currentColor",
77
+ borderRadius: 8,
78
+ opacity: 0.6,
79
+ overflow: "hidden",
80
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600 }, children: label }),
84
+ /* @__PURE__ */ jsxRuntime.jsx(
85
+ "code",
86
+ {
87
+ style: {
88
+ fontSize: "0.72em",
89
+ opacity: 0.85,
90
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
91
+ },
92
+ children: install
93
+ }
94
+ )
95
+ ]
96
+ }
97
+ );
98
+ }
99
+ var init_peer_missing = __esm({
100
+ "src/registry/element-renderers/peer-missing.tsx"() {
101
+ }
102
+ });
103
+
104
+ // src/registry/element-renderers/chart-host.tsx
105
+ var chart_host_exports = {};
106
+ __export(chart_host_exports, {
107
+ default: () => ChartHost
108
+ });
109
+ function ChartHost({ element }) {
110
+ const isDarkSlide = useIsDarkSlide();
111
+ const theme = element.chartTheme ?? (isDarkSlide ? "dark" : void 0);
112
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(ChartInner, { option: element.option, theme }) });
113
+ }
114
+ var ChartInner;
115
+ var init_chart_host = __esm({
116
+ "src/registry/element-renderers/chart-host.tsx"() {
117
+ init_slide_context();
118
+ init_peer_missing();
119
+ ChartInner = react.lazy(async () => {
120
+ try {
121
+ const mod = await import('@particle-academy/fancy-echarts');
122
+ const EChart = mod.EChart;
123
+ if (!EChart) {
124
+ return { default: () => /* @__PURE__ */ jsxRuntime.jsx(PeerMissing, { label: "Chart", install: "npm i @particle-academy/fancy-echarts" }) };
125
+ }
126
+ mod.registerAll?.();
127
+ mod.registerBuiltinThemes?.();
128
+ return {
129
+ default: ({ option, theme }) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(EChart, { option, theme }) })
130
+ };
131
+ } catch {
132
+ return { default: () => /* @__PURE__ */ jsxRuntime.jsx(PeerMissing, { label: "Chart", install: "npm i @particle-academy/fancy-echarts" }) };
133
+ }
134
+ });
135
+ }
136
+ });
137
+
138
+ // src/registry/element-renderers/code-host.tsx
139
+ var code_host_exports = {};
140
+ __export(code_host_exports, {
141
+ default: () => CodeHost
142
+ });
143
+ function CodeHost({ element }) {
144
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
145
+ CodeInner,
146
+ {
147
+ code: element.code,
148
+ language: element.language ?? "javascript",
149
+ codeTheme: element.codeTheme ?? "dark",
150
+ lineNumbers: element.lineNumbers ?? true
151
+ }
152
+ ) });
153
+ }
154
+ var CodeInner;
155
+ var init_code_host = __esm({
156
+ "src/registry/element-renderers/code-host.tsx"() {
157
+ init_peer_missing();
158
+ CodeInner = react.lazy(async () => {
159
+ try {
160
+ const mod = await import('@particle-academy/fancy-code');
161
+ const CodeEditor = mod.CodeEditor;
162
+ if (!CodeEditor) {
163
+ return { default: () => /* @__PURE__ */ jsxRuntime.jsx(PeerMissing, { label: "Code", install: "npm i @particle-academy/fancy-code" }) };
164
+ }
165
+ return {
166
+ default: ({ code, language, codeTheme, lineNumbers }) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%", overflow: "hidden", borderRadius: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(CodeEditor, { value: code, language, theme: codeTheme, readOnly: true, lineNumbers, children: /* @__PURE__ */ jsxRuntime.jsx(CodeEditor.Panel, {}) }) })
167
+ };
168
+ } catch {
169
+ return { default: () => /* @__PURE__ */ jsxRuntime.jsx(PeerMissing, { label: "Code", install: "npm i @particle-academy/fancy-code" }) };
170
+ }
171
+ });
172
+ }
173
+ });
174
+
175
+ // src/registry/element-renderers/table-host.tsx
176
+ var table_host_exports = {};
177
+ __export(table_host_exports, {
178
+ default: () => TableHost
179
+ });
180
+ function TableHost({ element }) {
181
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "100%", height: "100%", overflow: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Table, { className: "w-full", children: [
182
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Head, { children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Row, { children: element.columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Cell, { header: true, children: c.label }, c.key)) }) }),
183
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Body, { children: element.rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Row, { children: element.columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Table.Cell, { children: formatCell(row[c.key]) }, c.key)) }, i)) })
184
+ ] }) });
185
+ }
186
+ function formatCell(v) {
187
+ if (v == null) return "";
188
+ if (typeof v === "object") return JSON.stringify(v);
189
+ return String(v);
190
+ }
191
+ var init_table_host = __esm({
192
+ "src/registry/element-renderers/table-host.tsx"() {
193
+ }
194
+ });
195
+
196
+ // src/registry/element-renderers/embed-host.tsx
197
+ var embed_host_exports = {};
198
+ __export(embed_host_exports, {
199
+ default: () => EmbedHost
200
+ });
201
+ function EmbedHost({ element }) {
202
+ return /* @__PURE__ */ jsxRuntime.jsx(
203
+ "iframe",
204
+ {
205
+ src: element.src,
206
+ title: element.title ?? "Embedded content",
207
+ sandbox: element.sandbox ?? "allow-scripts",
208
+ style: { width: "100%", height: "100%", border: 0, display: "block" }
209
+ }
210
+ );
211
+ }
212
+ var init_embed_host = __esm({
213
+ "src/registry/element-renderers/embed-host.tsx"() {
214
+ }
215
+ });
8
216
 
9
217
  // src/theme/default-theme.ts
10
218
  var defaultTheme = {
@@ -461,46 +669,9 @@ function renderShape(el, s) {
461
669
  return null;
462
670
  }
463
671
  }
464
- var SlideContext = react.createContext(null);
465
- function useSlideContext() {
466
- return react.useContext(SlideContext);
467
- }
468
- function useSlideTheme() {
469
- return react.useContext(SlideContext)?.theme;
470
- }
471
- function useIsDarkSlide() {
472
- return react.useContext(SlideContext)?.isDark ?? false;
473
- }
474
- function isDarkColor(color) {
475
- const m = color.match(/^#([0-9a-f]{3,8})$/i);
476
- if (m) {
477
- let hex = m[1];
478
- if (hex.length === 3) {
479
- hex = hex.split("").map((c) => c + c).join("");
480
- }
481
- if (hex.length >= 6) {
482
- const r = parseInt(hex.slice(0, 2), 16);
483
- const g = parseInt(hex.slice(2, 4), 16);
484
- const b = parseInt(hex.slice(4, 6), 16);
485
- return relativeLuminance(r, g, b) < 0.5;
486
- }
487
- }
488
- const rgb = color.match(/rgba?\(([^)]+)\)/i);
489
- if (rgb) {
490
- const [r, g, b] = rgb[1].split(",").map((s) => parseInt(s.trim(), 10));
491
- if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
492
- return relativeLuminance(r, g, b) < 0.5;
493
- }
494
- }
495
- return false;
496
- }
497
- function relativeLuminance(r, g, b) {
498
- const toLinear = (c) => {
499
- const v = c / 255;
500
- return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
501
- };
502
- return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
503
- }
672
+
673
+ // src/components/Slide/Slide.tsx
674
+ init_slide_context();
504
675
  function Slide({
505
676
  slide,
506
677
  theme,
@@ -718,7 +889,7 @@ function SlideElementHost({
718
889
  touchAction: canMove ? "none" : void 0,
719
890
  ...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
720
891
  };
721
- const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx);
892
+ const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx) ?? elementPlaceholder(element);
722
893
  return /* @__PURE__ */ jsxRuntime.jsxs(
723
894
  "div",
724
895
  {
@@ -813,6 +984,37 @@ function renderInner({ element, theme, slideWidthPx, editing, selected, onConten
813
984
  return null;
814
985
  }
815
986
  }
987
+ function elementPlaceholder(element) {
988
+ if (element.type !== "chart" && element.type !== "code" && element.type !== "table" && element.type !== "embed") {
989
+ return null;
990
+ }
991
+ const label = element.type.charAt(0).toUpperCase() + element.type.slice(1);
992
+ return /* @__PURE__ */ jsxRuntime.jsxs(
993
+ "div",
994
+ {
995
+ style: {
996
+ width: "100%",
997
+ height: "100%",
998
+ display: "flex",
999
+ flexDirection: "column",
1000
+ alignItems: "center",
1001
+ justifyContent: "center",
1002
+ gap: "0.35em",
1003
+ textAlign: "center",
1004
+ padding: "0.5em",
1005
+ boxSizing: "border-box",
1006
+ border: "1px dashed currentColor",
1007
+ borderRadius: 8,
1008
+ opacity: 0.55,
1009
+ overflow: "hidden"
1010
+ },
1011
+ children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600 }, children: label }),
1013
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.7em", opacity: 0.8 }, children: "Pass renderElement to render" })
1014
+ ]
1015
+ }
1016
+ );
1017
+ }
816
1018
  function orderedElements(elements) {
817
1019
  return [...elements].sort((a, b) => {
818
1020
  const az = a.z ?? -1;
@@ -845,6 +1047,9 @@ function computeResize(drag, dx, dy) {
845
1047
  }
846
1048
  return { x, y, w, h };
847
1049
  }
1050
+
1051
+ // src/index.ts
1052
+ init_slide_context();
848
1053
  function useSlideKeyboard({
849
1054
  total,
850
1055
  index,
@@ -919,6 +1124,42 @@ function useSlideKeyboard({
919
1124
  return () => window.removeEventListener("keydown", handler);
920
1125
  }, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
921
1126
  }
1127
+ var ChartHost2 = react.lazy(() => Promise.resolve().then(() => (init_chart_host(), chart_host_exports)));
1128
+ var CodeHost2 = react.lazy(() => Promise.resolve().then(() => (init_code_host(), code_host_exports)));
1129
+ var TableHost2 = react.lazy(() => Promise.resolve().then(() => (init_table_host(), table_host_exports)));
1130
+ var EmbedHost2 = react.lazy(() => Promise.resolve().then(() => (init_embed_host(), embed_host_exports)));
1131
+ var Loading = ({ label }) => /* @__PURE__ */ jsxRuntime.jsxs(
1132
+ "div",
1133
+ {
1134
+ style: {
1135
+ display: "grid",
1136
+ placeItems: "center",
1137
+ width: "100%",
1138
+ height: "100%",
1139
+ color: "rgba(0,0,0,0.4)",
1140
+ fontSize: 12,
1141
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
1142
+ },
1143
+ children: [
1144
+ label,
1145
+ "\u2026"
1146
+ ]
1147
+ }
1148
+ );
1149
+ function defaultElementRegistry(element, slideWidthPx) {
1150
+ switch (element.type) {
1151
+ case "chart":
1152
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Loading, { label: "Loading chart" }), children: /* @__PURE__ */ jsxRuntime.jsx(ChartHost2, { element, slideWidthPx }) });
1153
+ case "code":
1154
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Loading, { label: "Loading code" }), children: /* @__PURE__ */ jsxRuntime.jsx(CodeHost2, { element }) });
1155
+ case "table":
1156
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Loading, { label: "Loading table" }), children: /* @__PURE__ */ jsxRuntime.jsx(TableHost2, { element }) });
1157
+ case "embed":
1158
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Loading, { label: "Loading embed" }), children: /* @__PURE__ */ jsxRuntime.jsx(EmbedHost2, { element }) });
1159
+ default:
1160
+ return void 0;
1161
+ }
1162
+ }
922
1163
  function SlideViewer({
923
1164
  deck,
924
1165
  index: controlledIndex,
@@ -927,7 +1168,7 @@ function SlideViewer({
927
1168
  onExit,
928
1169
  autoAdvanceMs,
929
1170
  hideChrome = false,
930
- renderElement,
1171
+ renderElement = defaultElementRegistry,
931
1172
  className
932
1173
  }) {
933
1174
  const isControlled = controlledIndex !== void 0;
@@ -2688,7 +2929,7 @@ function DeckEditor({
2688
2929
  onPresent,
2689
2930
  selectedSlideId: controlledSlideId,
2690
2931
  onSelectedSlideChange,
2691
- renderElement,
2932
+ renderElement = defaultElementRegistry,
2692
2933
  hideRail = false,
2693
2934
  hideNotes = false,
2694
2935
  hideToolbar = false,