@particle-academy/fancy-slides 0.7.0 → 0.9.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.
package/dist/index.cjs CHANGED
@@ -940,7 +940,18 @@ function SlideElementHost({
940
940
  touchAction: canMove ? "none" : void 0,
941
941
  ...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
942
942
  };
943
- const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx) ?? elementPlaceholder(element);
943
+ const rendered = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx) ?? elementPlaceholder(element);
944
+ const inner = element.href && !editing ? /* @__PURE__ */ jsxRuntime.jsx(
945
+ "a",
946
+ {
947
+ href: element.href,
948
+ target: "_blank",
949
+ rel: "noreferrer",
950
+ style: { display: "block", width: "100%", height: "100%", color: "inherit", textDecoration: "inherit" },
951
+ "data-fancy-slides-href": "",
952
+ children: rendered
953
+ }
954
+ ) : rendered;
944
955
  return /* @__PURE__ */ jsxRuntime.jsxs(
945
956
  "div",
946
957
  {
@@ -2250,10 +2261,10 @@ function EditorToolbar({
2250
2261
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
2251
2262
  ] });
2252
2263
  }
2253
- function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
2264
+ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetLayout, onSetAnimation, onSetElementAnimation }) {
2254
2265
  if (!element) {
2255
2266
  if (slide) {
2256
- return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
2267
+ return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetLayout, onSetElementAnimation });
2257
2268
  }
2258
2269
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fs-inspector flex h-full flex-col border-l border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-900", children: [
2259
2270
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
@@ -2284,16 +2295,32 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
2284
2295
  /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Tabs.Panels, { children: [
2285
2296
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "style", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(StyleSection, { element, onPatch }) }) }),
2286
2297
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "build", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AnimateSection, { animation: element.animation, onSetAnimation, isText: element.type === "text" }) }) }),
2287
- /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(LayoutSection, { element, onPatch }) }) }),
2298
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(LayoutSection, { element, onPatch, siblings: slide?.elements ?? [] }) }) }),
2288
2299
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "advanced", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AdvancedSection, { element, onPatch }) }) })
2289
2300
  ] })
2290
2301
  ] }) })
2291
2302
  ] });
2292
2303
  }
2304
+ var SLIDE_LAYOUTS = [
2305
+ { value: "blank", label: "Blank" },
2306
+ { value: "title", label: "Title" },
2307
+ { value: "title-content", label: "Title + content" },
2308
+ { value: "two-column", label: "Two column" },
2309
+ { value: "section-divider", label: "Section divider" },
2310
+ { value: "image-text", label: "Image + text" },
2311
+ { value: "text-image", label: "Text + image" },
2312
+ { value: "quote", label: "Quote" }
2313
+ ];
2314
+ function backgroundMode(bg) {
2315
+ if (bg?.gradient) return "gradient";
2316
+ if (bg?.image) return "image";
2317
+ return "color";
2318
+ }
2293
2319
  function SlideSettings({
2294
2320
  slide,
2295
2321
  onSetTransition,
2296
2322
  onSetBackground,
2323
+ onSetLayout,
2297
2324
  onSetElementAnimation
2298
2325
  }) {
2299
2326
  const transition = slide.transition;
@@ -2302,6 +2329,7 @@ function SlideSettings({
2302
2329
  const merged = { kind, duration: transition?.duration, direction: transition?.direction, ...next };
2303
2330
  onSetTransition?.(merged.kind === "none" ? { kind: "none" } : merged);
2304
2331
  };
2332
+ const bgMode = backgroundMode(slide.background);
2305
2333
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fs-inspector flex h-full w-full flex-col border-l border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900", children: [
2306
2334
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2307
2335
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!font-mono !uppercase !tracking-wider !text-zinc-500", children: "slide" }),
@@ -2311,6 +2339,19 @@ function SlideSettings({
2311
2339
  ] })
2312
2340
  ] }) }),
2313
2341
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
2342
+ onSetLayout && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mb-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2343
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Layout" }),
2344
+ /* @__PURE__ */ jsxRuntime.jsx(
2345
+ reactFancy.Select,
2346
+ {
2347
+ label: "Preset",
2348
+ list: SLIDE_LAYOUTS,
2349
+ value: slide.layout ?? "blank",
2350
+ onValueChange: (v) => onSetLayout(v)
2351
+ }
2352
+ ),
2353
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "The layout hint the deck commits to \u2014 carried through to the pptx export's slide layout." })
2354
+ ] }) }),
2314
2355
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2315
2356
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Transition" }),
2316
2357
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2354,13 +2395,63 @@ function SlideSettings({
2354
2395
  ] }) }),
2355
2396
  onSetBackground && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2356
