@particle-academy/fancy-slides 0.5.1 → 0.6.1

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,
@@ -876,6 +1047,9 @@ function computeResize(drag, dx, dy) {
876
1047
  }
877
1048
  return { x, y, w, h };
878
1049
  }
1050
+
1051
+ // src/index.ts
1052
+ init_slide_context();
879
1053
  function useSlideKeyboard({
880
1054
  total,
881
1055
  index,
@@ -950,6 +1124,42 @@ function useSlideKeyboard({
950
1124
  return () => window.removeEventListener("keydown", handler);
951
1125
  }, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
952
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
+ }
953
1163
  function SlideViewer({
954
1164
  deck,
955
1165
  index: controlledIndex,
@@ -958,7 +1168,7 @@ function SlideViewer({
958
1168
  onExit,
959
1169
  autoAdvanceMs,
960
1170
  hideChrome = false,
961
- renderElement,
1171
+ renderElement = defaultElementRegistry,
962
1172
  className
963
1173
  }) {
964
1174
  const isControlled = controlledIndex !== void 0;
@@ -1448,6 +1658,11 @@ function SlideThumbnail({
1448
1658
  className,
1449
1659
  style
1450
1660
  }) {
1661
+ const resolved = resolveTheme(theme);
1662
+ const ratio = resolved.aspectRatio ?? 16 / 9;
1663
+ const designWidth = resolved.slideWidth ?? 1280;
1664
+ const scale = width / designWidth;
1665
+ const height = width / ratio;
1451
1666
  return /* @__PURE__ */ jsxRuntime.jsx(
1452
1667
  "div",
1453
1668
  {
@@ -1460,12 +1675,27 @@ function SlideThumbnail({
1460
1675
  boxShadow: active ? "0 0 0 3px rgba(139, 92, 246, 0.2)" : "0 1px 2px rgba(0,0,0,0.05)",
1461
1676
  background: "#ffffff",
1462
1677
  width,
1678
+ height,
1463
1679
  ...style
1464
1680
  },
1465
1681
  onClick,
1466
1682
  onContextMenu,
1467
1683
  "data-fancy-slides-thumbnail": slide.id,
1468
- children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, width, renderElement })
1684
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1685
+ "div",
1686
+ {
1687
+ style: {
1688
+ width: designWidth,
1689
+ height: designWidth / ratio,
1690
+ transform: `scale(${scale})`,
1691
+ transformOrigin: "top left",
1692
+ // The thumb owns interaction — charts/code/iframes inside the
1693
+ // scaled slide shouldn't capture clicks.
1694
+ pointerEvents: "none"
1695
+ },
1696
+ children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, width: designWidth, renderElement })
1697
+ }
1698
+ )
1469
1699
  }
1470
1700
  );
1471
1701
  }
@@ -2719,7 +2949,7 @@ function DeckEditor({
2719
2949
  onPresent,
2720
2950
  selectedSlideId: controlledSlideId,
2721
2951
  onSelectedSlideChange,
2722
- renderElement,
2952
+ renderElement = defaultElementRegistry,
2723
2953
  hideRail = false,
2724
2954
  hideNotes = false,
2725
2955
  hideToolbar = false,