2397
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Background" }),
2357
- /* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(
2398
+ /* @__PURE__ */ jsxRuntime.jsx(
2399
+ reactFancy.Select,
2400
+ {
2401
+ label: "Type",
2402
+ list: [
2403
+ { value: "color", label: "Solid color" },
2404
+ { value: "gradient", label: "Gradient" },
2405
+ { value: "image", label: "Image" }
2406
+ ],
2407
+ value: bgMode,
2408
+ onValueChange: (v) => {
2409
+ if (v === "color") onSetBackground({ color: slide.background?.color ?? "#ffffff" });
2410
+ else if (v === "gradient") onSetBackground({ gradient: slide.background?.gradient ?? "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)" });
2411
+ else onSetBackground({ image: slide.background?.image ?? "", imageFit: slide.background?.imageFit ?? "cover", color: slide.background?.color });
2412
+ }
2413
+ }
2414
+ ),
2415
+ bgMode === "color" && /* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(
2358
2416
  reactFancy.ColorPicker,
2359
2417
  {
2360
2418
  value: slide.background?.color ?? "#ffffff",
2361
- onChange: (c) => onSetBackground({ ...slide.background, color: c })
2419
+ onChange: (c) => onSetBackground({ color: c })
2420
+ }
2421
+ ) }),
2422
+ bgMode === "gradient" && /* @__PURE__ */ jsxRuntime.jsx(
2423
+ reactFancy.Textarea,
2424
+ {
2425
+ label: "CSS gradient",
2426
+ value: slide.background?.gradient ?? "",
2427
+ onValueChange: (v) => onSetBackground({ gradient: v }),
2428
+ rows: 2
2362
2429
  }
2363
- ) })
2430
+ ),
2431
+ bgMode === "image" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2432
+ /* @__PURE__ */ jsxRuntime.jsx(
2433
+ reactFancy.Textarea,
2434
+ {
2435
+ label: "Image URL",
2436
+ value: slide.background?.image ?? "",
2437
+ onValueChange: (v) => onSetBackground({ ...slide.background, image: v }),
2438
+ rows: 2
2439
+ }
2440
+ ),
2441
+ /* @__PURE__ */ jsxRuntime.jsx(
2442
+ reactFancy.Select,
2443
+ {
2444
+ label: "Fit",
2445
+ list: [
2446
+ { value: "cover", label: "Cover" },
2447
+ { value: "contain", label: "Contain" },
2448
+ { value: "fill", label: "Fill (stretch)" }
2449
+ ],
2450
+ value: slide.background?.imageFit ?? "cover",
2451
+ onValueChange: (v) => onSetBackground({ ...slide.background, imageFit: v })
2452
+ }
2453
+ )
2454
+ ] })
2364
2455
  ] }) }),
2365
2456
  onSetElementAnimation && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(BuildOrderList, { slide, onSetElementAnimation }) })
2366
2457
  ] })
@@ -2409,7 +2500,10 @@ function buildLabel(element) {
2409
2500
  }
2410
2501
  return `${element.type} #${element.id.slice(-6)}`;
2411
2502
  }
2412
- function LayoutSection({ element, onPatch }) {
2503
+ function LayoutSection({ element, onPatch, siblings }) {
2504
+ const zs = siblings.map((e) => e.z ?? 0);
2505
+ const bringToFront = () => onPatch({ z: (zs.length ? Math.max(...zs) : 0) + 1 });
2506
+ const sendToBack = () => onPatch({ z: (zs.length ? Math.min(...zs) : 0) - 1 });
2413
2507
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2414
2508
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
2415
2509
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "X", type: "number", value: String(roundFrac(element.x)), onChange: (e) => onPatch({ x: clamp2(parseFloat(e.target.value), 0, 1) }) }),
@@ -2419,7 +2513,26 @@ function LayoutSection({ element, onPatch }) {
2419
2513
  ] }),
2420
2514
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
2421
2515
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Rotation", value: element.rotation ?? 0, onValueChange: (v) => onPatch({ rotation: Number(v) }), min: -180, max: 180 }),
2422
- /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Z-index", type: "number", value: String(element.z ?? 0), onChange: (e) => onPatch({ z: parseInt(e.target.value, 10) || 0 }) })
2516
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
2517
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Z-index", type: "number", value: String(element.z ?? 0), onChange: (e) => onPatch({ z: parseInt(e.target.value, 10) || 0 }), className: "flex-1" }),
2518
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: "ghost", onClick: bringToFront, "aria-label": "Bring to front", children: "Front" }),
2519
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: "ghost", onClick: sendToBack, "aria-label": "Send to back", children: "Back" })
2520
+ ] }),
2521
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
2522
+ /* @__PURE__ */ jsxRuntime.jsx(
2523
+ reactFancy.Input,
2524
+ {
2525
+ label: "Link (href)",
2526
+ value: element.href ?? "",
2527
+ placeholder: "https://\u2026",
2528
+ onChange: (e) => onPatch({ href: e.target.value || void 0 })
2529
+ }
2530
+ ),
2531
+ /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: [
2532
+ "Makes the whole element a click target in the viewer (opens a new tab) and exports as a pptx hyperlink. For links inside text, use markdown ",
2533
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "[label](url)" }),
2534
+ "."
2535
+ ] })
2423
2536
  ] });
2424
2537
  }
2425
2538
  function AdvancedSection({ element, onPatch }) {
@@ -2601,6 +2714,24 @@ function TextStyleControls({ element, onPatch }) {
2601
2714
  onValueChange: (v) => setStyle({ align: v })
2602
2715
  }
2603
2716
  ),
2717
+ /* @__PURE__ */ jsxRuntime.jsx(
2718
+ reactFancy.Select,
2719
+ {
2720
+ label: "Vertical align",
2721
+ list: [
2722
+ { value: "top", label: "Top" },
2723
+ { value: "middle", label: "Middle" },
2724
+ { value: "bottom", label: "Bottom" }
2725
+ ],
2726
+ value: s.verticalAlign ?? "top",
2727
+ onValueChange: (v) => setStyle({ verticalAlign: v })
2728
+ }
2729
+ ),
2730
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Line height", type: "number", value: String(s.lineHeight ?? 1.4), onChange: (e) => setStyle({ lineHeight: parseFloat(e.target.value) || 1.4 }) }),
2731
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
2732
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Switch, { label: "Italic", checked: !!s.italic, onCheckedChange: (v) => setStyle({ italic: v }) }),
2733
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Switch, { label: "Underline", checked: !!s.underline, onCheckedChange: (v) => setStyle({ underline: v }) })
2734
+ ] }),
2604
2735
  /* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ColorPicker, { value: s.color ?? "#0f172a", onChange: (c) => setStyle({ color: c }) }) })
2605
2736
  ] });
2606
2737
  }
@@ -2683,6 +2814,7 @@ function ShapeStyleControls({ element, onPatch }) {
2683
2814
  /* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Fill", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ColorPicker, { value: element.fill ?? "#ffffff", onChange: (c) => onPatch({ fill: c }) }) }),
2684
2815
  /* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Stroke", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ColorPicker, { value: element.stroke ?? "#0f172a", onChange: (c) => onPatch({ stroke: c }) }) }),
2685
2816
  /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Stroke width", value: element.strokeWidth ?? 2, onValueChange: (v) => onPatch({ strokeWidth: Number(v) }), min: 0, max: 20, step: 0.5 }),
2817
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Switch, { label: "Dashed stroke", checked: !!element.dashed, onCheckedChange: (v) => onPatch({ dashed: v }) }),
2686
2818
  (element.shape === "rounded-rect" || element.shape === "rect") && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Corner radius", value: element.radius ?? 0, onValueChange: (v) => onPatch({ radius: Number(v) }), min: 0, max: 40 })
2687
2819
  ] });
2688
2820
  }
@@ -2702,7 +2834,8 @@ function CodeStyleControls({ element, onPatch }) {
2702
2834
  value: element.codeTheme ?? "auto",
2703
2835
  onValueChange: (v) => onPatch({ codeTheme: v })
2704
2836
  }
2705
- )
2837
+ ),
2838
+ /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Switch, { label: "Line numbers", checked: element.lineNumbers ?? true, onCheckedChange: (v) => onPatch({ lineNumbers: v }) })
2706
2839
  ] });
2707
2840
  }
2708
2841
  function ChartStyleControls({ element, onPatch }) {
@@ -3209,6 +3342,7 @@ function DeckEditor({
3209
3342
  onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
3210
3343
  onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
3211
3344
  onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
3345
+ onSetLayout: (layout) => slide && ops.setLayout(slide.id, layout),
3212
3346
  onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
3213
3347
  onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
3214
3348
  }
@@ -3220,12 +3354,16 @@ function DeckEditor({
3220
3354
  );
3221
3355
  }
3222
3356
 
3357
+ // src/types.ts
3358
+ var SCHEMA_VERSION = 1;
3359
+
3223
3360
  exports.DeckEditor = DeckEditor;
3224
3361
  exports.EditorToolbar = EditorToolbar;
3225
3362
  exports.ElementInspector = ElementInspector;
3226
3363
  exports.ImageElementRenderer = ImageElementRenderer;
3227
3364
  exports.PRESENTATION_EDITOR_ACTIONS = PRESENTATION_EDITOR_ACTIONS;
3228
3365
  exports.PresenterView = PresenterView;
3366
+ exports.SCHEMA_VERSION = SCHEMA_VERSION;
3229
3367
  exports.ShapeElementRenderer = ShapeElementRenderer;
3230
3368
  exports.Slide = Slide;
3231
3369
  exports.SlideRail = SlideRail;