promptslide 0.3.12 → 0.3.14

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.14](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.13...promptslide-v0.3.14) (2026-04-01)
4
+
5
+
6
+ ### Features
7
+
8
+ * add optional white glow PNG for slide backgrounds ([#94](https://github.com/prompticeu/promptslide/issues/94)) ([cbd500e](https://github.com/prompticeu/promptslide/commit/cbd500e8bdb1f3125bdbad6668d924394d6245a2))
9
+ * add workflow hint to annotation sidebar ([#92](https://github.com/prompticeu/promptslide/issues/92)) ([0d898d6](https://github.com/prompticeu/promptslide/commit/0d898d66e90a767bf6187103d3fb7a24dfc7d828))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * allow arrow key navigation in fullscreen when annotation mode is active ([#95](https://github.com/prompticeu/promptslide/issues/95)) ([b2f8d31](https://github.com/prompticeu/promptslide/commit/b2f8d3150d24b919303de8126f6460c0123bfd29))
15
+
16
+ ## [0.3.13](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.12...promptslide-v0.3.13) (2026-03-29)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * pin dependency versions and preserve component names in publish pipeline ([#89](https://github.com/prompticeu/promptslide/issues/89)) ([22b7dcb](https://github.com/prompticeu/promptslide/commit/22b7dcbe33c9852fac2fbfef70943254a0d871cd))
22
+
3
23
  ## [0.3.12](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.11...promptslide-v0.3.12) (2026-03-28)
4
24
 
5
25
 
package/dist/index.js CHANGED
@@ -983,7 +983,7 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
983
983
 
984
984
  // src/core/annotations/annotation-panel.tsx
985
985
  import { MessageCircle, Trash2, X as X2 } from "lucide-react";
986
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
986
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
987
987
  function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }) {
988
988
  const open = annotations.filter((a) => a.status === "open");
989
989
  const resolved = annotations.filter((a) => a.status === "resolved");
@@ -1005,10 +1005,7 @@ function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose
1005
1005
  ] }),
1006
1006
  /* @__PURE__ */ jsx11("div", { className: "h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" }),
1007
1007
  /* @__PURE__ */ jsxs5("div", { className: "flex-1 overflow-y-auto p-2", children: [
1008
- annotations.length === 0 && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center gap-2 px-4 py-8 text-center", children: [
1009
- /* @__PURE__ */ jsx11("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-white/[0.04]", children: /* @__PURE__ */ jsx11(MessageCircle, { className: "h-5 w-5 text-neutral-600" }) }),
1010
- /* @__PURE__ */ jsx11("p", { className: "text-sm text-neutral-500", children: "Click on slide elements to add annotations" })
1011
- ] }),
1008
+ annotations.length === 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-4 px-3 py-6", children: /* @__PURE__ */ jsx11(WorkflowHint, {}) }),
1012
1009
  open.length > 0 && /* @__PURE__ */ jsxs5("div", { children: [
1013
1010
  /* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Open" }),
1014
1011
  open.map((a, i) => /* @__PURE__ */ jsx11(
@@ -1037,6 +1034,27 @@ function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose
1037
1034
  a.id
1038
1035
  ))
1039
1036
  ] })
1037
+ ] }),
1038
+ annotations.length > 0 && /* @__PURE__ */ jsxs5(Fragment2, { children: [
1039
+ /* @__PURE__ */ jsx11("div", { className: "h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" }),
1040
+ /* @__PURE__ */ jsx11("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx11(WorkflowHint, {}) })
1041
+ ] })
1042
+ ] });
1043
+ }
1044
+ function WorkflowHint() {
1045
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
1046
+ /* @__PURE__ */ jsx11("p", { className: "text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Workflow" }),
1047
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
1048
+ /* @__PURE__ */ jsx11("div", { className: "mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]", children: "1" }),
1049
+ /* @__PURE__ */ jsx11("p", { className: "text-[12px] leading-snug text-neutral-400", children: "Annotate everything you want to fix" })
1050
+ ] }),
1051
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
1052
+ /* @__PURE__ */ jsx11("div", { className: "mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]", children: "2" }),
1053
+ /* @__PURE__ */ jsx11("p", { className: "text-[12px] leading-snug text-neutral-400", children: "Switch to your coding agent" })
1054
+ ] }),
1055
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
1056
+ /* @__PURE__ */ jsx11("div", { className: "mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]", children: "3" }),
1057
+ /* @__PURE__ */ jsx11("p", { className: "text-[12px] leading-snug text-neutral-400", children: '"Fix open annotations"' })
1040
1058
  ] })
1041
1059
  ] });
1042
1060
  }
@@ -1301,7 +1319,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
1301
1319
  setViewMode((prev) => prev === "grid" ? "slide" : "grid");
1302
1320
  return;
1303
1321
  }
1304
- if (viewMode !== "slide" || isAnnotationMode) return;
1322
+ if (viewMode !== "slide" || isAnnotationMode && !isPresentationMode) return;
1305
1323
  if (e.key === "ArrowRight" || e.key === " ") {
1306
1324
  e.preventDefault();
1307
1325
  advance();
@@ -1312,7 +1330,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
1312
1330
  };
1313
1331
  window.addEventListener("keydown", handleKeyDown);
1314
1332
  return () => window.removeEventListener("keydown", handleKeyDown);
1315
- }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode]);
1333
+ }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode, isPresentationMode]);
1316
1334
  return /* @__PURE__ */ jsxs6("div", { className: "min-h-screen w-full bg-neutral-950 text-foreground", children: [
1317
1335
  /* @__PURE__ */ jsx12("style", { children: `
1318
1336
  @media print {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/animation-config.ts","../src/core/animation-context.tsx","../src/core/animated.tsx","../src/core/transitions.ts","../src/core/morph.tsx","../src/core/use-slide-navigation.ts","../src/core/theme-context.tsx","../src/core/slide-renderer.tsx","../src/core/slide-error-boundary.tsx","../src/core/slide-embed.tsx","../src/core/slide-deck.tsx","../src/core/annotations/annotation-overlay.tsx","../src/core/annotations/annotation-form.tsx","../src/core/annotations/annotation-pin.tsx","../src/core/annotations/selectors.ts","../src/core/annotations/annotation-panel.tsx","../src/core/annotations/use-annotations.ts","../src/core/annotations/adapters/http.ts","../src/core/utils.ts"],"sourcesContent":["/**\n * Centralized animation configuration for the slide deck framework.\n * All animation timings, easings, and spring configs are defined here.\n */\n\n// =============================================================================\n// TIMING CONSTANTS\n// =============================================================================\n\n/** Duration for slide-to-slide transitions (seconds) */\nexport const SLIDE_TRANSITION_DURATION = 0.3\n\n/** Duration for morph/layout animations between slides (seconds) */\nexport const MORPH_DURATION = 0.8\n\n/** Duration for within-slide step animations (seconds) */\nexport const STEP_ANIMATION_DURATION = 0.4\n\n/** Default stagger delay for grouped animations (seconds) */\nexport const STAGGER_DELAY = 0.1\n\n// =============================================================================\n// EASING PRESETS\n// =============================================================================\n\nexport const EASE_DEFAULT = \"easeInOut\" as const\nexport const EASE_OUT = \"easeOut\" as const\nexport const EASE_IN = \"easeIn\" as const\n\n/** Smooth ease-in-out curve for morph animations (cubic bezier) */\nexport const EASE_MORPH = [0.4, 0, 0.2, 1] as const\n\n// =============================================================================\n// SPRING CONFIGURATIONS\n// =============================================================================\n\n/** Spring config for snappy, responsive animations */\nexport const SPRING_SNAPPY = {\n type: \"spring\" as const,\n stiffness: 300,\n damping: 30\n}\n\n/** Spring config for smooth, gentle animations */\nexport const SPRING_SMOOTH = {\n type: \"spring\" as const,\n stiffness: 200,\n damping: 25\n}\n\n/** Spring config for bouncy animations */\nexport const SPRING_BOUNCY = {\n type: \"spring\" as const,\n stiffness: 400,\n damping: 20\n}\n\n// =============================================================================\n// TRANSITION PRESETS\n// =============================================================================\n\n/** Standard transition for slide transitions */\nexport const SLIDE_TRANSITION = {\n duration: SLIDE_TRANSITION_DURATION,\n ease: EASE_DEFAULT\n} as const\n\n/** Standard transition for morph animations (smooth ease-in-out) */\nexport const MORPH_TRANSITION = {\n duration: MORPH_DURATION,\n ease: EASE_MORPH\n}\n\n/** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */\nexport const STEP_TRANSITION = {\n ...SPRING_SNAPPY\n}\n\n// =============================================================================\n// DISTANCE CONSTANTS (pixels)\n// =============================================================================\n\n/** Distance for slide animations */\nexport const SLIDE_DISTANCE = 100\n\n/** Distance for within-slide element animations */\nexport const ELEMENT_SLIDE_DISTANCE = 30\n\n// =============================================================================\n// SLIDE DIMENSIONS\n// =============================================================================\n\n/** Standard slide dimensions (16:9 aspect ratio) */\nexport const SLIDE_DIMENSIONS = {\n width: 1280,\n height: 720,\n aspectRatio: 16 / 9\n} as const\n","import { createContext, useContext, useMemo } from \"react\"\n\ninterface AnimationContextValue {\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps in this slide (declared in slide config) */\n totalSteps: number\n /** When true, all animation steps should be visible (used for backward navigation) */\n showAllAnimations: boolean\n}\n\nconst AnimationContext = createContext<AnimationContextValue>({\n currentStep: 0,\n totalSteps: 0,\n showAllAnimations: false\n})\n\ninterface AnimationProviderProps {\n children: React.ReactNode\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps declared in slide config */\n totalSteps: number\n /** When true, show all animations regardless of currentStep (for backward navigation) */\n showAllAnimations?: boolean\n}\n\n/**\n * Provides animation context to child components.\n *\n * The totalSteps is now passed from parent (declared in slide config)\n * rather than discovered at runtime, eliminating race conditions.\n */\nexport function AnimationProvider({\n children,\n currentStep,\n totalSteps,\n showAllAnimations = false\n}: AnimationProviderProps) {\n const value = useMemo(\n () => ({\n currentStep,\n totalSteps,\n showAllAnimations\n }),\n [currentStep, totalSteps, showAllAnimations]\n )\n\n return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>\n}\n\nexport function useAnimationContext() {\n return useContext(AnimationContext)\n}\n","import { motion, Variants } from \"framer-motion\"\n\nimport {\n ELEMENT_SLIDE_DISTANCE,\n SPRING_SNAPPY,\n STAGGER_DELAY,\n STEP_ANIMATION_DURATION\n} from \"./animation-config\"\nimport { useAnimationContext } from \"./animation-context\"\n\n// =============================================================================\n// ANIMATION TYPES & VARIANTS\n// =============================================================================\n\nexport type AnimationType =\n | \"fade\"\n | \"slide-up\"\n | \"slide-down\"\n | \"slide-left\"\n | \"slide-right\"\n | \"scale\"\n\nconst animationVariants: Record<AnimationType, Variants> = {\n fade: {\n hidden: { opacity: 0 },\n visible: { opacity: 1 }\n },\n \"slide-up\": {\n hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-down\": {\n hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-left\": {\n hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n \"slide-right\": {\n hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n scale: {\n hidden: { opacity: 0, scale: 0.8 },\n visible: { opacity: 1, scale: 1 }\n }\n}\n\ninterface AnimatedProps {\n /** Which step reveals this content (1-indexed) */\n step: number\n /** Animation type */\n animation?: AnimationType\n /** Animation duration in seconds */\n duration?: number\n /** Delay after trigger in seconds */\n delay?: number\n /** Custom className */\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Animated element that appears at a specific animation step.\n *\n * The step count is now declared in slide config rather than discovered\n * at runtime, so this component simply consumes the context without\n * needing to register itself.\n */\nexport function Animated({\n step,\n animation = \"slide-up\",\n duration = STEP_ANIMATION_DURATION,\n delay = 0,\n className,\n children\n}: AnimatedProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= step\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={animationVariants[animation]}\n transition={{\n ...SPRING_SNAPPY,\n duration,\n delay: isVisible ? delay : 0\n }}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n/**\n * Wrapper for staggering multiple children.\n * Each direct child will be animated in sequence.\n */\ninterface AnimatedGroupProps {\n /** Starting step for the first child */\n startStep: number\n /** Animation type for all children */\n animation?: AnimationType\n /** Delay between each child in seconds */\n staggerDelay?: number\n className?: string\n children: React.ReactNode\n}\n\nexport function AnimatedGroup({\n startStep,\n animation = \"slide-up\",\n staggerDelay = STAGGER_DELAY,\n className,\n children\n}: AnimatedGroupProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n const childArray = Array.isArray(children) ? children : [children]\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= startStep\n\n const containerVariants: Variants = {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: staggerDelay\n }\n }\n }\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={containerVariants}\n className={className}\n >\n {childArray.map((child, index) => (\n <motion.div key={index} variants={animationVariants[animation]}>\n {child}\n </motion.div>\n ))}\n </motion.div>\n )\n}\n","/**\n * Slide transition variants for the slide deck framework.\n * These define how slides enter and exit during navigation.\n */\n\nimport type { Variants } from \"framer-motion\"\n\nimport {\n EASE_MORPH,\n MORPH_DURATION,\n SLIDE_DISTANCE,\n SLIDE_TRANSITION,\n SLIDE_TRANSITION_DURATION\n} from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type SlideTransitionType =\n | \"fade\"\n | \"slide-left\"\n | \"slide-right\"\n | \"slide-up\"\n | \"slide-down\"\n | \"zoom\"\n | \"zoom-fade\"\n | \"morph\"\n | \"none\"\n\nexport interface SlideTransitionConfig {\n /** The transition type */\n type: SlideTransitionType\n /** Optional custom duration (overrides default) */\n duration?: number\n /** Whether to use directional transitions based on navigation direction */\n directional?: boolean\n}\n\n// =============================================================================\n// STATIC VARIANTS (non-directional)\n// =============================================================================\n\nconst fadeVariants: Variants = {\n enter: { opacity: 0 },\n center: { opacity: 1 },\n exit: { opacity: 0 }\n}\n\nconst slideLeftVariants: Variants = {\n enter: { x: SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideRightVariants: Variants = {\n enter: { x: -SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideUpVariants: Variants = {\n enter: { y: SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideDownVariants: Variants = {\n enter: { y: -SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst zoomVariants: Variants = {\n enter: { scale: 0.8, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.2, opacity: 0 }\n}\n\nconst zoomFadeVariants: Variants = {\n enter: { scale: 0.95, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.05, opacity: 0 }\n}\n\nconst morphVariants: Variants = {\n enter: {\n opacity: 0,\n zIndex: 1\n },\n center: {\n opacity: 1,\n zIndex: 1,\n transition: {\n opacity: { delay: 0.05, duration: 0.25 }\n }\n },\n exit: {\n opacity: 0,\n zIndex: 0,\n transition: {\n opacity: { duration: MORPH_DURATION, ease: EASE_MORPH }\n }\n }\n}\n\nconst noneVariants: Variants = {\n enter: {},\n center: {},\n exit: {}\n}\n\n// =============================================================================\n// VARIANT REGISTRY\n// =============================================================================\n\nexport const SLIDE_VARIANTS: Record<SlideTransitionType, Variants> = {\n fade: fadeVariants,\n \"slide-left\": slideLeftVariants,\n \"slide-right\": slideRightVariants,\n \"slide-up\": slideUpVariants,\n \"slide-down\": slideDownVariants,\n zoom: zoomVariants,\n \"zoom-fade\": zoomFadeVariants,\n morph: morphVariants,\n none: noneVariants\n}\n\n// =============================================================================\n// DIRECTIONAL VARIANTS (based on navigation direction)\n// =============================================================================\n\nexport function createDirectionalVariants(axis: \"x\" | \"y\" = \"x\"): (direction: number) => Variants {\n return (direction: number) => ({\n enter: {\n [axis]: direction > 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n },\n center: {\n [axis]: 0,\n opacity: 1\n },\n exit: {\n [axis]: direction < 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n }\n })\n}\n\n/** Horizontal directional slide (left/right based on direction) */\nexport const directionalSlideX = createDirectionalVariants(\"x\")\n\n/** Vertical directional slide (up/down based on direction) */\nexport const directionalSlideY = createDirectionalVariants(\"y\")\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\nexport function getSlideVariants(\n config: SlideTransitionConfig | SlideTransitionType,\n direction: number = 1\n): Variants {\n const type = typeof config === \"string\" ? config : config.type\n const directional = typeof config === \"object\" ? config.directional : false\n\n if (directional && (type === \"slide-left\" || type === \"slide-right\")) {\n return directionalSlideX(direction)\n }\n\n if (directional && (type === \"slide-up\" || type === \"slide-down\")) {\n return directionalSlideY(direction)\n }\n\n return SLIDE_VARIANTS[type]\n}\n\nexport function getSlideTransition(config?: SlideTransitionConfig | SlideTransitionType): {\n duration: number\n ease: typeof SLIDE_TRANSITION.ease\n} {\n if (!config) return SLIDE_TRANSITION\n\n const type = typeof config === \"string\" ? config : config.type\n\n const defaultDuration = type === \"morph\" ? MORPH_DURATION : SLIDE_TRANSITION_DURATION\n\n const duration = typeof config === \"object\" && config.duration ? config.duration : defaultDuration\n\n return {\n ...SLIDE_TRANSITION,\n duration\n }\n}\n\n// =============================================================================\n// DEFAULT EXPORT\n// =============================================================================\n\nexport const DEFAULT_SLIDE_TRANSITION: SlideTransitionType = \"fade\"\n","import type { Transition } from \"framer-motion\"\n\nimport { motion } from \"framer-motion\"\n\nimport { MORPH_TRANSITION } from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface MorphProps {\n layoutId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\ninterface MorphGroupProps {\n groupId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\n// =============================================================================\n// MORPH COMPONENT\n// =============================================================================\n\n/**\n * Wrapper component that enables morph/shared-layout animations between slides.\n *\n * Usage:\n * ```tsx\n * // Slide 1 - Large version\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-6xl\">Title</h1>\n * </Morph>\n *\n * // Slide 2 - Small version (same layoutId = morphs between them)\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-2xl\">Title</h1>\n * </Morph>\n * ```\n */\nexport function Morph({\n layoutId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphProps) {\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH GROUP COMPONENT\n// =============================================================================\n\nexport function MorphGroup({\n groupId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphGroupProps) {\n return (\n <motion.div\n layout\n layoutId={groupId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH ITEM (for use within MorphGroup pattern)\n// =============================================================================\n\ninterface MorphItemProps {\n id: string\n prefix?: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphItem({\n id,\n prefix,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphItemProps) {\n const layoutId = prefix ? `${prefix}-${id}` : id\n\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH TEXT (specialized for text that changes size)\n// =============================================================================\n\ninterface MorphTextProps {\n layoutId: string\n as?: \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"p\" | \"span\"\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphText({\n layoutId,\n as: Component = \"span\",\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphTextProps) {\n const MotionComponent = motion[Component]\n\n return (\n <MotionComponent\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </MotionComponent>\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype NavigationStatus = \"idle\" | \"transitioning\"\n\ntype QueuedAction = \"advance\" | \"goBack\" | null\n\ninterface NavigationState {\n status: NavigationStatus\n direction: NavigationDirection\n}\n\nexport interface UseSlideNavigationOptions {\n slides: SlideConfig[]\n initialSlide?: number\n onSlideChange?: (slideIndex: number) => void\n}\n\nexport interface UseSlideNavigationReturn {\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n isTransitioning: boolean\n showAllAnimations: boolean\n advance: () => void\n goBack: () => void\n goToSlide: (index: number) => void\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// HOOK\n// =============================================================================\n\nexport function useSlideNavigation({\n slides,\n initialSlide = 0,\n onSlideChange\n}: UseSlideNavigationOptions): UseSlideNavigationReturn {\n const [currentSlide, setCurrentSlide] = useState(initialSlide)\n const [animationStep, setAnimationStep] = useState(0)\n\n const [navState, setNavState] = useState<NavigationState>({\n status: \"idle\",\n direction: 0\n })\n\n const [queuedAction, setQueuedAction] = useState<QueuedAction>(null)\n\n const totalSteps = slides[currentSlide]?.steps ?? 0\n\n const onTransitionComplete = useCallback(() => {\n setNavState(prev => {\n if (prev.status === \"transitioning\") {\n return { status: \"idle\", direction: 0 }\n }\n return prev\n })\n }, [])\n\n const advance = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"advance\")\n return\n }\n\n const currentTotalSteps = slides[currentSlide]?.steps ?? 0\n\n if (animationStep >= currentTotalSteps) {\n const nextSlide = (currentSlide + 1) % slides.length\n setNavState({ status: \"transitioning\", direction: 1 })\n setAnimationStep(0)\n setCurrentSlide(nextSlide)\n onSlideChange?.(nextSlide)\n } else {\n setAnimationStep(prev => prev + 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goBack = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"goBack\")\n return\n }\n\n if (animationStep <= 0) {\n const prevSlide = (currentSlide - 1 + slides.length) % slides.length\n const prevSlideSteps = slides[prevSlide]?.steps ?? 0\n setNavState({ status: \"transitioning\", direction: -1 })\n setAnimationStep(prevSlideSteps)\n setCurrentSlide(prevSlide)\n onSlideChange?.(prevSlide)\n } else {\n setAnimationStep(prev => prev - 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goToSlide = useCallback(\n (index: number) => {\n if (index < 0 || index >= slides.length || index === currentSlide) {\n return\n }\n\n const direction = index > currentSlide ? 1 : -1\n setNavState({ status: \"transitioning\", direction })\n setAnimationStep(0)\n setCurrentSlide(index)\n onSlideChange?.(index)\n },\n [currentSlide, slides.length, onSlideChange]\n )\n\n useEffect(() => {\n if (navState.status === \"idle\" && queuedAction !== null) {\n setQueuedAction(null)\n if (queuedAction === \"advance\") {\n advance()\n } else if (queuedAction === \"goBack\") {\n goBack()\n }\n }\n }, [navState.status, queuedAction, advance, goBack])\n\n return {\n currentSlide,\n animationStep,\n totalSteps,\n direction: navState.direction,\n isTransitioning: navState.status !== \"idle\",\n showAllAnimations: navState.direction === -1 && navState.status === \"transitioning\",\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n }\n}\n","import { createContext, useContext, useMemo } from \"react\"\nimport type { ThemeConfig } from \"./types\"\n\nconst ThemeContext = createContext<ThemeConfig | null>(null)\n\n/**\n * Access the full theme config. Returns null outside SlideThemeProvider.\n */\nexport function useTheme(): ThemeConfig | null {\n return useContext(ThemeContext)\n}\n\nfunction buildCssOverrides(theme: ThemeConfig): React.CSSProperties {\n const style: Record<string, string> = {}\n\n if (theme.colors?.primary) style[\"--primary\"] = theme.colors.primary\n if (theme.colors?.primaryForeground) style[\"--primary-foreground\"] = theme.colors.primaryForeground\n if (theme.colors?.secondary) style[\"--secondary\"] = theme.colors.secondary\n if (theme.colors?.secondaryForeground) style[\"--secondary-foreground\"] = theme.colors.secondaryForeground\n if (theme.colors?.accent) style[\"--accent\"] = theme.colors.accent\n if (theme.colors?.accentForeground) style[\"--accent-foreground\"] = theme.colors.accentForeground\n\n if (theme.fonts?.heading) style[\"--font-heading\"] = theme.fonts.heading\n if (theme.fonts?.body) style[\"--font-body\"] = theme.fonts.body\n\n return style as React.CSSProperties\n}\n\ninterface SlideThemeProviderProps {\n theme: ThemeConfig\n children: React.ReactNode\n}\n\n/**\n * Provides the full theme to all descendants.\n * Injects CSS variable overrides via inline style.\n */\nexport function SlideThemeProvider({ theme, children }: SlideThemeProviderProps) {\n const cssOverrides = useMemo(() => buildCssOverrides(theme), [theme])\n\n return (\n <ThemeContext.Provider value={theme}>\n <div style={cssOverrides} className=\"contents\">\n {children}\n </div>\n </ThemeContext.Provider>\n )\n}\n","import { AnimatePresence, motion } from \"framer-motion\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\nimport { SLIDE_TRANSITION } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { DEFAULT_SLIDE_TRANSITION, getSlideVariants } from \"./transitions\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface SlideRendererProps {\n slides: SlideConfig[]\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n showAllAnimations: boolean\n transition?: SlideTransitionType\n directionalTransition?: boolean\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Renders a single slide with animated transitions.\n * Extracted from SlideDeck so it can be reused in SlideEmbed and other contexts.\n */\nexport function SlideRenderer({\n slides,\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n transition,\n directionalTransition,\n onTransitionComplete\n}: SlideRendererProps) {\n const currentSlideTransition = slides[currentSlide]?.transition\n const transitionType = currentSlideTransition ?? transition ?? DEFAULT_SLIDE_TRANSITION\n const isDirectional = directionalTransition ?? false\n\n const slideVariants = getSlideVariants(\n { type: transitionType, directional: isDirectional },\n direction\n )\n\n const CurrentSlideComponent = slides[currentSlide]!.component\n\n return (\n <AnimatePresence initial={false}>\n <motion.div\n key={currentSlide}\n variants={slideVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={SLIDE_TRANSITION}\n onAnimationComplete={definition => {\n if (definition === \"center\") {\n onTransitionComplete()\n }\n }}\n className=\"absolute inset-0 h-full w-full\"\n >\n <AnimationProvider\n currentStep={animationStep}\n totalSteps={totalSteps}\n showAllAnimations={showAllAnimations}\n >\n <SlideErrorBoundary\n slideIndex={currentSlide}\n slideTitle={slides[currentSlide]?.title}\n >\n <CurrentSlideComponent\n slideNumber={currentSlide + 1}\n totalSlides={slides.length}\n />\n </SlideErrorBoundary>\n </AnimationProvider>\n </motion.div>\n </AnimatePresence>\n )\n}\n","import React from \"react\"\n\ninterface SlideErrorBoundaryProps {\n slideIndex: number\n slideTitle?: string\n children: React.ReactNode\n}\n\ninterface SlideErrorBoundaryState {\n hasError: boolean\n error: Error | null\n}\n\nexport class SlideErrorBoundary extends React.Component<\n SlideErrorBoundaryProps,\n SlideErrorBoundaryState\n> {\n state: SlideErrorBoundaryState = { hasError: false, error: null }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `[PromptSlide] Slide ${this.props.slideIndex + 1}${this.props.slideTitle ? ` (\"${this.props.slideTitle}\")` : \"\"} crashed:`,\n error,\n errorInfo\n )\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex h-full w-full flex-col items-center justify-center bg-red-950/20 p-12\">\n <div className=\"mb-4 text-lg font-semibold text-red-400\">\n Slide {this.props.slideIndex + 1} Error\n {this.props.slideTitle && (\n <span className=\"ml-2 font-normal text-red-400/70\">({this.props.slideTitle})</span>\n )}\n </div>\n <div className=\"max-w-2xl text-center font-mono text-sm break-words text-red-300/80\">\n {this.state.error?.message ?? \"Unknown error\"}\n </div>\n </div>\n )\n }\n return this.props.children\n }\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface SlideEmbedProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Headless slide viewer controlled via window.postMessage.\n * Designed for embedding in an iframe (e.g. the registry editor preview).\n *\n * Inbound messages (parent → embed):\n * { type: \"navigate\", data: { slide: number } }\n * { type: \"advance\" }\n * { type: \"goBack\" }\n *\n * Outbound messages (embed → parent):\n * { type: \"slideReady\" }\n * { type: \"slideState\", data: { currentSlide, totalSlides, animationStep, totalSteps, titles } }\n * { type: \"hmrUpdate\" }\n */\nexport function SlideEmbed({ slides, transition, directionalTransition }: SlideEmbedProps) {\n const [scale, setScale] = useState(1)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({ slides })\n\n // Post slide state to parent whenever it changes\n useEffect(() => {\n const state = {\n currentSlide,\n totalSlides: slides.length,\n animationStep,\n totalSteps,\n titles: slides.map(s => s.title || \"\")\n }\n window.parent.postMessage({ type: \"slideState\", data: state }, \"*\")\n }, [currentSlide, animationStep, totalSteps, slides])\n\n // Signal readiness on mount\n useEffect(() => {\n window.parent.postMessage({ type: \"slideReady\" }, \"*\")\n }, [])\n\n // Listen for Vite HMR updates\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const hot = (import.meta as any).hot as { on: (event: string, cb: () => void) => void } | undefined\n if (hot) {\n hot.on(\"vite:afterUpdate\", () => {\n window.parent.postMessage({ type: \"hmrUpdate\" }, \"*\")\n })\n }\n }, [])\n\n // Listen for inbound messages from parent\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const { type, data } = event.data || {}\n switch (type) {\n case \"navigate\":\n if (typeof data?.slide === \"number\") goToSlide(data.slide)\n break\n case \"advance\":\n advance()\n break\n case \"goBack\":\n goBack()\n break\n }\n },\n [advance, goBack, goToSlide]\n )\n\n useEffect(() => {\n window.addEventListener(\"message\", handleMessage)\n return () => window.removeEventListener(\"message\", handleMessage)\n }, [handleMessage])\n\n // Scale to fit viewport while preserving aspect ratio\n useEffect(() => {\n const calculateScale = () => {\n const scaleX = window.innerWidth / SLIDE_DIMENSIONS.width\n const scaleY = window.innerHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [])\n\n return (\n <div className=\"h-screen w-screen overflow-hidden bg-black\">\n <div\n className=\"absolute left-1/2 top-1/2 overflow-hidden\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `translate(-50%, -50%) scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n </div>\n )\n}\n","import { LayoutGroup } from \"framer-motion\"\nimport { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, MessageCircle, Monitor } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { AnnotationOverlay, AnnotationPanel, useAnnotations } from \"./annotations\"\nimport type { Annotation, AnnotationTarget } from \"./annotations\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\nimport { cn } from \"./utils\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype ViewMode = \"slide\" | \"list\" | \"grid\"\n\ninterface SlideDeckProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n /** Annotation data to display. When provided (even empty array), annotation UI is enabled. When undefined, annotation UI is hidden. */\n annotations?: Annotation[]\n /** Called when the user creates an annotation */\n onAnnotationAdd?: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n /** Called when the user deletes an annotation */\n onAnnotationDelete?: (id: string) => void\n}\n\n// =============================================================================\n// EXPORT VIEW (for Playwright screenshot capture)\n// =============================================================================\n\nfunction SlideExportView({ slides, slideIndex }: { slides: SlideConfig[]; slideIndex: number }) {\n const [ready, setReady] = useState(false)\n const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1))\n const slideConfig = slides[clampedIndex]!\n const SlideComponent = slideConfig.component\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n return (\n <div\n data-export-ready={ready ? \"true\" : undefined}\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n overflow: \"hidden\",\n position: \"relative\",\n background: \"black\"\n }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={clampedIndex} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={clampedIndex + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n )\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nexport function SlideDeck({ slides, transition, directionalTransition, annotations, onAnnotationAdd, onAnnotationDelete }: SlideDeckProps) {\n // Check for export mode via URL params\n const [exportParams] = useState(() => {\n if (typeof window === \"undefined\") return null\n const params = new URLSearchParams(window.location.search)\n if (params.get(\"export\") !== \"true\") return null\n return { slideIndex: parseInt(params.get(\"slide\") || \"0\", 10) }\n })\n\n if (exportParams) {\n return <SlideExportView slides={slides} slideIndex={exportParams.slideIndex} />\n }\n\n // Use internal useAnnotations as fallback when no external annotations prop is provided\n const internal = useAnnotations()\n const isExternallyManaged = annotations !== undefined\n const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations\n const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation\n const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation\n\n const openCount = useMemo(() => effectiveAnnotations.filter(a => a.status === \"open\").length, [effectiveAnnotations])\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => effectiveAnnotations.filter(a => a.slideIndex === slideIndex),\n [effectiveAnnotations]\n )\n\n const [viewMode, setViewMode] = useState<ViewMode>(\"slide\")\n const [isPresentationMode, setIsPresentationMode] = useState(false)\n const [isAnnotationMode, setIsAnnotationMode] = useState(false)\n const [showAnnotationPanel, setShowAnnotationPanel] = useState(false)\n const [selectedAnnotationId, setSelectedAnnotationId] = useState<string | null>(null)\n const [scale, setScale] = useState(1)\n const containerRef = useRef<HTMLDivElement>(null)\n const slideContainerRef = useRef<HTMLDivElement>(null)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({\n slides\n })\n\n const togglePresentationMode = useCallback(async () => {\n if (!document.fullscreenElement) {\n await document.documentElement.requestFullscreen()\n } else {\n await document.exitFullscreen()\n }\n }, [])\n\n // Listen for fullscreen changes\n useEffect(() => {\n const handleFullscreenChange = () => {\n setIsPresentationMode(!!document.fullscreenElement)\n }\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange)\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange)\n }, [])\n\n // Calculate scale factor for presentation mode\n useEffect(() => {\n const calculateScale = () => {\n if (!isPresentationMode) {\n setScale(1)\n return\n }\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n const scaleX = viewportWidth / SLIDE_DIMENSIONS.width\n const scaleY = viewportHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [isPresentationMode])\n\n const handleExportPdf = () => {\n const previousMode = viewMode\n setViewMode(\"list\")\n\n setTimeout(() => {\n const handleAfterPrint = () => {\n setViewMode(previousMode)\n window.removeEventListener(\"afterprint\", handleAfterPrint)\n }\n window.addEventListener(\"afterprint\", handleAfterPrint)\n window.print()\n }, 100)\n }\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"f\" || e.key === \"F\") {\n togglePresentationMode()\n return\n }\n\n // G for grid view toggle\n if (e.key === \"g\" || e.key === \"G\") {\n setViewMode(prev => (prev === \"grid\" ? \"slide\" : \"grid\"))\n return\n }\n\n if (viewMode !== \"slide\" || isAnnotationMode) return\n\n if (e.key === \"ArrowRight\" || e.key === \" \") {\n e.preventDefault()\n advance()\n } else if (e.key === \"ArrowLeft\") {\n e.preventDefault()\n goBack()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode])\n\n return (\n <div className=\"min-h-screen w-full bg-neutral-950 text-foreground\">\n <style>{`\n @media print {\n @page {\n size: 1920px 1080px;\n margin: 0;\n }\n html,\n body {\n width: 100%;\n height: 100%;\n margin: 0 !important;\n padding: 0 !important;\n overflow: visible !important;\n }\n body {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n background: transparent !important;\n }\n }\n `}</style>\n\n {/* Toolbar */}\n <div\n className={cn(\n \"fixed top-4 z-50 flex gap-1 rounded-lg border border-neutral-800 bg-neutral-950/90 p-1 backdrop-blur-sm transition-[right] print:hidden\",\n isPresentationMode && \"hidden\",\n isAnnotationMode && showAnnotationPanel ? \"right-[19.5rem]\" : \"right-4\"\n )}\n >\n <button\n onClick={() => setViewMode(\"slide\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"slide\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Presentation View\"\n >\n <Monitor className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"list\" && \"bg-neutral-800 text-white\"\n )}\n title=\"List View\"\n >\n <List className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"grid\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"grid\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Grid View\"\n >\n <Grid3X3 className=\"h-4 w-4\" />\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={() => {\n setIsAnnotationMode(prev => {\n const next = !prev\n setShowAnnotationPanel(next)\n if (!next) setSelectedAnnotationId(null)\n return next\n })\n }}\n className={cn(\n \"relative rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n isAnnotationMode && \"bg-[#FF6B35] text-white hover:bg-[#FF7A4A]\"\n )}\n title=\"Annotate slides\"\n >\n <MessageCircle className=\"h-4 w-4\" />\n {openCount > 0 && (\n <span className=\"absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#FF6B35] text-[10px] font-bold text-white\">\n {openCount}\n </span>\n )}\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={handleExportPdf}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Download PDF\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n <button\n onClick={togglePresentationMode}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Present (F)\"\n >\n <Maximize className=\"h-4 w-4\" />\n </button>\n </div>\n\n {/* Slide View */}\n {viewMode === \"slide\" && (\n <div\n className={cn(\n \"flex h-screen w-full print:hidden\",\n isPresentationMode ? \"bg-black\" : \"\"\n )}\n >\n <div\n ref={containerRef}\n role=\"presentation\"\n tabIndex={isPresentationMode ? 0 : undefined}\n className={cn(\n \"flex flex-1 flex-col items-center justify-center overflow-hidden\",\n isPresentationMode ? \"bg-black p-0\" : \"p-4 md:p-8\"\n )}\n onClick={isPresentationMode ? advance : undefined}\n onKeyDown={\n isPresentationMode\n ? e => {\n if (e.key === \"Enter\" || e.key === \" \") advance()\n }\n : undefined\n }\n >\n <LayoutGroup id=\"slide-deck\">\n {isPresentationMode ? (\n <div\n className=\"pointer-events-none relative overflow-hidden bg-black\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n ) : (\n <div ref={slideContainerRef} className=\"relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl\">\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n {isAnnotationMode && (\n <AnnotationOverlay\n slides={slides}\n currentSlide={currentSlide}\n slideContainerRef={slideContainerRef}\n selectedId={selectedAnnotationId}\n onSelectId={setSelectedAnnotationId}\n onShowPanel={() => setShowAnnotationPanel(true)}\n slideAnnotations={getSlideAnnotations(currentSlide)}\n addAnnotation={effectiveAdd ?? (() => {})}\n\n />\n )}\n </div>\n )}\n </LayoutGroup>\n\n {/* Navigation Controls */}\n {!isPresentationMode && (\n <div className=\"mt-6 flex items-center gap-4\">\n <button\n onClick={goBack}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <div className=\"flex min-w-[4rem] flex-col items-center\">\n <span className=\"font-mono text-sm text-neutral-500\">\n {currentSlide + 1} / {slides.length}\n </span>\n {slides[currentSlide]?.title && (\n <span className=\"mt-0.5 text-xs text-neutral-600\">\n {slides[currentSlide].title}\n </span>\n )}\n </div>\n <button\n onClick={advance}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Annotation Panel — beside the slide */}\n {isAnnotationMode && showAnnotationPanel && !isPresentationMode && (\n <AnnotationPanel\n annotations={getSlideAnnotations(currentSlide)}\n selectedId={selectedAnnotationId}\n onSelect={setSelectedAnnotationId}\n onDelete={effectiveDelete ?? (() => {})}\n onClose={() => {\n setShowAnnotationPanel(false)\n setSelectedAnnotationId(null)\n }}\n />\n )}\n </div>\n )}\n\n {/* Grid View */}\n {viewMode === \"grid\" && (\n <div className=\"mx-auto max-w-7xl p-8 pt-16 print:hidden\">\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n const prevSection = index > 0 ? slides[index - 1]?.section : undefined\n const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection\n\n return (\n <div key={index} className={showSectionHeader ? \"col-span-full\" : undefined}>\n {showSectionHeader && (\n <h3 className=\"mt-4 mb-3 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0\">\n {slideConfig.section}\n </h3>\n )}\n <button\n onClick={() => {\n goToSlide(index)\n setViewMode(\"slide\")\n }}\n className=\"group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-primary hover:shadow-lg hover:shadow-primary/10\"\n >\n <div\n className=\"h-full w-full origin-top-left scale-[0.25]\"\n style={{ width: \"400%\", height: \"400%\" }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n <div className=\"absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20\" />\n <div className=\"absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white\">\n {slideConfig.title ? `${index + 1}. ${slideConfig.title}` : index + 1}\n </div>\n </button>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* List View */}\n <div\n className={cn(\n \"mx-auto max-w-7xl p-8 pt-16\",\n \"print:m-0 print:block print:max-w-none print:p-0\",\n viewMode === \"list\" ? \"block\" : \"hidden print:block\"\n )}\n >\n <div className=\"grid grid-cols-1 gap-8 print:block\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n return (\n <div\n key={index}\n className=\"aspect-video w-full overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-sm print:relative print:m-0 print:h-[1080px] print:w-[1920px] print:break-after-page print:overflow-hidden print:rounded-none print:border-0 print:shadow-none\"\n >\n <div className=\"h-full w-full print:h-[720px] print:w-[1280px] print:origin-top-left print:scale-[1.5]\">\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { SlideConfig } from \"../types\"\nimport { AnnotationForm } from \"./annotation-form\"\nimport { AnnotationPin } from \"./annotation-pin\"\nimport { buildElementTarget, resolveTarget } from \"./selectors\"\nimport type { Annotation, AnnotationTarget } from \"./types\"\n\ninterface AnnotationOverlayProps {\n slides: SlideConfig[]\n currentSlide: number\n /** Ref to the slide container element (the div wrapping SlideRenderer) */\n slideContainerRef: React.RefObject<HTMLDivElement | null>\n selectedId: string | null\n onSelectId: (id: string | null) => void\n onShowPanel: () => void\n slideAnnotations: Annotation[]\n addAnnotation: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n}\n\ninterface PendingAnnotation {\n target: AnnotationTarget\n xPercent: number\n yPercent: number\n}\n\nexport function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId, onSelectId, onShowPanel, slideAnnotations, addAnnotation }: AnnotationOverlayProps) {\n const [pending, setPending] = useState<PendingAnnotation | null>(null)\n const [hoveredElement, setHoveredElement] = useState<DOMRect | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`\n\n // Resolve annotation positions — use the element if found, otherwise fallback coordinates\n const resolvedAnnotations = slideAnnotations.map((a, i) => {\n const container = slideContainerRef.current\n if (!container) {\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n }\n\n const { element } = resolveTarget(a.target, container)\n if (element) {\n const rect = element.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((rect.left + rect.width / 2 - containerRect.left) / containerRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - containerRect.top) / containerRect.height) * 100\n return { annotation: a, xPercent, yPercent, number: i + 1 }\n }\n\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n })\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n const container = slideContainerRef.current\n if (!container) return\n\n // Get the element under the click by temporarily hiding the overlay\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (!elementUnder || !container.contains(elementUnder)) {\n setPending(null)\n onSelectId(null)\n return\n }\n\n // Pick the most specific meaningful element (skip tiny text nodes, pick their parent)\n let target = elementUnder\n if (target.tagName === \"SPAN\" && target.parentElement && container.contains(target.parentElement)) {\n // For inline spans, prefer their parent for a more meaningful target\n const parent = target.parentElement\n if (parent.tagName !== \"DIV\" || parent.children.length <= 3) {\n target = parent\n }\n }\n\n const annotationTarget = buildElementTarget(target, container)\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((e.clientX - containerRect.left) / containerRect.width) * 100\n const yPercent = ((e.clientY - containerRect.top) / containerRect.height) * 100\n\n setPending({ target: annotationTarget, xPercent, yPercent })\n onSelectId(null)\n },\n [slideContainerRef, onSelectId]\n )\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!pending) return\n addAnnotation(currentSlide, slideTitle, pending.target, text)\n setPending(null)\n onShowPanel()\n },\n [pending, currentSlide, slideTitle, addAnnotation, onShowPanel]\n )\n\n // Track hover for element highlighting\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n if (pending) {\n setHoveredElement(null)\n return\n }\n\n const container = slideContainerRef.current\n if (!container) return\n\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {\n const containerRect = container.getBoundingClientRect()\n const elRect = elementUnder.getBoundingClientRect()\n setHoveredElement(\n new DOMRect(\n elRect.left - containerRect.left,\n elRect.top - containerRect.top,\n elRect.width,\n elRect.height\n )\n )\n } else {\n setHoveredElement(null)\n }\n },\n [slideContainerRef, pending]\n )\n\n // Clear pending when slide changes\n useEffect(() => {\n setPending(null)\n onSelectId(null)\n setHoveredElement(null)\n }, [currentSlide, onSelectId])\n\n return (\n <>\n {/* Hover highlight */}\n {hoveredElement && (\n <div\n className=\"pointer-events-none absolute z-20 rounded-lg border-2 border-dashed border-[#FF6B35]/50 bg-[#FF6B35]/5\"\n style={{\n left: hoveredElement.x,\n top: hoveredElement.y,\n width: hoveredElement.width,\n height: hoveredElement.height\n }}\n />\n )}\n\n {/* Click capture overlay */}\n <div\n ref={overlayRef}\n role=\"button\"\n tabIndex={0}\n className=\"absolute inset-0 z-20\"\n style={{ cursor: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none'%3E%3Ccircle cx='12' cy='12' r='8' stroke='%23FF6B35' stroke-width='2' opacity='0.8'/%3E%3Ccircle cx='12' cy='12' r='2' fill='%23FF6B35'/%3E%3C/svg%3E\") 12 12, crosshair` }}\n onClick={handleOverlayClick}\n onKeyDown={e => { if (e.key === \"Escape\") { setPending(null); onSelectId(null) } }}\n onMouseMove={handleMouseMove}\n onMouseLeave={() => setHoveredElement(null)}\n />\n\n {/* Annotation pins */}\n {resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => (\n <AnnotationPin\n key={annotation.id}\n number={number}\n status={annotation.status}\n xPercent={xPercent}\n yPercent={yPercent}\n isSelected={annotation.id === selectedId}\n onClick={() => {\n onSelectId(annotation.id === selectedId ? null : annotation.id)\n onShowPanel()\n setPending(null)\n }}\n />\n ))}\n\n {/* New annotation form */}\n {pending && (\n <AnnotationForm\n xPercent={pending.xPercent}\n yPercent={pending.yPercent}\n onSubmit={handleSubmit}\n onCancel={() => setPending(null)}\n />\n )}\n\n </>\n )\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport { ArrowUp, X } from \"lucide-react\"\n\ninterface AnnotationFormProps {\n xPercent: number\n yPercent: number\n onSubmit: (text: string) => void\n onCancel: () => void\n}\n\nexport function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }: AnnotationFormProps) {\n const [text, setText] = useState(\"\")\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n const handleSubmit = () => {\n const trimmed = text.trim()\n if (!trimmed) return\n onSubmit(trimmed)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault()\n handleSubmit()\n }\n if (e.key === \"Escape\") {\n onCancel()\n }\n e.stopPropagation()\n }\n\n // Position the form, flipping if near edges\n const left = xPercent > 70 ? undefined : `${xPercent}%`\n const right = xPercent > 70 ? `${100 - xPercent}%` : undefined\n const top = yPercent > 70 ? undefined : `${yPercent}%`\n const bottom = yPercent > 70 ? `${100 - yPercent}%` : undefined\n\n return (\n <div\n role=\"dialog\"\n className=\"absolute z-40 w-72 overflow-hidden rounded-xl border border-white/[0.08] bg-neutral-900/95 shadow-2xl backdrop-blur-2xl\"\n style={{ left, right, top, bottom }}\n onClick={e => e.stopPropagation()}\n onKeyDown={e => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b border-white/[0.06] px-3.5 py-2.5\">\n <span className=\"text-xs font-medium tracking-wide text-neutral-400\">Add annotation</span>\n <button\n onClick={onCancel}\n className=\"rounded-lg p-1 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"p-3\">\n <textarea\n ref={textareaRef}\n value={text}\n onChange={e => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe the change you want...\"\n className=\"w-full resize-none rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-[#FF6B35]/50 focus:bg-white/[0.06]\"\n rows={3}\n />\n <div className=\"mt-2.5 flex items-center justify-between\">\n <span className=\"text-[11px] text-neutral-600\">Enter to send</span>\n <button\n onClick={handleSubmit}\n disabled={!text.trim()}\n className=\"flex h-7 w-7 items-center justify-center rounded-lg bg-[#FF6B35] text-white shadow-lg shadow-[#FF6B35]/20 transition-all hover:bg-[#FF7A4A] hover:shadow-[#FF6B35]/30 disabled:opacity-30 disabled:shadow-none disabled:hover:bg-[#FF6B35]\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" strokeWidth={2.5} />\n </button>\n </div>\n </div>\n </div>\n )\n}\n","interface AnnotationPinProps {\n number: number\n status: \"open\" | \"resolved\"\n xPercent: number\n yPercent: number\n isSelected: boolean\n onClick: () => void\n}\n\nexport function AnnotationPin({ number, status, xPercent, yPercent, isSelected, onClick }: AnnotationPinProps) {\n const isResolved = status === \"resolved\"\n\n return (\n <button\n onClick={e => {\n e.stopPropagation()\n onClick()\n }}\n className=\"absolute z-30 -translate-x-1/2 -translate-y-1/2 transition-all duration-200 hover:scale-110\"\n style={{ left: `${xPercent}%`, top: `${yPercent}%` }}\n title={`Annotation #${number}`}\n >\n {/* Glow ring */}\n {isSelected && !isResolved && (\n <div className=\"absolute inset-[-4px] animate-pulse rounded-full bg-[#FF6B35]/25 blur-sm\" />\n )}\n <div\n className={`relative flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-semibold shadow-lg backdrop-blur-sm transition-all duration-200 ${\n isSelected\n ? isResolved\n ? \"bg-neutral-500/90 text-white ring-2 ring-neutral-400/50\"\n : \"bg-[#FF6B35] text-white ring-2 ring-[#FF6B35]/40 ring-offset-1 ring-offset-black/50\"\n : isResolved\n ? \"bg-neutral-700/80 text-neutral-400\"\n : \"bg-[#FF6B35]/90 text-white hover:bg-[#FF6B35]\"\n }`}\n >\n {number}\n </div>\n </button>\n )\n}\n","import type { AnnotationTarget } from \"./types\"\n\n/**\n * Build a composite target descriptor from a clicked DOM element.\n */\nexport function buildElementTarget(\n element: HTMLElement,\n slideRoot: HTMLElement\n): AnnotationTarget {\n const rect = element.getBoundingClientRect()\n const rootRect = slideRoot.getBoundingClientRect()\n\n const xPercent = ((rect.left + rect.width / 2 - rootRect.left) / rootRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - rootRect.top) / rootRect.height) * 100\n\n return {\n dataAnnotate: element.getAttribute(\"data-annotate\") || undefined,\n contentNearPin: getTextFingerprint(element),\n position: { xPercent, yPercent }\n }\n}\n\n/**\n * Resolve a stored target back to a DOM element, trying strategies in priority order.\n */\nexport function resolveTarget(\n target: AnnotationTarget,\n slideRoot: HTMLElement\n): { element: HTMLElement | null; method: \"dataAnnotate\" | \"contentNearPin\" | \"position\" } {\n // 1. Try data-annotate attribute\n if (target.dataAnnotate) {\n const el = slideRoot.querySelector<HTMLElement>(`[data-annotate=\"${target.dataAnnotate}\"]`)\n if (el) return { element: el, method: \"dataAnnotate\" }\n }\n\n // 2. Try text content match\n if (target.contentNearPin) {\n const match = findByTextContent(slideRoot, target.contentNearPin)\n if (match) return { element: match, method: \"contentNearPin\" }\n }\n\n // 3. Fallback to coordinates (no element found)\n return { element: null, method: \"position\" }\n}\n\n/**\n * Extract a text fingerprint from an element (first 100 chars, trimmed).\n */\nfunction getTextFingerprint(element: HTMLElement): string | undefined {\n const text = element.textContent?.trim()\n if (!text) return undefined\n return text.slice(0, 100)\n}\n\n/**\n * Find an element by matching its text content.\n */\nfunction findByTextContent(root: HTMLElement, text: string): HTMLElement | null {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)\n let best: HTMLElement | null = null\n let bestLength = Infinity\n\n while (walker.nextNode()) {\n const el = walker.currentNode as HTMLElement\n const elText = el.textContent?.trim()\n if (!elText) continue\n\n const elFingerprint = elText.slice(0, 100)\n if (elFingerprint === text && elText.length < bestLength) {\n best = el\n bestLength = elText.length\n }\n }\n\n return best\n}\n","import { MessageCircle, Trash2, X } from \"lucide-react\"\nimport type { Annotation } from \"./types\"\n\ninterface AnnotationPanelProps {\n annotations: Annotation[]\n selectedId: string | null\n onSelect: (id: string) => void\n onDelete: (id: string) => void\n onClose: () => void\n}\n\nexport function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }: AnnotationPanelProps) {\n const open = annotations.filter(a => a.status === \"open\")\n const resolved = annotations.filter(a => a.status === \"resolved\")\n\n return (\n <div className=\"flex h-full w-80 flex-shrink-0 flex-col border-l border-white/[0.06] bg-neutral-950/95 backdrop-blur-2xl\">\n <div className=\"flex items-center justify-between px-4 py-3.5\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex h-6 w-6 items-center justify-center rounded-lg bg-[#FF6B35]/15\">\n <MessageCircle className=\"h-3.5 w-3.5 text-[#FF6B35]\" />\n </div>\n <span className=\"text-sm font-medium text-white\">Annotations</span>\n {open.length > 0 && (\n <span className=\"flex h-5 min-w-5 items-center justify-center rounded-full bg-[#FF6B35]/15 px-1.5 text-[11px] font-semibold text-[#FF6B35]\">\n {open.length}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n\n <div className=\"flex-1 overflow-y-auto p-2\">\n {annotations.length === 0 && (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-white/[0.04]\">\n <MessageCircle className=\"h-5 w-5 text-neutral-600\" />\n </div>\n <p className=\"text-sm text-neutral-500\">Click on slide elements to add annotations</p>\n </div>\n )}\n\n {open.length > 0 && (\n <div>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Open\n </div>\n {open.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n\n {resolved.length > 0 && (\n <div className={open.length > 0 ? \"mt-3\" : \"\"}>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Resolved\n </div>\n {resolved.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={open.length + i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nfunction AnnotationItem({\n annotation,\n number,\n isSelected,\n onSelect,\n onDelete\n}: {\n annotation: Annotation\n number: number\n isSelected: boolean\n onSelect: () => void\n onDelete: () => void\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onSelect}\n onKeyDown={e => { if (e.key === \"Enter\" || e.key === \" \") onSelect() }}\n className={`group relative mb-0.5 cursor-pointer rounded-xl p-2.5 transition-all duration-150 ${\n isSelected\n ? \"bg-[#FF6B35]/10 ring-1 ring-[#FF6B35]/20\"\n : \"hover:bg-white/[0.04]\"\n }`}\n >\n <button\n onClick={e => {\n e.stopPropagation()\n onDelete()\n }}\n className=\"absolute top-2 right-2 rounded-lg p-1 text-neutral-600 opacity-0 transition-all hover:bg-white/[0.08] hover:text-neutral-300 group-hover:opacity-100\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </button>\n <div className=\"flex items-start gap-2.5\">\n <div\n className={`mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-[11px] font-semibold ${\n annotation.status === \"open\"\n ? \"bg-[#FF6B35] text-white\"\n : \"bg-neutral-700 text-neutral-400\"\n }`}\n >\n {number}\n </div>\n <div className=\"min-w-0 pr-4\">\n <p className=\"text-[13px] leading-relaxed text-neutral-200\">{annotation.body}</p>\n {annotation.target.contentNearPin && (\n <p className=\"mt-1 truncate text-[11px] text-neutral-600\">\n {annotation.target.contentNearPin}\n </p>\n )}\n {annotation.resolution && (\n <p className=\"mt-1.5 text-[11px] text-emerald-400/80 italic\">\n {annotation.resolution}\n </p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { createHttpAdapter } from \"./adapters/http\"\nimport type { Annotation, AnnotationStorageAdapter, AnnotationTarget } from \"./types\"\n\nexport function useAnnotations(adapter?: AnnotationStorageAdapter) {\n const adapterRef = useRef(adapter ?? createHttpAdapter())\n const [annotations, setAnnotations] = useState<Annotation[]>([])\n\n // Load on mount and subscribe to external updates\n useEffect(() => {\n adapterRef.current.load().then(setAnnotations)\n return adapterRef.current.subscribe?.(setAnnotations)\n }, [])\n\n const addAnnotation = useCallback(\n (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => {\n const annotation: Annotation = {\n id: crypto.randomUUID(),\n slideIndex,\n slideTitle,\n target,\n body,\n createdAt: new Date().toISOString(),\n status: \"open\"\n }\n setAnnotations(prev => [...prev, annotation])\n adapterRef.current.add(annotation)\n },\n []\n )\n\n const deleteAnnotation = useCallback((id: string) => {\n setAnnotations(prev => prev.filter(a => a.id !== id))\n adapterRef.current.remove(id)\n }, [])\n\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => annotations.filter(a => a.slideIndex === slideIndex),\n [annotations]\n )\n\n const openCount = useMemo(() => annotations.filter(a => a.status === \"open\").length, [annotations])\n\n // Allow external state updates (e.g. from postMessage adapter)\n const updateAnnotations = useCallback((updated: Annotation[]) => {\n setAnnotations(updated)\n }, [])\n\n return { annotations, addAnnotation, deleteAnnotation, getSlideAnnotations, openCount, updateAnnotations }\n}\n","import type { Annotation, AnnotationsFile, AnnotationStorageAdapter } from \"../types\"\n\nconst ENDPOINT = \"/__promptslide_annotations\"\n\nasync function loadAll(): Promise<Annotation[]> {\n try {\n const res = await fetch(ENDPOINT)\n if (!res.ok) return []\n const data: AnnotationsFile = await res.json()\n return data.annotations || []\n } catch {\n return []\n }\n}\n\nasync function saveAll(annotations: Annotation[]): Promise<void> {\n const data: AnnotationsFile = { version: 1, annotations }\n await fetch(ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data)\n })\n}\n\n/** Storage adapter for local Vite dev server (reads/writes annotations.json via middleware) */\nexport function createHttpAdapter(): AnnotationStorageAdapter {\n let cache: Annotation[] = []\n\n return {\n async load() {\n cache = await loadAll()\n return cache\n },\n async add(annotation) {\n cache = [...cache, annotation]\n await saveAll(cache)\n },\n async remove(id) {\n cache = cache.filter(a => a.id !== id)\n await saveAll(cache)\n }\n }\n}\n","import type { ClassValue } from \"clsx\"\n\nimport { clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":"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","names":["jsx","motion","jsx","motion","createContext","useContext","useMemo","jsx","motion","jsx","jsx","motion","useCallback","useEffect","useState","jsx","useState","useEffect","useCallback","MessageCircle","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useState","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useRef","useCallback","useEffect","X","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useEffect","useCallback","useMemo","jsx","jsxs","useState","useEffect","useMemo","useCallback","useRef","MessageCircle"]}
1
+ {"version":3,"sources":["../src/core/animation-config.ts","../src/core/animation-context.tsx","../src/core/animated.tsx","../src/core/transitions.ts","../src/core/morph.tsx","../src/core/use-slide-navigation.ts","../src/core/theme-context.tsx","../src/core/slide-renderer.tsx","../src/core/slide-error-boundary.tsx","../src/core/slide-embed.tsx","../src/core/slide-deck.tsx","../src/core/annotations/annotation-overlay.tsx","../src/core/annotations/annotation-form.tsx","../src/core/annotations/annotation-pin.tsx","../src/core/annotations/selectors.ts","../src/core/annotations/annotation-panel.tsx","../src/core/annotations/use-annotations.ts","../src/core/annotations/adapters/http.ts","../src/core/utils.ts"],"sourcesContent":["/**\n * Centralized animation configuration for the slide deck framework.\n * All animation timings, easings, and spring configs are defined here.\n */\n\n// =============================================================================\n// TIMING CONSTANTS\n// =============================================================================\n\n/** Duration for slide-to-slide transitions (seconds) */\nexport const SLIDE_TRANSITION_DURATION = 0.3\n\n/** Duration for morph/layout animations between slides (seconds) */\nexport const MORPH_DURATION = 0.8\n\n/** Duration for within-slide step animations (seconds) */\nexport const STEP_ANIMATION_DURATION = 0.4\n\n/** Default stagger delay for grouped animations (seconds) */\nexport const STAGGER_DELAY = 0.1\n\n// =============================================================================\n// EASING PRESETS\n// =============================================================================\n\nexport const EASE_DEFAULT = \"easeInOut\" as const\nexport const EASE_OUT = \"easeOut\" as const\nexport const EASE_IN = \"easeIn\" as const\n\n/** Smooth ease-in-out curve for morph animations (cubic bezier) */\nexport const EASE_MORPH = [0.4, 0, 0.2, 1] as const\n\n// =============================================================================\n// SPRING CONFIGURATIONS\n// =============================================================================\n\n/** Spring config for snappy, responsive animations */\nexport const SPRING_SNAPPY = {\n type: \"spring\" as const,\n stiffness: 300,\n damping: 30\n}\n\n/** Spring config for smooth, gentle animations */\nexport const SPRING_SMOOTH = {\n type: \"spring\" as const,\n stiffness: 200,\n damping: 25\n}\n\n/** Spring config for bouncy animations */\nexport const SPRING_BOUNCY = {\n type: \"spring\" as const,\n stiffness: 400,\n damping: 20\n}\n\n// =============================================================================\n// TRANSITION PRESETS\n// =============================================================================\n\n/** Standard transition for slide transitions */\nexport const SLIDE_TRANSITION = {\n duration: SLIDE_TRANSITION_DURATION,\n ease: EASE_DEFAULT\n} as const\n\n/** Standard transition for morph animations (smooth ease-in-out) */\nexport const MORPH_TRANSITION = {\n duration: MORPH_DURATION,\n ease: EASE_MORPH\n}\n\n/** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */\nexport const STEP_TRANSITION = {\n ...SPRING_SNAPPY\n}\n\n// =============================================================================\n// DISTANCE CONSTANTS (pixels)\n// =============================================================================\n\n/** Distance for slide animations */\nexport const SLIDE_DISTANCE = 100\n\n/** Distance for within-slide element animations */\nexport const ELEMENT_SLIDE_DISTANCE = 30\n\n// =============================================================================\n// SLIDE DIMENSIONS\n// =============================================================================\n\n/** Standard slide dimensions (16:9 aspect ratio) */\nexport const SLIDE_DIMENSIONS = {\n width: 1280,\n height: 720,\n aspectRatio: 16 / 9\n} as const\n","import { createContext, useContext, useMemo } from \"react\"\n\ninterface AnimationContextValue {\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps in this slide (declared in slide config) */\n totalSteps: number\n /** When true, all animation steps should be visible (used for backward navigation) */\n showAllAnimations: boolean\n}\n\nconst AnimationContext = createContext<AnimationContextValue>({\n currentStep: 0,\n totalSteps: 0,\n showAllAnimations: false\n})\n\ninterface AnimationProviderProps {\n children: React.ReactNode\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps declared in slide config */\n totalSteps: number\n /** When true, show all animations regardless of currentStep (for backward navigation) */\n showAllAnimations?: boolean\n}\n\n/**\n * Provides animation context to child components.\n *\n * The totalSteps is now passed from parent (declared in slide config)\n * rather than discovered at runtime, eliminating race conditions.\n */\nexport function AnimationProvider({\n children,\n currentStep,\n totalSteps,\n showAllAnimations = false\n}: AnimationProviderProps) {\n const value = useMemo(\n () => ({\n currentStep,\n totalSteps,\n showAllAnimations\n }),\n [currentStep, totalSteps, showAllAnimations]\n )\n\n return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>\n}\n\nexport function useAnimationContext() {\n return useContext(AnimationContext)\n}\n","import { motion, Variants } from \"framer-motion\"\n\nimport {\n ELEMENT_SLIDE_DISTANCE,\n SPRING_SNAPPY,\n STAGGER_DELAY,\n STEP_ANIMATION_DURATION\n} from \"./animation-config\"\nimport { useAnimationContext } from \"./animation-context\"\n\n// =============================================================================\n// ANIMATION TYPES & VARIANTS\n// =============================================================================\n\nexport type AnimationType =\n | \"fade\"\n | \"slide-up\"\n | \"slide-down\"\n | \"slide-left\"\n | \"slide-right\"\n | \"scale\"\n\nconst animationVariants: Record<AnimationType, Variants> = {\n fade: {\n hidden: { opacity: 0 },\n visible: { opacity: 1 }\n },\n \"slide-up\": {\n hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-down\": {\n hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-left\": {\n hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n \"slide-right\": {\n hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n scale: {\n hidden: { opacity: 0, scale: 0.8 },\n visible: { opacity: 1, scale: 1 }\n }\n}\n\ninterface AnimatedProps {\n /** Which step reveals this content (1-indexed) */\n step: number\n /** Animation type */\n animation?: AnimationType\n /** Animation duration in seconds */\n duration?: number\n /** Delay after trigger in seconds */\n delay?: number\n /** Custom className */\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Animated element that appears at a specific animation step.\n *\n * The step count is now declared in slide config rather than discovered\n * at runtime, so this component simply consumes the context without\n * needing to register itself.\n */\nexport function Animated({\n step,\n animation = \"slide-up\",\n duration = STEP_ANIMATION_DURATION,\n delay = 0,\n className,\n children\n}: AnimatedProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= step\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={animationVariants[animation]}\n transition={{\n ...SPRING_SNAPPY,\n duration,\n delay: isVisible ? delay : 0\n }}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n/**\n * Wrapper for staggering multiple children.\n * Each direct child will be animated in sequence.\n */\ninterface AnimatedGroupProps {\n /** Starting step for the first child */\n startStep: number\n /** Animation type for all children */\n animation?: AnimationType\n /** Delay between each child in seconds */\n staggerDelay?: number\n className?: string\n children: React.ReactNode\n}\n\nexport function AnimatedGroup({\n startStep,\n animation = \"slide-up\",\n staggerDelay = STAGGER_DELAY,\n className,\n children\n}: AnimatedGroupProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n const childArray = Array.isArray(children) ? children : [children]\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= startStep\n\n const containerVariants: Variants = {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: staggerDelay\n }\n }\n }\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={containerVariants}\n className={className}\n >\n {childArray.map((child, index) => (\n <motion.div key={index} variants={animationVariants[animation]}>\n {child}\n </motion.div>\n ))}\n </motion.div>\n )\n}\n","/**\n * Slide transition variants for the slide deck framework.\n * These define how slides enter and exit during navigation.\n */\n\nimport type { Variants } from \"framer-motion\"\n\nimport {\n EASE_MORPH,\n MORPH_DURATION,\n SLIDE_DISTANCE,\n SLIDE_TRANSITION,\n SLIDE_TRANSITION_DURATION\n} from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type SlideTransitionType =\n | \"fade\"\n | \"slide-left\"\n | \"slide-right\"\n | \"slide-up\"\n | \"slide-down\"\n | \"zoom\"\n | \"zoom-fade\"\n | \"morph\"\n | \"none\"\n\nexport interface SlideTransitionConfig {\n /** The transition type */\n type: SlideTransitionType\n /** Optional custom duration (overrides default) */\n duration?: number\n /** Whether to use directional transitions based on navigation direction */\n directional?: boolean\n}\n\n// =============================================================================\n// STATIC VARIANTS (non-directional)\n// =============================================================================\n\nconst fadeVariants: Variants = {\n enter: { opacity: 0 },\n center: { opacity: 1 },\n exit: { opacity: 0 }\n}\n\nconst slideLeftVariants: Variants = {\n enter: { x: SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideRightVariants: Variants = {\n enter: { x: -SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideUpVariants: Variants = {\n enter: { y: SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideDownVariants: Variants = {\n enter: { y: -SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst zoomVariants: Variants = {\n enter: { scale: 0.8, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.2, opacity: 0 }\n}\n\nconst zoomFadeVariants: Variants = {\n enter: { scale: 0.95, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.05, opacity: 0 }\n}\n\nconst morphVariants: Variants = {\n enter: {\n opacity: 0,\n zIndex: 1\n },\n center: {\n opacity: 1,\n zIndex: 1,\n transition: {\n opacity: { delay: 0.05, duration: 0.25 }\n }\n },\n exit: {\n opacity: 0,\n zIndex: 0,\n transition: {\n opacity: { duration: MORPH_DURATION, ease: EASE_MORPH }\n }\n }\n}\n\nconst noneVariants: Variants = {\n enter: {},\n center: {},\n exit: {}\n}\n\n// =============================================================================\n// VARIANT REGISTRY\n// =============================================================================\n\nexport const SLIDE_VARIANTS: Record<SlideTransitionType, Variants> = {\n fade: fadeVariants,\n \"slide-left\": slideLeftVariants,\n \"slide-right\": slideRightVariants,\n \"slide-up\": slideUpVariants,\n \"slide-down\": slideDownVariants,\n zoom: zoomVariants,\n \"zoom-fade\": zoomFadeVariants,\n morph: morphVariants,\n none: noneVariants\n}\n\n// =============================================================================\n// DIRECTIONAL VARIANTS (based on navigation direction)\n// =============================================================================\n\nexport function createDirectionalVariants(axis: \"x\" | \"y\" = \"x\"): (direction: number) => Variants {\n return (direction: number) => ({\n enter: {\n [axis]: direction > 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n },\n center: {\n [axis]: 0,\n opacity: 1\n },\n exit: {\n [axis]: direction < 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n }\n })\n}\n\n/** Horizontal directional slide (left/right based on direction) */\nexport const directionalSlideX = createDirectionalVariants(\"x\")\n\n/** Vertical directional slide (up/down based on direction) */\nexport const directionalSlideY = createDirectionalVariants(\"y\")\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\nexport function getSlideVariants(\n config: SlideTransitionConfig | SlideTransitionType,\n direction: number = 1\n): Variants {\n const type = typeof config === \"string\" ? config : config.type\n const directional = typeof config === \"object\" ? config.directional : false\n\n if (directional && (type === \"slide-left\" || type === \"slide-right\")) {\n return directionalSlideX(direction)\n }\n\n if (directional && (type === \"slide-up\" || type === \"slide-down\")) {\n return directionalSlideY(direction)\n }\n\n return SLIDE_VARIANTS[type]\n}\n\nexport function getSlideTransition(config?: SlideTransitionConfig | SlideTransitionType): {\n duration: number\n ease: typeof SLIDE_TRANSITION.ease\n} {\n if (!config) return SLIDE_TRANSITION\n\n const type = typeof config === \"string\" ? config : config.type\n\n const defaultDuration = type === \"morph\" ? MORPH_DURATION : SLIDE_TRANSITION_DURATION\n\n const duration = typeof config === \"object\" && config.duration ? config.duration : defaultDuration\n\n return {\n ...SLIDE_TRANSITION,\n duration\n }\n}\n\n// =============================================================================\n// DEFAULT EXPORT\n// =============================================================================\n\nexport const DEFAULT_SLIDE_TRANSITION: SlideTransitionType = \"fade\"\n","import type { Transition } from \"framer-motion\"\n\nimport { motion } from \"framer-motion\"\n\nimport { MORPH_TRANSITION } from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface MorphProps {\n layoutId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\ninterface MorphGroupProps {\n groupId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\n// =============================================================================\n// MORPH COMPONENT\n// =============================================================================\n\n/**\n * Wrapper component that enables morph/shared-layout animations between slides.\n *\n * Usage:\n * ```tsx\n * // Slide 1 - Large version\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-6xl\">Title</h1>\n * </Morph>\n *\n * // Slide 2 - Small version (same layoutId = morphs between them)\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-2xl\">Title</h1>\n * </Morph>\n * ```\n */\nexport function Morph({\n layoutId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphProps) {\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH GROUP COMPONENT\n// =============================================================================\n\nexport function MorphGroup({\n groupId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphGroupProps) {\n return (\n <motion.div\n layout\n layoutId={groupId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH ITEM (for use within MorphGroup pattern)\n// =============================================================================\n\ninterface MorphItemProps {\n id: string\n prefix?: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphItem({\n id,\n prefix,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphItemProps) {\n const layoutId = prefix ? `${prefix}-${id}` : id\n\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH TEXT (specialized for text that changes size)\n// =============================================================================\n\ninterface MorphTextProps {\n layoutId: string\n as?: \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"p\" | \"span\"\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphText({\n layoutId,\n as: Component = \"span\",\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphTextProps) {\n const MotionComponent = motion[Component]\n\n return (\n <MotionComponent\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </MotionComponent>\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype NavigationStatus = \"idle\" | \"transitioning\"\n\ntype QueuedAction = \"advance\" | \"goBack\" | null\n\ninterface NavigationState {\n status: NavigationStatus\n direction: NavigationDirection\n}\n\nexport interface UseSlideNavigationOptions {\n slides: SlideConfig[]\n initialSlide?: number\n onSlideChange?: (slideIndex: number) => void\n}\n\nexport interface UseSlideNavigationReturn {\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n isTransitioning: boolean\n showAllAnimations: boolean\n advance: () => void\n goBack: () => void\n goToSlide: (index: number) => void\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// HOOK\n// =============================================================================\n\nexport function useSlideNavigation({\n slides,\n initialSlide = 0,\n onSlideChange\n}: UseSlideNavigationOptions): UseSlideNavigationReturn {\n const [currentSlide, setCurrentSlide] = useState(initialSlide)\n const [animationStep, setAnimationStep] = useState(0)\n\n const [navState, setNavState] = useState<NavigationState>({\n status: \"idle\",\n direction: 0\n })\n\n const [queuedAction, setQueuedAction] = useState<QueuedAction>(null)\n\n const totalSteps = slides[currentSlide]?.steps ?? 0\n\n const onTransitionComplete = useCallback(() => {\n setNavState(prev => {\n if (prev.status === \"transitioning\") {\n return { status: \"idle\", direction: 0 }\n }\n return prev\n })\n }, [])\n\n const advance = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"advance\")\n return\n }\n\n const currentTotalSteps = slides[currentSlide]?.steps ?? 0\n\n if (animationStep >= currentTotalSteps) {\n const nextSlide = (currentSlide + 1) % slides.length\n setNavState({ status: \"transitioning\", direction: 1 })\n setAnimationStep(0)\n setCurrentSlide(nextSlide)\n onSlideChange?.(nextSlide)\n } else {\n setAnimationStep(prev => prev + 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goBack = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"goBack\")\n return\n }\n\n if (animationStep <= 0) {\n const prevSlide = (currentSlide - 1 + slides.length) % slides.length\n const prevSlideSteps = slides[prevSlide]?.steps ?? 0\n setNavState({ status: \"transitioning\", direction: -1 })\n setAnimationStep(prevSlideSteps)\n setCurrentSlide(prevSlide)\n onSlideChange?.(prevSlide)\n } else {\n setAnimationStep(prev => prev - 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goToSlide = useCallback(\n (index: number) => {\n if (index < 0 || index >= slides.length || index === currentSlide) {\n return\n }\n\n const direction = index > currentSlide ? 1 : -1\n setNavState({ status: \"transitioning\", direction })\n setAnimationStep(0)\n setCurrentSlide(index)\n onSlideChange?.(index)\n },\n [currentSlide, slides.length, onSlideChange]\n )\n\n useEffect(() => {\n if (navState.status === \"idle\" && queuedAction !== null) {\n setQueuedAction(null)\n if (queuedAction === \"advance\") {\n advance()\n } else if (queuedAction === \"goBack\") {\n goBack()\n }\n }\n }, [navState.status, queuedAction, advance, goBack])\n\n return {\n currentSlide,\n animationStep,\n totalSteps,\n direction: navState.direction,\n isTransitioning: navState.status !== \"idle\",\n showAllAnimations: navState.direction === -1 && navState.status === \"transitioning\",\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n }\n}\n","import { createContext, useContext, useMemo } from \"react\"\nimport type { ThemeConfig } from \"./types\"\n\nconst ThemeContext = createContext<ThemeConfig | null>(null)\n\n/**\n * Access the full theme config. Returns null outside SlideThemeProvider.\n */\nexport function useTheme(): ThemeConfig | null {\n return useContext(ThemeContext)\n}\n\nfunction buildCssOverrides(theme: ThemeConfig): React.CSSProperties {\n const style: Record<string, string> = {}\n\n if (theme.colors?.primary) style[\"--primary\"] = theme.colors.primary\n if (theme.colors?.primaryForeground) style[\"--primary-foreground\"] = theme.colors.primaryForeground\n if (theme.colors?.secondary) style[\"--secondary\"] = theme.colors.secondary\n if (theme.colors?.secondaryForeground) style[\"--secondary-foreground\"] = theme.colors.secondaryForeground\n if (theme.colors?.accent) style[\"--accent\"] = theme.colors.accent\n if (theme.colors?.accentForeground) style[\"--accent-foreground\"] = theme.colors.accentForeground\n\n if (theme.fonts?.heading) style[\"--font-heading\"] = theme.fonts.heading\n if (theme.fonts?.body) style[\"--font-body\"] = theme.fonts.body\n\n return style as React.CSSProperties\n}\n\ninterface SlideThemeProviderProps {\n theme: ThemeConfig\n children: React.ReactNode\n}\n\n/**\n * Provides the full theme to all descendants.\n * Injects CSS variable overrides via inline style.\n */\nexport function SlideThemeProvider({ theme, children }: SlideThemeProviderProps) {\n const cssOverrides = useMemo(() => buildCssOverrides(theme), [theme])\n\n return (\n <ThemeContext.Provider value={theme}>\n <div style={cssOverrides} className=\"contents\">\n {children}\n </div>\n </ThemeContext.Provider>\n )\n}\n","import { AnimatePresence, motion } from \"framer-motion\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\nimport { SLIDE_TRANSITION } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { DEFAULT_SLIDE_TRANSITION, getSlideVariants } from \"./transitions\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface SlideRendererProps {\n slides: SlideConfig[]\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n showAllAnimations: boolean\n transition?: SlideTransitionType\n directionalTransition?: boolean\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Renders a single slide with animated transitions.\n * Extracted from SlideDeck so it can be reused in SlideEmbed and other contexts.\n */\nexport function SlideRenderer({\n slides,\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n transition,\n directionalTransition,\n onTransitionComplete\n}: SlideRendererProps) {\n const currentSlideTransition = slides[currentSlide]?.transition\n const transitionType = currentSlideTransition ?? transition ?? DEFAULT_SLIDE_TRANSITION\n const isDirectional = directionalTransition ?? false\n\n const slideVariants = getSlideVariants(\n { type: transitionType, directional: isDirectional },\n direction\n )\n\n const CurrentSlideComponent = slides[currentSlide]!.component\n\n return (\n <AnimatePresence initial={false}>\n <motion.div\n key={currentSlide}\n variants={slideVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={SLIDE_TRANSITION}\n onAnimationComplete={definition => {\n if (definition === \"center\") {\n onTransitionComplete()\n }\n }}\n className=\"absolute inset-0 h-full w-full\"\n >\n <AnimationProvider\n currentStep={animationStep}\n totalSteps={totalSteps}\n showAllAnimations={showAllAnimations}\n >\n <SlideErrorBoundary\n slideIndex={currentSlide}\n slideTitle={slides[currentSlide]?.title}\n >\n <CurrentSlideComponent\n slideNumber={currentSlide + 1}\n totalSlides={slides.length}\n />\n </SlideErrorBoundary>\n </AnimationProvider>\n </motion.div>\n </AnimatePresence>\n )\n}\n","import React from \"react\"\n\ninterface SlideErrorBoundaryProps {\n slideIndex: number\n slideTitle?: string\n children: React.ReactNode\n}\n\ninterface SlideErrorBoundaryState {\n hasError: boolean\n error: Error | null\n}\n\nexport class SlideErrorBoundary extends React.Component<\n SlideErrorBoundaryProps,\n SlideErrorBoundaryState\n> {\n state: SlideErrorBoundaryState = { hasError: false, error: null }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `[PromptSlide] Slide ${this.props.slideIndex + 1}${this.props.slideTitle ? ` (\"${this.props.slideTitle}\")` : \"\"} crashed:`,\n error,\n errorInfo\n )\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex h-full w-full flex-col items-center justify-center bg-red-950/20 p-12\">\n <div className=\"mb-4 text-lg font-semibold text-red-400\">\n Slide {this.props.slideIndex + 1} Error\n {this.props.slideTitle && (\n <span className=\"ml-2 font-normal text-red-400/70\">({this.props.slideTitle})</span>\n )}\n </div>\n <div className=\"max-w-2xl text-center font-mono text-sm break-words text-red-300/80\">\n {this.state.error?.message ?? \"Unknown error\"}\n </div>\n </div>\n )\n }\n return this.props.children\n }\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface SlideEmbedProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Headless slide viewer controlled via window.postMessage.\n * Designed for embedding in an iframe (e.g. the registry editor preview).\n *\n * Inbound messages (parent → embed):\n * { type: \"navigate\", data: { slide: number } }\n * { type: \"advance\" }\n * { type: \"goBack\" }\n *\n * Outbound messages (embed → parent):\n * { type: \"slideReady\" }\n * { type: \"slideState\", data: { currentSlide, totalSlides, animationStep, totalSteps, titles } }\n * { type: \"hmrUpdate\" }\n */\nexport function SlideEmbed({ slides, transition, directionalTransition }: SlideEmbedProps) {\n const [scale, setScale] = useState(1)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({ slides })\n\n // Post slide state to parent whenever it changes\n useEffect(() => {\n const state = {\n currentSlide,\n totalSlides: slides.length,\n animationStep,\n totalSteps,\n titles: slides.map(s => s.title || \"\")\n }\n window.parent.postMessage({ type: \"slideState\", data: state }, \"*\")\n }, [currentSlide, animationStep, totalSteps, slides])\n\n // Signal readiness on mount\n useEffect(() => {\n window.parent.postMessage({ type: \"slideReady\" }, \"*\")\n }, [])\n\n // Listen for Vite HMR updates\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const hot = (import.meta as any).hot as { on: (event: string, cb: () => void) => void } | undefined\n if (hot) {\n hot.on(\"vite:afterUpdate\", () => {\n window.parent.postMessage({ type: \"hmrUpdate\" }, \"*\")\n })\n }\n }, [])\n\n // Listen for inbound messages from parent\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const { type, data } = event.data || {}\n switch (type) {\n case \"navigate\":\n if (typeof data?.slide === \"number\") goToSlide(data.slide)\n break\n case \"advance\":\n advance()\n break\n case \"goBack\":\n goBack()\n break\n }\n },\n [advance, goBack, goToSlide]\n )\n\n useEffect(() => {\n window.addEventListener(\"message\", handleMessage)\n return () => window.removeEventListener(\"message\", handleMessage)\n }, [handleMessage])\n\n // Scale to fit viewport while preserving aspect ratio\n useEffect(() => {\n const calculateScale = () => {\n const scaleX = window.innerWidth / SLIDE_DIMENSIONS.width\n const scaleY = window.innerHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [])\n\n return (\n <div className=\"h-screen w-screen overflow-hidden bg-black\">\n <div\n className=\"absolute left-1/2 top-1/2 overflow-hidden\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `translate(-50%, -50%) scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n </div>\n )\n}\n","import { LayoutGroup } from \"framer-motion\"\nimport { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, MessageCircle, Monitor } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { AnnotationOverlay, AnnotationPanel, useAnnotations } from \"./annotations\"\nimport type { Annotation, AnnotationTarget } from \"./annotations\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\nimport { cn } from \"./utils\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype ViewMode = \"slide\" | \"list\" | \"grid\"\n\ninterface SlideDeckProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n /** Annotation data to display. When provided (even empty array), annotation UI is enabled. When undefined, annotation UI is hidden. */\n annotations?: Annotation[]\n /** Called when the user creates an annotation */\n onAnnotationAdd?: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n /** Called when the user deletes an annotation */\n onAnnotationDelete?: (id: string) => void\n}\n\n// =============================================================================\n// EXPORT VIEW (for Playwright screenshot capture)\n// =============================================================================\n\nfunction SlideExportView({ slides, slideIndex }: { slides: SlideConfig[]; slideIndex: number }) {\n const [ready, setReady] = useState(false)\n const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1))\n const slideConfig = slides[clampedIndex]!\n const SlideComponent = slideConfig.component\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n return (\n <div\n data-export-ready={ready ? \"true\" : undefined}\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n overflow: \"hidden\",\n position: \"relative\",\n background: \"black\"\n }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={clampedIndex} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={clampedIndex + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n )\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nexport function SlideDeck({ slides, transition, directionalTransition, annotations, onAnnotationAdd, onAnnotationDelete }: SlideDeckProps) {\n // Check for export mode via URL params\n const [exportParams] = useState(() => {\n if (typeof window === \"undefined\") return null\n const params = new URLSearchParams(window.location.search)\n if (params.get(\"export\") !== \"true\") return null\n return { slideIndex: parseInt(params.get(\"slide\") || \"0\", 10) }\n })\n\n if (exportParams) {\n return <SlideExportView slides={slides} slideIndex={exportParams.slideIndex} />\n }\n\n // Use internal useAnnotations as fallback when no external annotations prop is provided\n const internal = useAnnotations()\n const isExternallyManaged = annotations !== undefined\n const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations\n const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation\n const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation\n\n const openCount = useMemo(() => effectiveAnnotations.filter(a => a.status === \"open\").length, [effectiveAnnotations])\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => effectiveAnnotations.filter(a => a.slideIndex === slideIndex),\n [effectiveAnnotations]\n )\n\n const [viewMode, setViewMode] = useState<ViewMode>(\"slide\")\n const [isPresentationMode, setIsPresentationMode] = useState(false)\n const [isAnnotationMode, setIsAnnotationMode] = useState(false)\n const [showAnnotationPanel, setShowAnnotationPanel] = useState(false)\n const [selectedAnnotationId, setSelectedAnnotationId] = useState<string | null>(null)\n const [scale, setScale] = useState(1)\n const containerRef = useRef<HTMLDivElement>(null)\n const slideContainerRef = useRef<HTMLDivElement>(null)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({\n slides\n })\n\n const togglePresentationMode = useCallback(async () => {\n if (!document.fullscreenElement) {\n await document.documentElement.requestFullscreen()\n } else {\n await document.exitFullscreen()\n }\n }, [])\n\n // Listen for fullscreen changes\n useEffect(() => {\n const handleFullscreenChange = () => {\n setIsPresentationMode(!!document.fullscreenElement)\n }\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange)\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange)\n }, [])\n\n // Calculate scale factor for presentation mode\n useEffect(() => {\n const calculateScale = () => {\n if (!isPresentationMode) {\n setScale(1)\n return\n }\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n const scaleX = viewportWidth / SLIDE_DIMENSIONS.width\n const scaleY = viewportHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [isPresentationMode])\n\n const handleExportPdf = () => {\n const previousMode = viewMode\n setViewMode(\"list\")\n\n setTimeout(() => {\n const handleAfterPrint = () => {\n setViewMode(previousMode)\n window.removeEventListener(\"afterprint\", handleAfterPrint)\n }\n window.addEventListener(\"afterprint\", handleAfterPrint)\n window.print()\n }, 100)\n }\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"f\" || e.key === \"F\") {\n togglePresentationMode()\n return\n }\n\n // G for grid view toggle\n if (e.key === \"g\" || e.key === \"G\") {\n setViewMode(prev => (prev === \"grid\" ? \"slide\" : \"grid\"))\n return\n }\n\n if (viewMode !== \"slide\" || (isAnnotationMode && !isPresentationMode)) return\n\n if (e.key === \"ArrowRight\" || e.key === \" \") {\n e.preventDefault()\n advance()\n } else if (e.key === \"ArrowLeft\") {\n e.preventDefault()\n goBack()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode, isPresentationMode])\n\n return (\n <div className=\"min-h-screen w-full bg-neutral-950 text-foreground\">\n <style>{`\n @media print {\n @page {\n size: 1920px 1080px;\n margin: 0;\n }\n html,\n body {\n width: 100%;\n height: 100%;\n margin: 0 !important;\n padding: 0 !important;\n overflow: visible !important;\n }\n body {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n background: transparent !important;\n }\n }\n `}</style>\n\n {/* Toolbar */}\n <div\n className={cn(\n \"fixed top-4 z-50 flex gap-1 rounded-lg border border-neutral-800 bg-neutral-950/90 p-1 backdrop-blur-sm transition-[right] print:hidden\",\n isPresentationMode && \"hidden\",\n isAnnotationMode && showAnnotationPanel ? \"right-[19.5rem]\" : \"right-4\"\n )}\n >\n <button\n onClick={() => setViewMode(\"slide\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"slide\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Presentation View\"\n >\n <Monitor className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"list\" && \"bg-neutral-800 text-white\"\n )}\n title=\"List View\"\n >\n <List className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"grid\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"grid\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Grid View\"\n >\n <Grid3X3 className=\"h-4 w-4\" />\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={() => {\n setIsAnnotationMode(prev => {\n const next = !prev\n setShowAnnotationPanel(next)\n if (!next) setSelectedAnnotationId(null)\n return next\n })\n }}\n className={cn(\n \"relative rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n isAnnotationMode && \"bg-[#FF6B35] text-white hover:bg-[#FF7A4A]\"\n )}\n title=\"Annotate slides\"\n >\n <MessageCircle className=\"h-4 w-4\" />\n {openCount > 0 && (\n <span className=\"absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#FF6B35] text-[10px] font-bold text-white\">\n {openCount}\n </span>\n )}\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={handleExportPdf}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Download PDF\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n <button\n onClick={togglePresentationMode}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Present (F)\"\n >\n <Maximize className=\"h-4 w-4\" />\n </button>\n </div>\n\n {/* Slide View */}\n {viewMode === \"slide\" && (\n <div\n className={cn(\n \"flex h-screen w-full print:hidden\",\n isPresentationMode ? \"bg-black\" : \"\"\n )}\n >\n <div\n ref={containerRef}\n role=\"presentation\"\n tabIndex={isPresentationMode ? 0 : undefined}\n className={cn(\n \"flex flex-1 flex-col items-center justify-center overflow-hidden\",\n isPresentationMode ? \"bg-black p-0\" : \"p-4 md:p-8\"\n )}\n onClick={isPresentationMode ? advance : undefined}\n onKeyDown={\n isPresentationMode\n ? e => {\n if (e.key === \"Enter\" || e.key === \" \") advance()\n }\n : undefined\n }\n >\n <LayoutGroup id=\"slide-deck\">\n {isPresentationMode ? (\n <div\n className=\"pointer-events-none relative overflow-hidden bg-black\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n ) : (\n <div ref={slideContainerRef} className=\"relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl\">\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n {isAnnotationMode && (\n <AnnotationOverlay\n slides={slides}\n currentSlide={currentSlide}\n slideContainerRef={slideContainerRef}\n selectedId={selectedAnnotationId}\n onSelectId={setSelectedAnnotationId}\n onShowPanel={() => setShowAnnotationPanel(true)}\n slideAnnotations={getSlideAnnotations(currentSlide)}\n addAnnotation={effectiveAdd ?? (() => {})}\n\n />\n )}\n </div>\n )}\n </LayoutGroup>\n\n {/* Navigation Controls */}\n {!isPresentationMode && (\n <div className=\"mt-6 flex items-center gap-4\">\n <button\n onClick={goBack}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <div className=\"flex min-w-[4rem] flex-col items-center\">\n <span className=\"font-mono text-sm text-neutral-500\">\n {currentSlide + 1} / {slides.length}\n </span>\n {slides[currentSlide]?.title && (\n <span className=\"mt-0.5 text-xs text-neutral-600\">\n {slides[currentSlide].title}\n </span>\n )}\n </div>\n <button\n onClick={advance}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Annotation Panel — beside the slide */}\n {isAnnotationMode && showAnnotationPanel && !isPresentationMode && (\n <AnnotationPanel\n annotations={getSlideAnnotations(currentSlide)}\n selectedId={selectedAnnotationId}\n onSelect={setSelectedAnnotationId}\n onDelete={effectiveDelete ?? (() => {})}\n onClose={() => {\n setShowAnnotationPanel(false)\n setSelectedAnnotationId(null)\n }}\n />\n )}\n </div>\n )}\n\n {/* Grid View */}\n {viewMode === \"grid\" && (\n <div className=\"mx-auto max-w-7xl p-8 pt-16 print:hidden\">\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n const prevSection = index > 0 ? slides[index - 1]?.section : undefined\n const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection\n\n return (\n <div key={index} className={showSectionHeader ? \"col-span-full\" : undefined}>\n {showSectionHeader && (\n <h3 className=\"mt-4 mb-3 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0\">\n {slideConfig.section}\n </h3>\n )}\n <button\n onClick={() => {\n goToSlide(index)\n setViewMode(\"slide\")\n }}\n className=\"group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-primary hover:shadow-lg hover:shadow-primary/10\"\n >\n <div\n className=\"h-full w-full origin-top-left scale-[0.25]\"\n style={{ width: \"400%\", height: \"400%\" }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n <div className=\"absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20\" />\n <div className=\"absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white\">\n {slideConfig.title ? `${index + 1}. ${slideConfig.title}` : index + 1}\n </div>\n </button>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* List View */}\n <div\n className={cn(\n \"mx-auto max-w-7xl p-8 pt-16\",\n \"print:m-0 print:block print:max-w-none print:p-0\",\n viewMode === \"list\" ? \"block\" : \"hidden print:block\"\n )}\n >\n <div className=\"grid grid-cols-1 gap-8 print:block\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n return (\n <div\n key={index}\n className=\"aspect-video w-full overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-sm print:relative print:m-0 print:h-[1080px] print:w-[1920px] print:break-after-page print:overflow-hidden print:rounded-none print:border-0 print:shadow-none\"\n >\n <div className=\"h-full w-full print:h-[720px] print:w-[1280px] print:origin-top-left print:scale-[1.5]\">\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { SlideConfig } from \"../types\"\nimport { AnnotationForm } from \"./annotation-form\"\nimport { AnnotationPin } from \"./annotation-pin\"\nimport { buildElementTarget, resolveTarget } from \"./selectors\"\nimport type { Annotation, AnnotationTarget } from \"./types\"\n\ninterface AnnotationOverlayProps {\n slides: SlideConfig[]\n currentSlide: number\n /** Ref to the slide container element (the div wrapping SlideRenderer) */\n slideContainerRef: React.RefObject<HTMLDivElement | null>\n selectedId: string | null\n onSelectId: (id: string | null) => void\n onShowPanel: () => void\n slideAnnotations: Annotation[]\n addAnnotation: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n}\n\ninterface PendingAnnotation {\n target: AnnotationTarget\n xPercent: number\n yPercent: number\n}\n\nexport function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId, onSelectId, onShowPanel, slideAnnotations, addAnnotation }: AnnotationOverlayProps) {\n const [pending, setPending] = useState<PendingAnnotation | null>(null)\n const [hoveredElement, setHoveredElement] = useState<DOMRect | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`\n\n // Resolve annotation positions — use the element if found, otherwise fallback coordinates\n const resolvedAnnotations = slideAnnotations.map((a, i) => {\n const container = slideContainerRef.current\n if (!container) {\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n }\n\n const { element } = resolveTarget(a.target, container)\n if (element) {\n const rect = element.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((rect.left + rect.width / 2 - containerRect.left) / containerRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - containerRect.top) / containerRect.height) * 100\n return { annotation: a, xPercent, yPercent, number: i + 1 }\n }\n\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n })\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n const container = slideContainerRef.current\n if (!container) return\n\n // Get the element under the click by temporarily hiding the overlay\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (!elementUnder || !container.contains(elementUnder)) {\n setPending(null)\n onSelectId(null)\n return\n }\n\n // Pick the most specific meaningful element (skip tiny text nodes, pick their parent)\n let target = elementUnder\n if (target.tagName === \"SPAN\" && target.parentElement && container.contains(target.parentElement)) {\n // For inline spans, prefer their parent for a more meaningful target\n const parent = target.parentElement\n if (parent.tagName !== \"DIV\" || parent.children.length <= 3) {\n target = parent\n }\n }\n\n const annotationTarget = buildElementTarget(target, container)\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((e.clientX - containerRect.left) / containerRect.width) * 100\n const yPercent = ((e.clientY - containerRect.top) / containerRect.height) * 100\n\n setPending({ target: annotationTarget, xPercent, yPercent })\n onSelectId(null)\n },\n [slideContainerRef, onSelectId]\n )\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!pending) return\n addAnnotation(currentSlide, slideTitle, pending.target, text)\n setPending(null)\n onShowPanel()\n },\n [pending, currentSlide, slideTitle, addAnnotation, onShowPanel]\n )\n\n // Track hover for element highlighting\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n if (pending) {\n setHoveredElement(null)\n return\n }\n\n const container = slideContainerRef.current\n if (!container) return\n\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {\n const containerRect = container.getBoundingClientRect()\n const elRect = elementUnder.getBoundingClientRect()\n setHoveredElement(\n new DOMRect(\n elRect.left - containerRect.left,\n elRect.top - containerRect.top,\n elRect.width,\n elRect.height\n )\n )\n } else {\n setHoveredElement(null)\n }\n },\n [slideContainerRef, pending]\n )\n\n // Clear pending when slide changes\n useEffect(() => {\n setPending(null)\n onSelectId(null)\n setHoveredElement(null)\n }, [currentSlide, onSelectId])\n\n return (\n <>\n {/* Hover highlight */}\n {hoveredElement && (\n <div\n className=\"pointer-events-none absolute z-20 rounded-lg border-2 border-dashed border-[#FF6B35]/50 bg-[#FF6B35]/5\"\n style={{\n left: hoveredElement.x,\n top: hoveredElement.y,\n width: hoveredElement.width,\n height: hoveredElement.height\n }}\n />\n )}\n\n {/* Click capture overlay */}\n <div\n ref={overlayRef}\n role=\"button\"\n tabIndex={0}\n className=\"absolute inset-0 z-20\"\n style={{ cursor: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none'%3E%3Ccircle cx='12' cy='12' r='8' stroke='%23FF6B35' stroke-width='2' opacity='0.8'/%3E%3Ccircle cx='12' cy='12' r='2' fill='%23FF6B35'/%3E%3C/svg%3E\") 12 12, crosshair` }}\n onClick={handleOverlayClick}\n onKeyDown={e => { if (e.key === \"Escape\") { setPending(null); onSelectId(null) } }}\n onMouseMove={handleMouseMove}\n onMouseLeave={() => setHoveredElement(null)}\n />\n\n {/* Annotation pins */}\n {resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => (\n <AnnotationPin\n key={annotation.id}\n number={number}\n status={annotation.status}\n xPercent={xPercent}\n yPercent={yPercent}\n isSelected={annotation.id === selectedId}\n onClick={() => {\n onSelectId(annotation.id === selectedId ? null : annotation.id)\n onShowPanel()\n setPending(null)\n }}\n />\n ))}\n\n {/* New annotation form */}\n {pending && (\n <AnnotationForm\n xPercent={pending.xPercent}\n yPercent={pending.yPercent}\n onSubmit={handleSubmit}\n onCancel={() => setPending(null)}\n />\n )}\n\n </>\n )\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport { ArrowUp, X } from \"lucide-react\"\n\ninterface AnnotationFormProps {\n xPercent: number\n yPercent: number\n onSubmit: (text: string) => void\n onCancel: () => void\n}\n\nexport function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }: AnnotationFormProps) {\n const [text, setText] = useState(\"\")\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n const handleSubmit = () => {\n const trimmed = text.trim()\n if (!trimmed) return\n onSubmit(trimmed)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault()\n handleSubmit()\n }\n if (e.key === \"Escape\") {\n onCancel()\n }\n e.stopPropagation()\n }\n\n // Position the form, flipping if near edges\n const left = xPercent > 70 ? undefined : `${xPercent}%`\n const right = xPercent > 70 ? `${100 - xPercent}%` : undefined\n const top = yPercent > 70 ? undefined : `${yPercent}%`\n const bottom = yPercent > 70 ? `${100 - yPercent}%` : undefined\n\n return (\n <div\n role=\"dialog\"\n className=\"absolute z-40 w-72 overflow-hidden rounded-xl border border-white/[0.08] bg-neutral-900/95 shadow-2xl backdrop-blur-2xl\"\n style={{ left, right, top, bottom }}\n onClick={e => e.stopPropagation()}\n onKeyDown={e => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b border-white/[0.06] px-3.5 py-2.5\">\n <span className=\"text-xs font-medium tracking-wide text-neutral-400\">Add annotation</span>\n <button\n onClick={onCancel}\n className=\"rounded-lg p-1 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"p-3\">\n <textarea\n ref={textareaRef}\n value={text}\n onChange={e => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe the change you want...\"\n className=\"w-full resize-none rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-[#FF6B35]/50 focus:bg-white/[0.06]\"\n rows={3}\n />\n <div className=\"mt-2.5 flex items-center justify-between\">\n <span className=\"text-[11px] text-neutral-600\">Enter to send</span>\n <button\n onClick={handleSubmit}\n disabled={!text.trim()}\n className=\"flex h-7 w-7 items-center justify-center rounded-lg bg-[#FF6B35] text-white shadow-lg shadow-[#FF6B35]/20 transition-all hover:bg-[#FF7A4A] hover:shadow-[#FF6B35]/30 disabled:opacity-30 disabled:shadow-none disabled:hover:bg-[#FF6B35]\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" strokeWidth={2.5} />\n </button>\n </div>\n </div>\n </div>\n )\n}\n","interface AnnotationPinProps {\n number: number\n status: \"open\" | \"resolved\"\n xPercent: number\n yPercent: number\n isSelected: boolean\n onClick: () => void\n}\n\nexport function AnnotationPin({ number, status, xPercent, yPercent, isSelected, onClick }: AnnotationPinProps) {\n const isResolved = status === \"resolved\"\n\n return (\n <button\n onClick={e => {\n e.stopPropagation()\n onClick()\n }}\n className=\"absolute z-30 -translate-x-1/2 -translate-y-1/2 transition-all duration-200 hover:scale-110\"\n style={{ left: `${xPercent}%`, top: `${yPercent}%` }}\n title={`Annotation #${number}`}\n >\n {/* Glow ring */}\n {isSelected && !isResolved && (\n <div className=\"absolute inset-[-4px] animate-pulse rounded-full bg-[#FF6B35]/25 blur-sm\" />\n )}\n <div\n className={`relative flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-semibold shadow-lg backdrop-blur-sm transition-all duration-200 ${\n isSelected\n ? isResolved\n ? \"bg-neutral-500/90 text-white ring-2 ring-neutral-400/50\"\n : \"bg-[#FF6B35] text-white ring-2 ring-[#FF6B35]/40 ring-offset-1 ring-offset-black/50\"\n : isResolved\n ? \"bg-neutral-700/80 text-neutral-400\"\n : \"bg-[#FF6B35]/90 text-white hover:bg-[#FF6B35]\"\n }`}\n >\n {number}\n </div>\n </button>\n )\n}\n","import type { AnnotationTarget } from \"./types\"\n\n/**\n * Build a composite target descriptor from a clicked DOM element.\n */\nexport function buildElementTarget(\n element: HTMLElement,\n slideRoot: HTMLElement\n): AnnotationTarget {\n const rect = element.getBoundingClientRect()\n const rootRect = slideRoot.getBoundingClientRect()\n\n const xPercent = ((rect.left + rect.width / 2 - rootRect.left) / rootRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - rootRect.top) / rootRect.height) * 100\n\n return {\n dataAnnotate: element.getAttribute(\"data-annotate\") || undefined,\n contentNearPin: getTextFingerprint(element),\n position: { xPercent, yPercent }\n }\n}\n\n/**\n * Resolve a stored target back to a DOM element, trying strategies in priority order.\n */\nexport function resolveTarget(\n target: AnnotationTarget,\n slideRoot: HTMLElement\n): { element: HTMLElement | null; method: \"dataAnnotate\" | \"contentNearPin\" | \"position\" } {\n // 1. Try data-annotate attribute\n if (target.dataAnnotate) {\n const el = slideRoot.querySelector<HTMLElement>(`[data-annotate=\"${target.dataAnnotate}\"]`)\n if (el) return { element: el, method: \"dataAnnotate\" }\n }\n\n // 2. Try text content match\n if (target.contentNearPin) {\n const match = findByTextContent(slideRoot, target.contentNearPin)\n if (match) return { element: match, method: \"contentNearPin\" }\n }\n\n // 3. Fallback to coordinates (no element found)\n return { element: null, method: \"position\" }\n}\n\n/**\n * Extract a text fingerprint from an element (first 100 chars, trimmed).\n */\nfunction getTextFingerprint(element: HTMLElement): string | undefined {\n const text = element.textContent?.trim()\n if (!text) return undefined\n return text.slice(0, 100)\n}\n\n/**\n * Find an element by matching its text content.\n */\nfunction findByTextContent(root: HTMLElement, text: string): HTMLElement | null {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)\n let best: HTMLElement | null = null\n let bestLength = Infinity\n\n while (walker.nextNode()) {\n const el = walker.currentNode as HTMLElement\n const elText = el.textContent?.trim()\n if (!elText) continue\n\n const elFingerprint = elText.slice(0, 100)\n if (elFingerprint === text && elText.length < bestLength) {\n best = el\n bestLength = elText.length\n }\n }\n\n return best\n}\n","import { MessageCircle, Trash2, X } from \"lucide-react\"\nimport type { Annotation } from \"./types\"\n\ninterface AnnotationPanelProps {\n annotations: Annotation[]\n selectedId: string | null\n onSelect: (id: string) => void\n onDelete: (id: string) => void\n onClose: () => void\n}\n\nexport function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }: AnnotationPanelProps) {\n const open = annotations.filter(a => a.status === \"open\")\n const resolved = annotations.filter(a => a.status === \"resolved\")\n\n return (\n <div className=\"flex h-full w-80 flex-shrink-0 flex-col border-l border-white/[0.06] bg-neutral-950/95 backdrop-blur-2xl\">\n <div className=\"flex items-center justify-between px-4 py-3.5\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex h-6 w-6 items-center justify-center rounded-lg bg-[#FF6B35]/15\">\n <MessageCircle className=\"h-3.5 w-3.5 text-[#FF6B35]\" />\n </div>\n <span className=\"text-sm font-medium text-white\">Annotations</span>\n {open.length > 0 && (\n <span className=\"flex h-5 min-w-5 items-center justify-center rounded-full bg-[#FF6B35]/15 px-1.5 text-[11px] font-semibold text-[#FF6B35]\">\n {open.length}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n\n <div className=\"flex-1 overflow-y-auto p-2\">\n {annotations.length === 0 && (\n <div className=\"flex flex-col gap-4 px-3 py-6\">\n <WorkflowHint />\n </div>\n )}\n\n {open.length > 0 && (\n <div>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Open\n </div>\n {open.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n\n {resolved.length > 0 && (\n <div className={open.length > 0 ? \"mt-3\" : \"\"}>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Resolved\n </div>\n {resolved.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={open.length + i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n </div>\n\n {annotations.length > 0 && (\n <>\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n <div className=\"px-3 py-3\">\n <WorkflowHint />\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction WorkflowHint() {\n return (\n <div className=\"flex flex-col gap-2\">\n <p className=\"text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">Workflow</p>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">1</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Annotate everything you want to fix</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">2</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Switch to your coding agent</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">3</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">\"Fix open annotations\"</p>\n </div>\n </div>\n )\n}\n\nfunction AnnotationItem({\n annotation,\n number,\n isSelected,\n onSelect,\n onDelete\n}: {\n annotation: Annotation\n number: number\n isSelected: boolean\n onSelect: () => void\n onDelete: () => void\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onSelect}\n onKeyDown={e => { if (e.key === \"Enter\" || e.key === \" \") onSelect() }}\n className={`group relative mb-0.5 cursor-pointer rounded-xl p-2.5 transition-all duration-150 ${\n isSelected\n ? \"bg-[#FF6B35]/10 ring-1 ring-[#FF6B35]/20\"\n : \"hover:bg-white/[0.04]\"\n }`}\n >\n <button\n onClick={e => {\n e.stopPropagation()\n onDelete()\n }}\n className=\"absolute top-2 right-2 rounded-lg p-1 text-neutral-600 opacity-0 transition-all hover:bg-white/[0.08] hover:text-neutral-300 group-hover:opacity-100\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </button>\n <div className=\"flex items-start gap-2.5\">\n <div\n className={`mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-[11px] font-semibold ${\n annotation.status === \"open\"\n ? \"bg-[#FF6B35] text-white\"\n : \"bg-neutral-700 text-neutral-400\"\n }`}\n >\n {number}\n </div>\n <div className=\"min-w-0 pr-4\">\n <p className=\"text-[13px] leading-relaxed text-neutral-200\">{annotation.body}</p>\n {annotation.target.contentNearPin && (\n <p className=\"mt-1 truncate text-[11px] text-neutral-600\">\n {annotation.target.contentNearPin}\n </p>\n )}\n {annotation.resolution && (\n <p className=\"mt-1.5 text-[11px] text-emerald-400/80 italic\">\n {annotation.resolution}\n </p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { createHttpAdapter } from \"./adapters/http\"\nimport type { Annotation, AnnotationStorageAdapter, AnnotationTarget } from \"./types\"\n\nexport function useAnnotations(adapter?: AnnotationStorageAdapter) {\n const adapterRef = useRef(adapter ?? createHttpAdapter())\n const [annotations, setAnnotations] = useState<Annotation[]>([])\n\n // Load on mount and subscribe to external updates\n useEffect(() => {\n adapterRef.current.load().then(setAnnotations)\n return adapterRef.current.subscribe?.(setAnnotations)\n }, [])\n\n const addAnnotation = useCallback(\n (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => {\n const annotation: Annotation = {\n id: crypto.randomUUID(),\n slideIndex,\n slideTitle,\n target,\n body,\n createdAt: new Date().toISOString(),\n status: \"open\"\n }\n setAnnotations(prev => [...prev, annotation])\n adapterRef.current.add(annotation)\n },\n []\n )\n\n const deleteAnnotation = useCallback((id: string) => {\n setAnnotations(prev => prev.filter(a => a.id !== id))\n adapterRef.current.remove(id)\n }, [])\n\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => annotations.filter(a => a.slideIndex === slideIndex),\n [annotations]\n )\n\n const openCount = useMemo(() => annotations.filter(a => a.status === \"open\").length, [annotations])\n\n // Allow external state updates (e.g. from postMessage adapter)\n const updateAnnotations = useCallback((updated: Annotation[]) => {\n setAnnotations(updated)\n }, [])\n\n return { annotations, addAnnotation, deleteAnnotation, getSlideAnnotations, openCount, updateAnnotations }\n}\n","import type { Annotation, AnnotationsFile, AnnotationStorageAdapter } from \"../types\"\n\nconst ENDPOINT = \"/__promptslide_annotations\"\n\nasync function loadAll(): Promise<Annotation[]> {\n try {\n const res = await fetch(ENDPOINT)\n if (!res.ok) return []\n const data: AnnotationsFile = await res.json()\n return data.annotations || []\n } catch {\n return []\n }\n}\n\nasync function saveAll(annotations: Annotation[]): Promise<void> {\n const data: AnnotationsFile = { version: 1, annotations }\n await fetch(ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data)\n })\n}\n\n/** Storage adapter for local Vite dev server (reads/writes annotations.json via middleware) */\nexport function createHttpAdapter(): AnnotationStorageAdapter {\n let cache: Annotation[] = []\n\n return {\n async load() {\n cache = await loadAll()\n return cache\n },\n async add(annotation) {\n cache = [...cache, annotation]\n await saveAll(cache)\n },\n async remove(id) {\n cache = cache.filter(a => a.id !== id)\n await saveAll(cache)\n }\n }\n}\n","import type { ClassValue } from \"clsx\"\n\nimport { clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":";;;;;AAUO,IAAM,4BAA4B;AAGlC,IAAM,iBAAiB;AAGvB,IAAM,0BAA0B;AAGhC,IAAM,gBAAgB;AAMtB,IAAM,eAAe;AACrB,IAAM,WAAW;AACjB,IAAM,UAAU;AAGhB,IAAM,aAAa,CAAC,KAAK,GAAG,KAAK,CAAC;AAOlC,IAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AACX;AAGO,IAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AACX;AAGO,IAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AACX;AAOO,IAAM,mBAAmB;AAAA,EAC9B,UAAU;AAAA,EACV,MAAM;AACR;AAGO,IAAM,mBAAmB;AAAA,EAC9B,UAAU;AAAA,EACV,MAAM;AACR;AAGO,IAAM,kBAAkB;AAAA,EAC7B,GAAG;AACL;AAOO,IAAM,iBAAiB;AAGvB,IAAM,yBAAyB;AAO/B,IAAM,mBAAmB;AAAA,EAC9B,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,aAAa,KAAK;AACpB;;;ACjGA,SAAS,eAAe,YAAY,eAAe;AAgD1C;AArCT,IAAM,mBAAmB,cAAqC;AAAA,EAC5D,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,mBAAmB;AACrB,CAAC;AAkBM,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AACtB,GAA2B;AACzB,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,aAAa,YAAY,iBAAiB;AAAA,EAC7C;AAEA,SAAO,oBAAC,iBAAiB,UAAjB,EAA0B,OAAe,UAAS;AAC5D;AAEO,SAAS,sBAAsB;AACpC,SAAO,WAAW,gBAAgB;AACpC;;;ACrDA,SAAS,cAAwB;AAoF7B,gBAAAA,YAAA;AA9DJ,IAAM,oBAAqD;AAAA,EACzD,MAAM;AAAA,IACJ,QAAQ,EAAE,SAAS,EAAE;AAAA,IACrB,SAAS,EAAE,SAAS,EAAE;AAAA,EACxB;AAAA,EACA,YAAY;AAAA,IACV,QAAQ,EAAE,SAAS,GAAG,GAAG,uBAAuB;AAAA,IAChD,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE;AAAA,EAC9B;AAAA,EACA,cAAc;AAAA,IACZ,QAAQ,EAAE,SAAS,GAAG,GAAG,CAAC,uBAAuB;AAAA,IACjD,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE;AAAA,EAC9B;AAAA,EACA,cAAc;AAAA,IACZ,QAAQ,EAAE,SAAS,GAAG,GAAG,uBAAuB;AAAA,IAChD,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE;AAAA,EAC9B;AAAA,EACA,eAAe;AAAA,IACb,QAAQ,EAAE,SAAS,GAAG,GAAG,CAAC,uBAAuB;AAAA,IACjD,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE;AAAA,EAC9B;AAAA,EACA,OAAO;AAAA,IACL,QAAQ,EAAE,SAAS,GAAG,OAAO,IAAI;AAAA,IACjC,SAAS,EAAE,SAAS,GAAG,OAAO,EAAE;AAAA,EAClC;AACF;AAuBO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAkB;AAChB,QAAM,EAAE,aAAa,kBAAkB,IAAI,oBAAoB;AAG/D,QAAM,YAAY,qBAAqB,eAAe;AAEtD,SACE,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY,YAAY;AAAA,MACjC,UAAU,kBAAkB,SAAS;AAAA,MACrC,YAAY;AAAA,QACV,GAAG;AAAA,QACH;AAAA,QACA,OAAO,YAAY,QAAQ;AAAA,MAC7B;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAiBO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,YAAY;AAAA,EACZ,eAAe;AAAA,EACf;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,EAAE,aAAa,kBAAkB,IAAI,oBAAoB;AAE/D,QAAM,aAAa,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAGjE,QAAM,YAAY,qBAAqB,eAAe;AAEtD,QAAM,oBAA8B;AAAA,IAClC,QAAQ,CAAC;AAAA,IACT,SAAS;AAAA,MACP,YAAY;AAAA,QACV,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SACE,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY,YAAY;AAAA,MACjC,UAAU;AAAA,MACV;AAAA,MAEC,qBAAW,IAAI,CAAC,OAAO,UACtB,gBAAAA,KAAC,OAAO,KAAP,EAAuB,UAAU,kBAAkB,SAAS,GAC1D,mBADc,KAEjB,CACD;AAAA;AAAA,EACH;AAEJ;;;AC7GA,IAAM,eAAyB;AAAA,EAC7B,OAAO,EAAE,SAAS,EAAE;AAAA,EACpB,QAAQ,EAAE,SAAS,EAAE;AAAA,EACrB,MAAM,EAAE,SAAS,EAAE;AACrB;AAEA,IAAM,oBAA8B;AAAA,EAClC,OAAO,EAAE,GAAG,gBAAgB,SAAS,EAAE;AAAA,EACvC,QAAQ,EAAE,GAAG,GAAG,SAAS,EAAE;AAAA,EAC3B,MAAM,EAAE,GAAG,CAAC,gBAAgB,SAAS,EAAE;AACzC;AAEA,IAAM,qBAA+B;AAAA,EACnC,OAAO,EAAE,GAAG,CAAC,gBAAgB,SAAS,EAAE;AAAA,EACxC,QAAQ,EAAE,GAAG,GAAG,SAAS,EAAE;AAAA,EAC3B,MAAM,EAAE,GAAG,gBAAgB,SAAS,EAAE;AACxC;AAEA,IAAM,kBAA4B;AAAA,EAChC,OAAO,EAAE,GAAG,gBAAgB,SAAS,EAAE;AAAA,EACvC,QAAQ,EAAE,GAAG,GAAG,SAAS,EAAE;AAAA,EAC3B,MAAM,EAAE,GAAG,CAAC,gBAAgB,SAAS,EAAE;AACzC;AAEA,IAAM,oBAA8B;AAAA,EAClC,OAAO,EAAE,GAAG,CAAC,gBAAgB,SAAS,EAAE;AAAA,EACxC,QAAQ,EAAE,GAAG,GAAG,SAAS,EAAE;AAAA,EAC3B,MAAM,EAAE,GAAG,gBAAgB,SAAS,EAAE;AACxC;AAEA,IAAM,eAAyB;AAAA,EAC7B,OAAO,EAAE,OAAO,KAAK,SAAS,EAAE;AAAA,EAChC,QAAQ,EAAE,OAAO,GAAG,SAAS,EAAE;AAAA,EAC/B,MAAM,EAAE,OAAO,KAAK,SAAS,EAAE;AACjC;AAEA,IAAM,mBAA6B;AAAA,EACjC,OAAO,EAAE,OAAO,MAAM,SAAS,EAAE;AAAA,EACjC,QAAQ,EAAE,OAAO,GAAG,SAAS,EAAE;AAAA,EAC/B,MAAM,EAAE,OAAO,MAAM,SAAS,EAAE;AAClC;AAEA,IAAM,gBAA0B;AAAA,EAC9B,OAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,MACV,SAAS,EAAE,OAAO,MAAM,UAAU,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,MACV,SAAS,EAAE,UAAU,gBAAgB,MAAM,WAAW;AAAA,IACxD;AAAA,EACF;AACF;AAEA,IAAM,eAAyB;AAAA,EAC7B,OAAO,CAAC;AAAA,EACR,QAAQ,CAAC;AAAA,EACT,MAAM,CAAC;AACT;AAMO,IAAM,iBAAwD;AAAA,EACnE,MAAM;AAAA,EACN,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,EACP,MAAM;AACR;AAMO,SAAS,0BAA0B,OAAkB,KAAsC;AAChG,SAAO,CAAC,eAAuB;AAAA,IAC7B,OAAO;AAAA,MACL,CAAC,IAAI,GAAG,YAAY,IAAI,iBAAiB,CAAC;AAAA,MAC1C,SAAS;AAAA,IACX;AAAA,IACA,QAAQ;AAAA,MACN,CAAC,IAAI,GAAG;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,MAAM;AAAA,MACJ,CAAC,IAAI,GAAG,YAAY,IAAI,iBAAiB,CAAC;AAAA,MAC1C,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAGO,IAAM,oBAAoB,0BAA0B,GAAG;AAGvD,IAAM,oBAAoB,0BAA0B,GAAG;AAMvD,SAAS,iBACd,QACA,YAAoB,GACV;AACV,QAAM,OAAO,OAAO,WAAW,WAAW,SAAS,OAAO;AAC1D,QAAM,cAAc,OAAO,WAAW,WAAW,OAAO,cAAc;AAEtE,MAAI,gBAAgB,SAAS,gBAAgB,SAAS,gBAAgB;AACpE,WAAO,kBAAkB,SAAS;AAAA,EACpC;AAEA,MAAI,gBAAgB,SAAS,cAAc,SAAS,eAAe;AACjE,WAAO,kBAAkB,SAAS;AAAA,EACpC;AAEA,SAAO,eAAe,IAAI;AAC5B;AAEO,SAAS,mBAAmB,QAGjC;AACA,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,OAAO,OAAO,WAAW,WAAW,SAAS,OAAO;AAE1D,QAAM,kBAAkB,SAAS,UAAU,iBAAiB;AAE5D,QAAM,WAAW,OAAO,WAAW,YAAY,OAAO,WAAW,OAAO,WAAW;AAEnF,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,EACF;AACF;AAMO,IAAM,2BAAgD;;;ACrM7D,SAAS,UAAAC,eAAc;AAiDnB,gBAAAC,YAAA;AAPG,SAAS,MAAM;AAAA,EACpB;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAe;AACb,SACE,gBAAAA;AAAA,IAACC,QAAO;AAAA,IAAP;AAAA,MACC,QAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAMO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAoB;AAClB,SACE,gBAAAD;AAAA,IAACC,QAAO;AAAA,IAAP;AAAA,MACC,QAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAcO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,WAAW,SAAS,GAAG,MAAM,IAAI,EAAE,KAAK;AAE9C,SACE,gBAAAD;AAAA,IAACC,QAAO;AAAA,IAAP;AAAA,MACC,QAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAcO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA,IAAI,YAAY;AAAA,EAChB,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,kBAAkBA,QAAO,SAAS;AAExC,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,QAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;ACxJA,SAAS,aAAa,WAAW,gBAAgB;AAwC1C,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,eAAe;AAAA,EACf;AACF,GAAwD;AACtD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,YAAY;AAC7D,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,CAAC;AAEpD,QAAM,CAAC,UAAU,WAAW,IAAI,SAA0B;AAAA,IACxD,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,CAAC;AAED,QAAM,CAAC,cAAc,eAAe,IAAI,SAAuB,IAAI;AAEnE,QAAM,aAAa,OAAO,YAAY,GAAG,SAAS;AAElD,QAAM,uBAAuB,YAAY,MAAM;AAC7C,gBAAY,UAAQ;AAClB,UAAI,KAAK,WAAW,iBAAiB;AACnC,eAAO,EAAE,QAAQ,QAAQ,WAAW,EAAE;AAAA,MACxC;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,YAAY,MAAM;AAChC,QAAI,SAAS,WAAW,iBAAiB;AACvC,sBAAgB,SAAS;AACzB;AAAA,IACF;AAEA,UAAM,oBAAoB,OAAO,YAAY,GAAG,SAAS;AAEzD,QAAI,iBAAiB,mBAAmB;AACtC,YAAM,aAAa,eAAe,KAAK,OAAO;AAC9C,kBAAY,EAAE,QAAQ,iBAAiB,WAAW,EAAE,CAAC;AACrD,uBAAiB,CAAC;AAClB,sBAAgB,SAAS;AACzB,sBAAgB,SAAS;AAAA,IAC3B,OAAO;AACL,uBAAiB,UAAQ,OAAO,CAAC;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,eAAe,cAAc,QAAQ,aAAa,CAAC;AAExE,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,SAAS,WAAW,iBAAiB;AACvC,sBAAgB,QAAQ;AACxB;AAAA,IACF;AAEA,QAAI,iBAAiB,GAAG;AACtB,YAAM,aAAa,eAAe,IAAI,OAAO,UAAU,OAAO;AAC9D,YAAM,iBAAiB,OAAO,SAAS,GAAG,SAAS;AACnD,kBAAY,EAAE,QAAQ,iBAAiB,WAAW,GAAG,CAAC;AACtD,uBAAiB,cAAc;AAC/B,sBAAgB,SAAS;AACzB,sBAAgB,SAAS;AAAA,IAC3B,OAAO;AACL,uBAAiB,UAAQ,OAAO,CAAC;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,eAAe,cAAc,QAAQ,aAAa,CAAC;AAExE,QAAM,YAAY;AAAA,IAChB,CAAC,UAAkB;AACjB,UAAI,QAAQ,KAAK,SAAS,OAAO,UAAU,UAAU,cAAc;AACjE;AAAA,MACF;AAEA,YAAM,YAAY,QAAQ,eAAe,IAAI;AAC7C,kBAAY,EAAE,QAAQ,iBAAiB,UAAU,CAAC;AAClD,uBAAiB,CAAC;AAClB,sBAAgB,KAAK;AACrB,sBAAgB,KAAK;AAAA,IACvB;AAAA,IACA,CAAC,cAAc,OAAO,QAAQ,aAAa;AAAA,EAC7C;AAEA,YAAU,MAAM;AACd,QAAI,SAAS,WAAW,UAAU,iBAAiB,MAAM;AACvD,sBAAgB,IAAI;AACpB,UAAI,iBAAiB,WAAW;AAC9B,gBAAQ;AAAA,MACV,WAAW,iBAAiB,UAAU;AACpC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,cAAc,SAAS,MAAM,CAAC;AAEnD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,SAAS;AAAA,IACpB,iBAAiB,SAAS,WAAW;AAAA,IACrC,mBAAmB,SAAS,cAAc,MAAM,SAAS,WAAW;AAAA,IACpE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC7IA,SAAS,iBAAAE,gBAAe,cAAAC,aAAY,WAAAC,gBAAe;AA0C7C,gBAAAC,YAAA;AAvCN,IAAM,eAAeH,eAAkC,IAAI;AAKpD,SAAS,WAA+B;AAC7C,SAAOC,YAAW,YAAY;AAChC;AAEA,SAAS,kBAAkB,OAAyC;AAClE,QAAM,QAAgC,CAAC;AAEvC,MAAI,MAAM,QAAQ,QAAS,OAAM,WAAW,IAAI,MAAM,OAAO;AAC7D,MAAI,MAAM,QAAQ,kBAAmB,OAAM,sBAAsB,IAAI,MAAM,OAAO;AAClF,MAAI,MAAM,QAAQ,UAAW,OAAM,aAAa,IAAI,MAAM,OAAO;AACjE,MAAI,MAAM,QAAQ,oBAAqB,OAAM,wBAAwB,IAAI,MAAM,OAAO;AACtF,MAAI,MAAM,QAAQ,OAAQ,OAAM,UAAU,IAAI,MAAM,OAAO;AAC3D,MAAI,MAAM,QAAQ,iBAAkB,OAAM,qBAAqB,IAAI,MAAM,OAAO;AAEhF,MAAI,MAAM,OAAO,QAAS,OAAM,gBAAgB,IAAI,MAAM,MAAM;AAChE,MAAI,MAAM,OAAO,KAAM,OAAM,aAAa,IAAI,MAAM,MAAM;AAE1D,SAAO;AACT;AAWO,SAAS,mBAAmB,EAAE,OAAO,SAAS,GAA4B;AAC/E,QAAM,eAAeC,SAAQ,MAAM,kBAAkB,KAAK,GAAG,CAAC,KAAK,CAAC;AAEpE,SACE,gBAAAC,KAAC,aAAa,UAAb,EAAsB,OAAO,OAC5B,0BAAAA,KAAC,SAAI,OAAO,cAAc,WAAU,YACjC,UACH,GACF;AAEJ;;;AC/CA,SAAS,iBAAiB,UAAAC,eAAc;;;ACAxC,OAAO,WAAW;AAsCJ,SAGJ,OAAAC,MAHI;AAzBP,IAAM,qBAAN,cAAiC,MAAM,UAG5C;AAAA,EAHK;AAAA;AAIL,iCAAiC,EAAE,UAAU,OAAO,OAAO,KAAK;AAAA;AAAA,EAEhE,OAAO,yBAAyB,OAAc;AAC5C,WAAO,EAAE,UAAU,MAAM,MAAM;AAAA,EACjC;AAAA,EAEA,kBAAkB,OAAc,WAA4B;AAC1D,YAAQ;AAAA,MACN,uBAAuB,KAAK,MAAM,aAAa,CAAC,GAAG,KAAK,MAAM,aAAa,MAAM,KAAK,MAAM,UAAU,OAAO,EAAE;AAAA,MAC/G;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS;AACP,QAAI,KAAK,MAAM,UAAU;AACvB,aACE,qBAAC,SAAI,WAAU,8EACb;AAAA,6BAAC,SAAI,WAAU,2CAA0C;AAAA;AAAA,UAChD,KAAK,MAAM,aAAa;AAAA,UAAE;AAAA,UAChC,KAAK,MAAM,cACV,qBAAC,UAAK,WAAU,oCAAmC;AAAA;AAAA,YAAE,KAAK,MAAM;AAAA,YAAW;AAAA,aAAC;AAAA,WAEhF;AAAA,QACA,gBAAAA,KAAC,SAAI,WAAU,uEACZ,eAAK,MAAM,OAAO,WAAW,iBAChC;AAAA,SACF;AAAA,IAEJ;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;;;ADgCY,gBAAAC,YAAA;AA/CL,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,yBAAyB,OAAO,YAAY,GAAG;AACrD,QAAM,iBAAiB,0BAA0B,cAAc;AAC/D,QAAM,gBAAgB,yBAAyB;AAE/C,QAAM,gBAAgB;AAAA,IACpB,EAAE,MAAM,gBAAgB,aAAa,cAAc;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,wBAAwB,OAAO,YAAY,EAAG;AAEpD,SACE,gBAAAA,KAAC,mBAAgB,SAAS,OACxB,0BAAAA;AAAA,IAACC,QAAO;AAAA,IAAP;AAAA,MAEC,UAAU;AAAA,MACV,SAAQ;AAAA,MACR,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,YAAY;AAAA,MACZ,qBAAqB,gBAAc;AACjC,YAAI,eAAe,UAAU;AAC3B,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,MACA,WAAU;AAAA,MAEV,0BAAAD;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb;AAAA,UACA;AAAA,UAEA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,YAAY;AAAA,cACZ,YAAY,OAAO,YAAY,GAAG;AAAA,cAElC,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAa,eAAe;AAAA,kBAC5B,aAAa,OAAO;AAAA;AAAA,cACtB;AAAA;AAAA,UACF;AAAA;AAAA,MACF;AAAA;AAAA,IA3BK;AAAA,EA4BP,GACF;AAEJ;;;AE1FA,SAAS,eAAAE,cAAa,aAAAC,YAAW,YAAAC,iBAAgB;AAgIzC,gBAAAC,YAAA;AA3FD,SAAS,WAAW,EAAE,QAAQ,YAAY,sBAAsB,GAAoB;AACzF,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,CAAC;AAEpC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,mBAAmB,EAAE,OAAO,CAAC;AAGjC,EAAAC,WAAU,MAAM;AACd,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,aAAa,OAAO;AAAA,MACpB;AAAA,MACA;AAAA,MACA,QAAQ,OAAO,IAAI,OAAK,EAAE,SAAS,EAAE;AAAA,IACvC;AACA,WAAO,OAAO,YAAY,EAAE,MAAM,cAAc,MAAM,MAAM,GAAG,GAAG;AAAA,EACpE,GAAG,CAAC,cAAc,eAAe,YAAY,MAAM,CAAC;AAGpD,EAAAA,WAAU,MAAM;AACd,WAAO,OAAO,YAAY,EAAE,MAAM,aAAa,GAAG,GAAG;AAAA,EACvD,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AAEd,UAAM,MAAO,YAAoB;AACjC,QAAI,KAAK;AACP,UAAI,GAAG,oBAAoB,MAAM;AAC/B,eAAO,OAAO,YAAY,EAAE,MAAM,YAAY,GAAG,GAAG;AAAA,MACtD,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM,gBAAgBC;AAAA,IACpB,CAAC,UAAwB;AACvB,YAAM,EAAE,MAAM,KAAK,IAAI,MAAM,QAAQ,CAAC;AACtC,cAAQ,MAAM;AAAA,QACZ,KAAK;AACH,cAAI,OAAO,MAAM,UAAU,SAAU,WAAU,KAAK,KAAK;AACzD;AAAA,QACF,KAAK;AACH,kBAAQ;AACR;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,SAAS,QAAQ,SAAS;AAAA,EAC7B;AAEA,EAAAD,WAAU,MAAM;AACd,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EAClE,GAAG,CAAC,aAAa,CAAC;AAGlB,EAAAA,WAAU,MAAM;AACd,UAAM,iBAAiB,MAAM;AAC3B,YAAM,SAAS,OAAO,aAAa,iBAAiB;AACpD,YAAM,SAAS,OAAO,cAAc,iBAAiB;AACrD,eAAS,KAAK,IAAI,QAAQ,MAAM,CAAC;AAAA,IACnC;AAEA,mBAAe;AACf,WAAO,iBAAiB,UAAU,cAAc;AAChD,WAAO,MAAM,OAAO,oBAAoB,UAAU,cAAc;AAAA,EAClE,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAF,KAAC,SAAI,WAAU,8CACb,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,OAAO,iBAAiB;AAAA,QACxB,QAAQ,iBAAiB;AAAA,QACzB,WAAW,+BAA+B,KAAK;AAAA,QAC/C,iBAAiB;AAAA,MACnB;AAAA,MAEA,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;;;AC9IA,SAAS,mBAAmB;AAC5B,SAAS,aAAa,cAAc,UAAU,SAAS,MAAM,UAAU,iBAAAI,gBAAe,eAAe;AACrG,SAAS,eAAAC,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACFlE,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACAzD,SAAS,aAAAC,YAAW,QAAQ,YAAAC,iBAAgB;AAC5C,SAAS,SAAS,SAAS;AAgDrB,SACE,OAAAC,MADF,QAAAC,aAAA;AAvCC,SAAS,eAAe,EAAE,UAAU,UAAU,UAAU,SAAS,GAAwB;AAC9F,QAAM,CAAC,MAAM,OAAO,IAAIF,UAAS,EAAE;AACnC,QAAM,cAAc,OAA4B,IAAI;AAEpD,EAAAD,WAAU,MAAM;AACd,gBAAY,SAAS,MAAM;AAAA,EAC7B,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,MAAM;AACzB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AACd,aAAS,OAAO;AAAA,EAClB;AAEA,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,QAAE,eAAe;AACjB,mBAAa;AAAA,IACf;AACA,QAAI,EAAE,QAAQ,UAAU;AACtB,eAAS;AAAA,IACX;AACA,MAAE,gBAAgB;AAAA,EACpB;AAGA,QAAM,OAAO,WAAW,KAAK,SAAY,GAAG,QAAQ;AACpD,QAAM,QAAQ,WAAW,KAAK,GAAG,MAAM,QAAQ,MAAM;AACrD,QAAM,MAAM,WAAW,KAAK,SAAY,GAAG,QAAQ;AACnD,QAAM,SAAS,WAAW,KAAK,GAAG,MAAM,QAAQ,MAAM;AAEtD,SACE,gBAAAG;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO,EAAE,MAAM,OAAO,KAAK,OAAO;AAAA,MAClC,SAAS,OAAK,EAAE,gBAAgB;AAAA,MAChC,WAAW,OAAK,EAAE,gBAAgB;AAAA,MAElC;AAAA,wBAAAA,MAAC,SAAI,WAAU,gFACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,sDAAqD,4BAAc;AAAA,UACnF,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cAEV,0BAAAA,KAAC,KAAE,WAAU,eAAc;AAAA;AAAA,UAC7B;AAAA,WACF;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,OACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,OAAK,QAAQ,EAAE,OAAO,KAAK;AAAA,cACrC,WAAW;AAAA,cACX,aAAY;AAAA,cACZ,WAAU;AAAA,cACV,MAAM;AAAA;AAAA,UACR;AAAA,UACA,gBAAAC,MAAC,SAAI,WAAU,4CACb;AAAA,4BAAAD,KAAC,UAAK,WAAU,gCAA+B,2BAAa;AAAA,YAC5D,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,UAAU,CAAC,KAAK,KAAK;AAAA,gBACrB,WAAU;AAAA,gBAEV,0BAAAA,KAAC,WAAQ,WAAU,eAAc,aAAa,KAAK;AAAA;AAAA,YACrD;AAAA,aACF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACpEI,SAWI,OAAAE,MAXJ,QAAAC,aAAA;AAJG,SAAS,cAAc,EAAE,QAAQ,QAAQ,UAAU,UAAU,YAAY,QAAQ,GAAuB;AAC7G,QAAM,aAAa,WAAW;AAE9B,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,OAAK;AACZ,UAAE,gBAAgB;AAClB,gBAAQ;AAAA,MACV;AAAA,MACA,WAAU;AAAA,MACV,OAAO,EAAE,MAAM,GAAG,QAAQ,KAAK,KAAK,GAAG,QAAQ,IAAI;AAAA,MACnD,OAAO,eAAe,MAAM;AAAA,MAG3B;AAAA,sBAAc,CAAC,cACd,gBAAAD,KAAC,SAAI,WAAU,4EAA2E;AAAA,QAE5F,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,mJACT,aACI,aACE,4DACA,wFACF,aACE,uCACA,+CACR;AAAA,YAEC;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACpCO,SAAS,mBACd,SACA,WACkB;AAClB,QAAM,OAAO,QAAQ,sBAAsB;AAC3C,QAAM,WAAW,UAAU,sBAAsB;AAEjD,QAAM,YAAa,KAAK,OAAO,KAAK,QAAQ,IAAI,SAAS,QAAQ,SAAS,QAAS;AACnF,QAAM,YAAa,KAAK,MAAM,KAAK,SAAS,IAAI,SAAS,OAAO,SAAS,SAAU;AAEnF,SAAO;AAAA,IACL,cAAc,QAAQ,aAAa,eAAe,KAAK;AAAA,IACvD,gBAAgB,mBAAmB,OAAO;AAAA,IAC1C,UAAU,EAAE,UAAU,SAAS;AAAA,EACjC;AACF;AAKO,SAAS,cACd,QACA,WACyF;AAEzF,MAAI,OAAO,cAAc;AACvB,UAAM,KAAK,UAAU,cAA2B,mBAAmB,OAAO,YAAY,IAAI;AAC1F,QAAI,GAAI,QAAO,EAAE,SAAS,IAAI,QAAQ,eAAe;AAAA,EACvD;AAGA,MAAI,OAAO,gBAAgB;AACzB,UAAM,QAAQ,kBAAkB,WAAW,OAAO,cAAc;AAChE,QAAI,MAAO,QAAO,EAAE,SAAS,OAAO,QAAQ,iBAAiB;AAAA,EAC/D;AAGA,SAAO,EAAE,SAAS,MAAM,QAAQ,WAAW;AAC7C;AAKA,SAAS,mBAAmB,SAA0C;AACpE,QAAM,OAAO,QAAQ,aAAa,KAAK;AACvC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,MAAM,GAAG,GAAG;AAC1B;AAKA,SAAS,kBAAkB,MAAmB,MAAkC;AAC9E,QAAM,SAAS,SAAS,iBAAiB,MAAM,WAAW,YAAY;AACtE,MAAI,OAA2B;AAC/B,MAAI,aAAa;AAEjB,SAAO,OAAO,SAAS,GAAG;AACxB,UAAM,KAAK,OAAO;AAClB,UAAM,SAAS,GAAG,aAAa,KAAK;AACpC,QAAI,CAAC,OAAQ;AAEb,UAAM,gBAAgB,OAAO,MAAM,GAAG,GAAG;AACzC,QAAI,kBAAkB,QAAQ,OAAO,SAAS,YAAY;AACxD,aAAO;AACP,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;;;AHkEI,mBAGI,OAAAE,OAHJ,QAAAC,aAAA;AApHG,SAAS,kBAAkB,EAAE,QAAQ,cAAc,mBAAmB,YAAY,YAAY,aAAa,kBAAkB,cAAc,GAA2B;AAC3K,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAmC,IAAI;AACrE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAyB,IAAI;AACzE,QAAM,aAAaC,QAAuB,IAAI;AAE9C,QAAM,aAAa,OAAO,YAAY,GAAG,SAAS,SAAS,eAAe,CAAC;AAG3E,QAAM,sBAAsB,iBAAiB,IAAI,CAAC,GAAG,MAAM;AACzD,UAAM,YAAY,kBAAkB;AACpC,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,YAAY,GAAG,UAAU,EAAE,OAAO,SAAS,UAAU,UAAU,EAAE,OAAO,SAAS,UAAU,QAAQ,IAAI,EAAE;AAAA,IACpH;AAEA,UAAM,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,SAAS;AACrD,QAAI,SAAS;AACX,YAAM,OAAO,QAAQ,sBAAsB;AAC3C,YAAM,gBAAgB,UAAU,sBAAsB;AACtD,YAAM,YAAa,KAAK,OAAO,KAAK,QAAQ,IAAI,cAAc,QAAQ,cAAc,QAAS;AAC7F,YAAM,YAAa,KAAK,MAAM,KAAK,SAAS,IAAI,cAAc,OAAO,cAAc,SAAU;AAC7F,aAAO,EAAE,YAAY,GAAG,UAAU,UAAU,QAAQ,IAAI,EAAE;AAAA,IAC5D;AAEA,WAAO,EAAE,YAAY,GAAG,UAAU,EAAE,OAAO,SAAS,UAAU,UAAU,EAAE,OAAO,SAAS,UAAU,QAAQ,IAAI,EAAE;AAAA,EACpH,CAAC;AAED,QAAM,qBAAqBC;AAAA,IACzB,CAAC,MAAwB;AACvB,YAAM,YAAY,kBAAkB;AACpC,UAAI,CAAC,UAAW;AAGhB,YAAM,UAAU,WAAW;AAC3B,UAAI,QAAS,SAAQ,MAAM,gBAAgB;AAC3C,YAAM,eAAe,SAAS,iBAAiB,EAAE,SAAS,EAAE,OAAO;AACnE,UAAI,QAAS,SAAQ,MAAM,gBAAgB;AAE3C,UAAI,CAAC,gBAAgB,CAAC,UAAU,SAAS,YAAY,GAAG;AACtD,mBAAW,IAAI;AACf,mBAAW,IAAI;AACf;AAAA,MACF;AAGA,UAAI,SAAS;AACb,UAAI,OAAO,YAAY,UAAU,OAAO,iBAAiB,UAAU,SAAS,OAAO,aAAa,GAAG;AAEjG,cAAM,SAAS,OAAO;AACtB,YAAI,OAAO,YAAY,SAAS,OAAO,SAAS,UAAU,GAAG;AAC3D,mBAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,mBAAmB,mBAAmB,QAAQ,SAAS;AAC7D,YAAM,gBAAgB,UAAU,sBAAsB;AACtD,YAAM,YAAa,EAAE,UAAU,cAAc,QAAQ,cAAc,QAAS;AAC5E,YAAM,YAAa,EAAE,UAAU,cAAc,OAAO,cAAc,SAAU;AAE5E,iBAAW,EAAE,QAAQ,kBAAkB,UAAU,SAAS,CAAC;AAC3D,iBAAW,IAAI;AAAA,IACjB;AAAA,IACA,CAAC,mBAAmB,UAAU;AAAA,EAChC;AAEA,QAAM,eAAeA;AAAA,IACnB,CAAC,SAAiB;AAChB,UAAI,CAAC,QAAS;AACd,oBAAc,cAAc,YAAY,QAAQ,QAAQ,IAAI;AAC5D,iBAAW,IAAI;AACf,kBAAY;AAAA,IACd;AAAA,IACA,CAAC,SAAS,cAAc,YAAY,eAAe,WAAW;AAAA,EAChE;AAGA,QAAM,kBAAkBA;AAAA,IACtB,CAAC,MAAwB;AACvB,UAAI,SAAS;AACX,0BAAkB,IAAI;AACtB;AAAA,MACF;AAEA,YAAM,YAAY,kBAAkB;AACpC,UAAI,CAAC,UAAW;AAEhB,YAAM,UAAU,WAAW;AAC3B,UAAI,QAAS,SAAQ,MAAM,gBAAgB;AAC3C,YAAM,eAAe,SAAS,iBAAiB,EAAE,SAAS,EAAE,OAAO;AACnE,UAAI,QAAS,SAAQ,MAAM,gBAAgB;AAE3C,UAAI,gBAAgB,UAAU,SAAS,YAAY,KAAK,iBAAiB,WAAW;AAClF,cAAM,gBAAgB,UAAU,sBAAsB;AACtD,cAAM,SAAS,aAAa,sBAAsB;AAClD;AAAA,UACE,IAAI;AAAA,YACF,OAAO,OAAO,cAAc;AAAA,YAC5B,OAAO,MAAM,cAAc;AAAA,YAC3B,OAAO;AAAA,YACP,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,OAAO;AACL,0BAAkB,IAAI;AAAA,MACxB;AAAA,IACF;AAAA,IACA,CAAC,mBAAmB,OAAO;AAAA,EAC7B;AAGA,EAAAC,WAAU,MAAM;AACd,eAAW,IAAI;AACf,eAAW,IAAI;AACf,sBAAkB,IAAI;AAAA,EACxB,GAAG,CAAC,cAAc,UAAU,CAAC;AAE7B,SACE,gBAAAJ,MAAA,YAEG;AAAA,sBACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,MAAM,eAAe;AAAA,UACrB,KAAK,eAAe;AAAA,UACpB,OAAO,eAAe;AAAA,UACtB,QAAQ,eAAe;AAAA,QACzB;AAAA;AAAA,IACF;AAAA,IAIF,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,UAAU;AAAA,QACV,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,gRAAgR;AAAA,QACjS,SAAS;AAAA,QACT,WAAW,OAAK;AAAE,cAAI,EAAE,QAAQ,UAAU;AAAE,uBAAW,IAAI;AAAG,uBAAW,IAAI;AAAA,UAAE;AAAA,QAAE;AAAA,QACjF,aAAa;AAAA,QACb,cAAc,MAAM,kBAAkB,IAAI;AAAA;AAAA,IAC5C;AAAA,IAGC,oBAAoB,IAAI,CAAC,EAAE,YAAY,UAAU,UAAU,OAAO,MACjE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,QAAQ,WAAW;AAAA,QACnB;AAAA,QACA;AAAA,QACA,YAAY,WAAW,OAAO;AAAA,QAC9B,SAAS,MAAM;AACb,qBAAW,WAAW,OAAO,aAAa,OAAO,WAAW,EAAE;AAC9D,sBAAY;AACZ,qBAAW,IAAI;AAAA,QACjB;AAAA;AAAA,MAVK,WAAW;AAAA,IAWlB,CACD;AAAA,IAGA,WACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,QAAQ;AAAA,QAClB,UAAU,QAAQ;AAAA,QAClB,UAAU;AAAA,QACV,UAAU,MAAM,WAAW,IAAI;AAAA;AAAA,IACjC;AAAA,KAGJ;AAEJ;;;AIrMA,SAAS,eAAe,QAAQ,KAAAM,UAAS;AAkBjC,SAkEA,YAAAC,WAhEI,OAAAC,OAFJ,QAAAC,aAAA;AAPD,SAAS,gBAAgB,EAAE,aAAa,YAAY,UAAU,UAAU,QAAQ,GAAyB;AAC9G,QAAM,OAAO,YAAY,OAAO,OAAK,EAAE,WAAW,MAAM;AACxD,QAAM,WAAW,YAAY,OAAO,OAAK,EAAE,WAAW,UAAU;AAEhE,SACE,gBAAAA,MAAC,SAAI,WAAU,4GACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,iDACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,6BACb;AAAA,wBAAAD,MAAC,SAAI,WAAU,uEACb,0BAAAA,MAAC,iBAAc,WAAU,8BAA6B,GACxD;AAAA,QACA,gBAAAA,MAAC,UAAK,WAAU,kCAAiC,yBAAW;AAAA,QAC3D,KAAK,SAAS,KACb,gBAAAA,MAAC,UAAK,WAAU,6HACb,eAAK,QACR;AAAA,SAEJ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAEV,0BAAAA,MAACF,IAAA,EAAE,WAAU,WAAU;AAAA;AAAA,MACzB;AAAA,OACF;AAAA,IAEA,gBAAAE,MAAC,SAAI,WAAU,0EAAyE;AAAA,IAExF,gBAAAC,MAAC,SAAI,WAAU,8BACZ;AAAA,kBAAY,WAAW,KACtB,gBAAAD,MAAC,SAAI,WAAU,iCACb,0BAAAA,MAAC,gBAAa,GAChB;AAAA,MAGD,KAAK,SAAS,KACb,gBAAAC,MAAC,SACC;AAAA,wBAAAD,MAAC,SAAI,WAAU,sFAAqF,kBAEpG;AAAA,QACC,KAAK,IAAI,CAAC,GAAG,MACZ,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,YAAY;AAAA,YACZ,QAAQ,IAAI;AAAA,YACZ,YAAY,EAAE,OAAO;AAAA,YACrB,UAAU,MAAM,SAAS,EAAE,EAAE;AAAA,YAC7B,UAAU,MAAM,SAAS,EAAE,EAAE;AAAA;AAAA,UALxB,EAAE;AAAA,QAMT,CACD;AAAA,SACH;AAAA,MAGD,SAAS,SAAS,KACjB,gBAAAC,MAAC,SAAI,WAAW,KAAK,SAAS,IAAI,SAAS,IACzC;AAAA,wBAAAD,MAAC,SAAI,WAAU,sFAAqF,sBAEpG;AAAA,QACC,SAAS,IAAI,CAAC,GAAG,MAChB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,YAAY;AAAA,YACZ,QAAQ,KAAK,SAAS,IAAI;AAAA,YAC1B,YAAY,EAAE,OAAO;AAAA,YACrB,UAAU,MAAM,SAAS,EAAE,EAAE;AAAA,YAC7B,UAAU,MAAM,SAAS,EAAE,EAAE;AAAA;AAAA,UALxB,EAAE;AAAA,QAMT,CACD;AAAA,SACH;AAAA,OAEJ;AAAA,IAEC,YAAY,SAAS,KACpB,gBAAAC,MAAAF,WAAA,EACE;AAAA,sBAAAC,MAAC,SAAI,WAAU,0EAAyE;AAAA,MACxF,gBAAAA,MAAC,SAAI,WAAU,aACb,0BAAAA,MAAC,gBAAa,GAChB;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE,gBAAAC,MAAC,SAAI,WAAU,uBACb;AAAA,oBAAAD,MAAC,OAAE,WAAU,qEAAoE,sBAAQ;AAAA,IACzF,gBAAAC,MAAC,SAAI,WAAU,4BACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,uIAAsI,eAAC;AAAA,MACtJ,gBAAAA,MAAC,OAAE,WAAU,6CAA4C,iDAAmC;AAAA,OAC9F;AAAA,IACA,gBAAAC,MAAC,SAAI,WAAU,4BACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,uIAAsI,eAAC;AAAA,MACtJ,gBAAAA,MAAC,OAAE,WAAU,6CAA4C,yCAA2B;AAAA,OACtF;AAAA,IACA,gBAAAC,MAAC,SAAI,WAAU,4BACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,uIAAsI,eAAC;AAAA,MACtJ,gBAAAA,MAAC,OAAE,WAAU,6CAA4C,oCAAsB;AAAA,OACjF;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,OAAK;AAAE,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK,UAAS;AAAA,MAAE;AAAA,MACrE,WAAW,qFACT,aACI,6CACA,uBACN;AAAA,MAEA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,OAAK;AACZ,gBAAE,gBAAgB;AAClB,uBAAS;AAAA,YACX;AAAA,YACA,WAAU;AAAA,YAEV,0BAAAA,MAAC,UAAO,WAAU,WAAU;AAAA;AAAA,QAC9B;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,4BACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,wGACT,WAAW,WAAW,SAClB,4BACA,iCACN;AAAA,cAEC;AAAA;AAAA,UACH;AAAA,UACA,gBAAAC,MAAC,SAAI,WAAU,gBACb;AAAA,4BAAAD,MAAC,OAAE,WAAU,gDAAgD,qBAAW,MAAK;AAAA,YAC5E,WAAW,OAAO,kBACjB,gBAAAA,MAAC,OAAE,WAAU,8CACV,qBAAW,OAAO,gBACrB;AAAA,YAED,WAAW,cACV,gBAAAA,MAAC,OAAE,WAAU,iDACV,qBAAW,YACd;AAAA,aAEJ;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;AC/KA,SAAS,eAAAE,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACElE,IAAM,WAAW;AAEjB,eAAe,UAAiC;AAC9C,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,QAAQ;AAChC,QAAI,CAAC,IAAI,GAAI,QAAO,CAAC;AACrB,UAAM,OAAwB,MAAM,IAAI,KAAK;AAC7C,WAAO,KAAK,eAAe,CAAC;AAAA,EAC9B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,QAAQ,aAA0C;AAC/D,QAAM,OAAwB,EAAE,SAAS,GAAG,YAAY;AACxD,QAAM,MAAM,UAAU;AAAA,IACpB,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AACH;AAGO,SAAS,oBAA8C;AAC5D,MAAI,QAAsB,CAAC;AAE3B,SAAO;AAAA,IACL,MAAM,OAAO;AACX,cAAQ,MAAM,QAAQ;AACtB,aAAO;AAAA,IACT;AAAA,IACA,MAAM,IAAI,YAAY;AACpB,cAAQ,CAAC,GAAG,OAAO,UAAU;AAC7B,YAAM,QAAQ,KAAK;AAAA,IACrB;AAAA,IACA,MAAM,OAAO,IAAI;AACf,cAAQ,MAAM,OAAO,OAAK,EAAE,OAAO,EAAE;AACrC,YAAM,QAAQ,KAAK;AAAA,IACrB;AAAA,EACF;AACF;;;ADtCO,SAAS,eAAe,SAAoC;AACjE,QAAM,aAAaC,QAAO,WAAW,kBAAkB,CAAC;AACxD,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAuB,CAAC,CAAC;AAG/D,EAAAC,WAAU,MAAM;AACd,eAAW,QAAQ,KAAK,EAAE,KAAK,cAAc;AAC7C,WAAO,WAAW,QAAQ,YAAY,cAAc;AAAA,EACtD,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBC;AAAA,IACpB,CAAC,YAAoB,YAAoB,QAA0B,SAAiB;AAClF,YAAM,aAAyB;AAAA,QAC7B,IAAI,OAAO,WAAW;AAAA,QACtB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,QAAQ;AAAA,MACV;AACA,qBAAe,UAAQ,CAAC,GAAG,MAAM,UAAU,CAAC;AAC5C,iBAAW,QAAQ,IAAI,UAAU;AAAA,IACnC;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmBA,aAAY,CAAC,OAAe;AACnD,mBAAe,UAAQ,KAAK,OAAO,OAAK,EAAE,OAAO,EAAE,CAAC;AACpD,eAAW,QAAQ,OAAO,EAAE;AAAA,EAC9B,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsBA;AAAA,IAC1B,CAAC,eAAuB,YAAY,OAAO,OAAK,EAAE,eAAe,UAAU;AAAA,IAC3E,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,YAAYC,SAAQ,MAAM,YAAY,OAAO,OAAK,EAAE,WAAW,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC;AAGlG,QAAM,oBAAoBD,aAAY,CAAC,YAA0B;AAC/D,mBAAe,OAAO;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,aAAa,eAAe,kBAAkB,qBAAqB,WAAW,kBAAkB;AAC3G;;;AE/CA,SAAS,YAAY;AACrB,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;AR0DU,gBAAAE,OA4MF,QAAAC,aA5ME;AA3BV,SAAS,gBAAgB,EAAE,QAAQ,WAAW,GAAkD;AAC9F,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,KAAK;AACxC,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,OAAO,SAAS,CAAC,CAAC;AACxE,QAAM,cAAc,OAAO,YAAY;AACvC,QAAM,iBAAiB,YAAY;AAEnC,EAAAC,WAAU,MAAM;AACd,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,qBAAmB,QAAQ,SAAS;AAAA,MACpC,OAAO;AAAA,QACL,OAAO,iBAAiB;AAAA,QACxB,QAAQ,iBAAiB;AAAA,QACzB,UAAU;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,MACd;AAAA,MAEA,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAa,YAAY;AAAA,UACzB,YAAY,YAAY;AAAA,UACxB,mBAAmB;AAAA,UAEnB,0BAAAA,MAAC,sBAAmB,YAAY,cAAc,YAAY,YAAY,OACpE,0BAAAA,MAAC,kBAAe,aAAa,eAAe,GAAG,aAAa,OAAO,QAAQ,GAC7E;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAMO,SAAS,UAAU,EAAE,QAAQ,YAAY,uBAAuB,aAAa,iBAAiB,mBAAmB,GAAmB;AAEzI,QAAM,CAAC,YAAY,IAAIE,UAAS,MAAM;AACpC,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,QAAI,OAAO,IAAI,QAAQ,MAAM,OAAQ,QAAO;AAC5C,WAAO,EAAE,YAAY,SAAS,OAAO,IAAI,OAAO,KAAK,KAAK,EAAE,EAAE;AAAA,EAChE,CAAC;AAED,MAAI,cAAc;AAChB,WAAO,gBAAAF,MAAC,mBAAgB,QAAgB,YAAY,aAAa,YAAY;AAAA,EAC/E;AAGA,QAAM,WAAW,eAAe;AAChC,QAAM,sBAAsB,gBAAgB;AAC5C,QAAM,uBAAuB,sBAAsB,cAAc,SAAS;AAC1E,QAAM,eAAe,sBAAsB,kBAAkB,SAAS;AACtE,QAAM,kBAAkB,sBAAsB,qBAAqB,SAAS;AAE5E,QAAM,YAAYI,SAAQ,MAAM,qBAAqB,OAAO,OAAK,EAAE,WAAW,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC;AACpH,QAAM,sBAAsBC;AAAA,IAC1B,CAAC,eAAuB,qBAAqB,OAAO,OAAK,EAAE,eAAe,UAAU;AAAA,IACpF,CAAC,oBAAoB;AAAA,EACvB;AAEA,QAAM,CAAC,UAAU,WAAW,IAAIH,UAAmB,OAAO;AAC1D,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,UAAS,KAAK;AAClE,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,KAAK;AAC9D,QAAM,CAAC,qBAAqB,sBAAsB,IAAIA,UAAS,KAAK;AACpE,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAwB,IAAI;AACpF,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,CAAC;AACpC,QAAM,eAAeI,QAAuB,IAAI;AAChD,QAAM,oBAAoBA,QAAuB,IAAI;AAErD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,mBAAmB;AAAA,IACrB;AAAA,EACF,CAAC;AAED,QAAM,yBAAyBD,aAAY,YAAY;AACrD,QAAI,CAAC,SAAS,mBAAmB;AAC/B,YAAM,SAAS,gBAAgB,kBAAkB;AAAA,IACnD,OAAO;AACL,YAAM,SAAS,eAAe;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,EAAAF,WAAU,MAAM;AACd,UAAM,yBAAyB,MAAM;AACnC,4BAAsB,CAAC,CAAC,SAAS,iBAAiB;AAAA,IACpD;AACA,aAAS,iBAAiB,oBAAoB,sBAAsB;AACpE,WAAO,MAAM,SAAS,oBAAoB,oBAAoB,sBAAsB;AAAA,EACtF,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AACd,UAAM,iBAAiB,MAAM;AAC3B,UAAI,CAAC,oBAAoB;AACvB,iBAAS,CAAC;AACV;AAAA,MACF;AACA,YAAM,gBAAgB,OAAO;AAC7B,YAAM,iBAAiB,OAAO;AAC9B,YAAM,SAAS,gBAAgB,iBAAiB;AAChD,YAAM,SAAS,iBAAiB,iBAAiB;AACjD,eAAS,KAAK,IAAI,QAAQ,MAAM,CAAC;AAAA,IACnC;AAEA,mBAAe;AACf,WAAO,iBAAiB,UAAU,cAAc;AAChD,WAAO,MAAM,OAAO,oBAAoB,UAAU,cAAc;AAAA,EAClE,GAAG,CAAC,kBAAkB,CAAC;AAEvB,QAAM,kBAAkB,MAAM;AAC5B,UAAM,eAAe;AACrB,gBAAY,MAAM;AAElB,eAAW,MAAM;AACf,YAAM,mBAAmB,MAAM;AAC7B,oBAAY,YAAY;AACxB,eAAO,oBAAoB,cAAc,gBAAgB;AAAA,MAC3D;AACA,aAAO,iBAAiB,cAAc,gBAAgB;AACtD,aAAO,MAAM;AAAA,IACf,GAAG,GAAG;AAAA,EACR;AAGA,EAAAA,WAAU,MAAM;AACd,UAAM,gBAAgB,CAAC,MAAqB;AAC1C,UAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,KAAK;AAClC,+BAAuB;AACvB;AAAA,MACF;AAGA,UAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,KAAK;AAClC,oBAAY,UAAS,SAAS,SAAS,UAAU,MAAO;AACxD;AAAA,MACF;AAEA,UAAI,aAAa,WAAY,oBAAoB,CAAC,mBAAqB;AAEvE,UAAI,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,KAAK;AAC3C,UAAE,eAAe;AACjB,gBAAQ;AAAA,MACV,WAAW,EAAE,QAAQ,aAAa;AAChC,UAAE,eAAe;AACjB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EAClE,GAAG,CAAC,SAAS,QAAQ,UAAU,wBAAwB,kBAAkB,kBAAkB,CAAC;AAE5F,SACE,gBAAAF,MAAC,SAAI,WAAU,sDACb;AAAA,oBAAAD,MAAC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAoBN;AAAA,IAGF,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,sBAAsB;AAAA,UACtB,oBAAoB,sBAAsB,oBAAoB;AAAA,QAChE;AAAA,QAEA;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,OAAO;AAAA,cAClC,WAAW;AAAA,gBACT;AAAA,gBACA,aAAa,WAAW;AAAA,cAC1B;AAAA,cACA,OAAM;AAAA,cAEN,0BAAAA,MAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,UAC/B;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,MAAM;AAAA,cACjC,WAAW;AAAA,gBACT;AAAA,gBACA,aAAa,UAAU;AAAA,cACzB;AAAA,cACA,OAAM;AAAA,cAEN,0BAAAA,MAAC,QAAK,WAAU,WAAU;AAAA;AAAA,UAC5B;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,MAAM;AAAA,cACjC,WAAW;AAAA,gBACT;AAAA,gBACA,aAAa,UAAU;AAAA,cACzB;AAAA,cACA,OAAM;AAAA,cAEN,0BAAAA,MAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,UAC/B;AAAA,UAEA,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,UAE1C,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM;AACb,oCAAoB,UAAQ;AAC1B,wBAAM,OAAO,CAAC;AACd,yCAAuB,IAAI;AAC3B,sBAAI,CAAC,KAAM,yBAAwB,IAAI;AACvC,yBAAO;AAAA,gBACT,CAAC;AAAA,cACH;AAAA,cACA,WAAW;AAAA,gBACT;AAAA,gBACA,oBAAoB;AAAA,cACtB;AAAA,cACA,OAAM;AAAA,cAEN;AAAA,gCAAAD,MAACO,gBAAA,EAAc,WAAU,WAAU;AAAA,gBAClC,YAAY,KACX,gBAAAP,MAAC,UAAK,WAAU,gIACb,qBACH;AAAA;AAAA;AAAA,UAEJ;AAAA,UAEA,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,UAE1C,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,OAAM;AAAA,cAEN,0BAAAA,MAAC,YAAS,WAAU,WAAU;AAAA;AAAA,UAChC;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,OAAM;AAAA,cAEN,0BAAAA,MAAC,YAAS,WAAU,WAAU;AAAA;AAAA,UAChC;AAAA;AAAA;AAAA,IACF;AAAA,IAGC,aAAa,WACZ,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,qBAAqB,aAAa;AAAA,QACpC;AAAA,QAEA;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,UAAU,qBAAqB,IAAI;AAAA,cACnC,WAAW;AAAA,gBACT;AAAA,gBACA,qBAAqB,iBAAiB;AAAA,cACxC;AAAA,cACA,SAAS,qBAAqB,UAAU;AAAA,cACxC,WACE,qBACI,OAAK;AACH,oBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK,SAAQ;AAAA,cAClD,IACA;AAAA,cAGN;AAAA,gCAAAD,MAAC,eAAY,IAAG,cACb,+BACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO;AAAA,sBACL,OAAO,iBAAiB;AAAA,sBACxB,QAAQ,iBAAiB;AAAA,sBACzB,WAAW,SAAS,KAAK;AAAA,sBACzB,iBAAiB;AAAA,oBACnB;AAAA,oBAEA,0BAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA,wBACA;AAAA;AAAA,oBACF;AAAA;AAAA,gBACF,IAEA,gBAAAC,MAAC,SAAI,KAAK,mBAAmB,WAAU,mHACrC;AAAA,kCAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA;AAAA;AAAA,kBACF;AAAA,kBACC,oBACC,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC;AAAA,sBACA;AAAA,sBACA;AAAA,sBACA,YAAY;AAAA,sBACZ,YAAY;AAAA,sBACZ,aAAa,MAAM,uBAAuB,IAAI;AAAA,sBAC9C,kBAAkB,oBAAoB,YAAY;AAAA,sBAClD,eAAe,iBAAiB,MAAM;AAAA,sBAAC;AAAA;AAAA,kBAEzC;AAAA,mBAEJ,GAEJ;AAAA,gBAGC,CAAC,sBACA,gBAAAC,MAAC,SAAI,WAAU,gCACb;AAAA,kCAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAS;AAAA,sBACT,WAAU;AAAA,sBAEV,0BAAAA,MAAC,eAAY,WAAU,WAAU;AAAA;AAAA,kBACnC;AAAA,kBACA,gBAAAC,MAAC,SAAI,WAAU,2CACb;AAAA,oCAAAA,MAAC,UAAK,WAAU,sCACb;AAAA,qCAAe;AAAA,sBAAE;AAAA,sBAAI,OAAO;AAAA,uBAC/B;AAAA,oBACC,OAAO,YAAY,GAAG,SACrB,gBAAAD,MAAC,UAAK,WAAU,mCACb,iBAAO,YAAY,EAAE,OACxB;AAAA,qBAEJ;AAAA,kBACA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAS;AAAA,sBACT,WAAU;AAAA,sBAEV,0BAAAA,MAAC,gBAAa,WAAU,WAAU;AAAA;AAAA,kBACpC;AAAA,mBACF;AAAA;AAAA;AAAA,UAEJ;AAAA,UAGC,oBAAoB,uBAAuB,CAAC,sBAC3C,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,aAAa,oBAAoB,YAAY;AAAA,cAC7C,YAAY;AAAA,cACZ,UAAU;AAAA,cACV,UAAU,oBAAoB,MAAM;AAAA,cAAC;AAAA,cACrC,SAAS,MAAM;AACb,uCAAuB,KAAK;AAC5B,wCAAwB,IAAI;AAAA,cAC9B;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IAEJ;AAAA,IAID,aAAa,UACZ,gBAAAA,MAAC,SAAI,WAAU,4CACb,0BAAAA,MAAC,SAAI,WAAU,wDACZ,iBAAO,IAAI,CAAC,aAAa,UAAU;AAClC,YAAM,iBAAiB,YAAY;AACnC,YAAM,cAAc,QAAQ,IAAI,OAAO,QAAQ,CAAC,GAAG,UAAU;AAC7D,YAAM,oBAAoB,YAAY,WAAW,YAAY,YAAY;AAEzE,aACE,gBAAAC,MAAC,SAAgB,WAAW,oBAAoB,kBAAkB,QAC/D;AAAA,6BACC,gBAAAD,MAAC,QAAG,WAAU,sFACX,sBAAY,SACf;AAAA,QAEF,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AACb,wBAAU,KAAK;AACf,0BAAY,OAAO;AAAA,YACrB;AAAA,YACA,WAAU;AAAA,YAEV;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA,kBAEvC,0BAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,aAAa,YAAY;AAAA,sBACzB,YAAY,YAAY;AAAA,sBACxB,mBAAmB;AAAA,sBAEnB,0BAAAA,MAAC,sBAAmB,YAAY,OAAO,YAAY,YAAY,OAC7D,0BAAAA,MAAC,kBAAe,aAAa,QAAQ,GAAG,aAAa,OAAO,QAAQ,GACtE;AAAA;AAAA,kBACF;AAAA;AAAA,cACF;AAAA,cACA,gBAAAA,MAAC,SAAI,WAAU,yEAAwE;AAAA,cACvF,gBAAAA,MAAC,SAAI,WAAU,yFACZ,sBAAY,QAAQ,GAAG,QAAQ,CAAC,KAAK,YAAY,KAAK,KAAK,QAAQ,GACtE;AAAA;AAAA;AAAA,QACF;AAAA,WA/BQ,KAgCV;AAAA,IAEJ,CAAC,GACH,GACF;AAAA,IAIF,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,aAAa,SAAS,UAAU;AAAA,QAClC;AAAA,QAEA,0BAAAA,MAAC,SAAI,WAAU,sCACZ,iBAAO,IAAI,CAAC,aAAa,UAAU;AAClC,gBAAM,iBAAiB,YAAY;AACnC,iBACE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cAEV,0BAAAA,MAAC,SAAI,WAAU,0FACb,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAa,YAAY;AAAA,kBACzB,YAAY,YAAY;AAAA,kBACxB,mBAAmB;AAAA,kBAEnB,0BAAAA,MAAC,sBAAmB,YAAY,OAAO,YAAY,YAAY,OAC7D,0BAAAA,MAAC,kBAAe,aAAa,QAAQ,GAAG,aAAa,OAAO,QAAQ,GACtE;AAAA;AAAA,cACF,GACF;AAAA;AAAA,YAbK;AAAA,UAcP;AAAA,QAEJ,CAAC,GACH;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":["jsx","motion","jsx","motion","createContext","useContext","useMemo","jsx","motion","jsx","jsx","motion","useCallback","useEffect","useState","jsx","useState","useEffect","useCallback","MessageCircle","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useState","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useRef","useCallback","useEffect","X","Fragment","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useEffect","useCallback","useMemo","jsx","jsxs","useState","useEffect","useMemo","useCallback","useRef","MessageCircle"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptslide",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "CLI and slide engine for PromptSlide presentations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -167,7 +167,7 @@ export async function add(args) {
167
167
  const shouldReplace = await confirm(" Replace entire deck-config.ts with this deck?", true)
168
168
  if (shouldReplace) {
169
169
  const slides = item.meta.slides.map(s => ({
170
- componentName: toPascalCase(s.slug),
170
+ componentName: s.componentName || toPascalCase(s.slug),
171
171
  importPath: `@/slides/${s.slug}`,
172
172
  steps: s.steps,
173
173
  section: s.section
@@ -180,7 +180,7 @@ export async function add(args) {
180
180
  } else {
181
181
  // Append individual slides
182
182
  for (const s of item.meta.slides) {
183
- const componentName = toPascalCase(s.slug)
183
+ const componentName = s.componentName || toPascalCase(s.slug)
184
184
  const importPath = `@/slides/${s.slug}`
185
185
  const updated = addSlideToDeckConfig(cwd, { componentName, importPath, steps: s.steps })
186
186
  if (updated) {
@@ -159,7 +159,7 @@ export async function clone(args) {
159
159
  // 6. Generate deck-config.ts
160
160
  if (item.meta?.slides) {
161
161
  const slides = item.meta.slides.map(s => ({
162
- componentName: toPascalCase(s.slug),
162
+ componentName: s.componentName || toPascalCase(s.slug),
163
163
  importPath: `@/slides/${s.slug}`,
164
164
  steps: s.steps,
165
165
  section: s.section
@@ -222,7 +222,7 @@ export async function create(args) {
222
222
  // Reconstruct deck-config.ts from deckConfig metadata
223
223
  if (fromItem.meta?.slides) {
224
224
  const slides = fromItem.meta.slides.map(s => ({
225
- componentName: toPascalCase(s.slug),
225
+ componentName: s.componentName || toPascalCase(s.slug),
226
226
  importPath: `@/slides/${s.slug}`,
227
227
  steps: s.steps,
228
228
  section: s.section
@@ -71,7 +71,7 @@ function detectSteps(content) {
71
71
  return max
72
72
  }
73
73
 
74
- function detectNpmDeps(content) {
74
+ function detectNpmDeps(content, projectDeps = {}) {
75
75
  const deps = {}
76
76
  // Match both unscoped (foo) and scoped (@scope/foo) packages, exclude relative imports
77
77
  const importRegex = /import\s+.*?\s+from\s+["']((?:@[a-zA-Z0-9-]+\/)?[a-zA-Z0-9-][^"']*)["']/g
@@ -81,7 +81,7 @@ function detectNpmDeps(content) {
81
81
  const pkg = full.startsWith("@") ? full.split("/").slice(0, 2).join("/") : full.split("/")[0]
82
82
  // Skip relative imports, react, and internal packages
83
83
  if (pkg.startsWith(".") || pkg === "react" || pkg === "react-dom" || pkg.startsWith("@promptslide/")) continue
84
- deps[pkg] = "latest"
84
+ deps[pkg] = projectDeps[pkg] ?? "latest"
85
85
  }
86
86
  return deps
87
87
  }
@@ -96,6 +96,15 @@ function detectRegistryDeps(content) {
96
96
  return deps
97
97
  }
98
98
 
99
+ function readProjectDeps(cwd) {
100
+ const pkgPath = join(cwd, "package.json")
101
+ if (!existsSync(pkgPath)) return {}
102
+ try {
103
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"))
104
+ return { ...pkg.devDependencies, ...pkg.dependencies }
105
+ } catch { return {} }
106
+ }
107
+
99
108
  /** Detect sibling layout imports — both relative ("./foo") and absolute ("@/layouts/foo"). */
100
109
  function detectLayoutSiblingDeps(content) {
101
110
  const deps = []
@@ -321,7 +330,7 @@ async function publishItem({ filePath, cwd, auth, typeOverride, interactive = tr
321
330
 
322
331
  const type = typeOverride || detectType(filePath) || "slide"
323
332
  const steps = detectSteps(content)
324
- const npmDeps = detectNpmDeps(content)
333
+ const npmDeps = detectNpmDeps(content, readProjectDeps(cwd))
325
334
  const registryDeps = detectRegistryDeps(content)
326
335
  const target = type === "layout" ? "src/layouts/" : "src/slides/"
327
336
 
@@ -396,6 +405,7 @@ async function publishItem({ filePath, cwd, auth, typeOverride, interactive = tr
396
405
 
397
406
  export async function publish(args) {
398
407
  const cwd = process.cwd()
408
+ const projectDeps = readProjectDeps(cwd)
399
409
 
400
410
  console.log()
401
411
  console.log(` ${bold("promptslide")} ${dim("publish")}`)
@@ -692,7 +702,7 @@ export async function publish(args) {
692
702
  skipped++
693
703
  } else {
694
704
  const assetDeps = detectAssetDepsInContent(themeContent, deckSlug, publicFileSet)
695
- const npmDeps = detectNpmDeps(themeContent)
705
+ const npmDeps = detectNpmDeps(themeContent, projectDeps)
696
706
 
697
707
  try {
698
708
  const result = await publishToRegistry({
@@ -730,7 +740,7 @@ export async function publish(args) {
730
740
  }
731
741
 
732
742
  const assetDeps = detectAssetDepsInContent(content, deckSlug, publicFileSet)
733
- const npmDeps = detectNpmDeps(content)
743
+ const npmDeps = detectNpmDeps(content, projectDeps)
734
744
  const siblingDeps = detectLayoutSiblingDeps(content).map(d => `${deckSlug}/${d}`)
735
745
  const regDeps = hasTheme ? [`${deckSlug}/theme`] : []
736
746
  regDeps.push(...siblingDeps, ...assetDeps)
@@ -775,7 +785,7 @@ export async function publish(args) {
775
785
 
776
786
  const assetDeps = detectAssetDepsInContent(content, deckSlug, publicFileSet)
777
787
  const layoutDeps = detectRegistryDeps(content).map(d => `${deckSlug}/${d}`)
778
- const npmDeps = detectNpmDeps(content)
788
+ const npmDeps = detectNpmDeps(content, projectDeps)
779
789
  const steps = detectSteps(content)
780
790
  const slideConfig = deckConfig.slides.find(s => s.slug === slideName)
781
791
  const section = slideConfig?.section || undefined
@@ -836,7 +846,7 @@ export async function publish(args) {
836
846
  // Collect npm deps from shared source files
837
847
  const sharedNpmDeps = {}
838
848
  for (const f of sharedFiles) {
839
- Object.assign(sharedNpmDeps, detectNpmDeps(f.content))
849
+ Object.assign(sharedNpmDeps, detectNpmDeps(f.content, projectDeps))
840
850
  }
841
851
 
842
852
  let deckItemId = null
@@ -905,7 +915,7 @@ export async function publish(args) {
905
915
  // Detect metadata
906
916
  const type = typeOverride || detectType(filePath) || "slide"
907
917
  const steps = detectSteps(content)
908
- const npmDeps = detectNpmDeps(content)
918
+ const npmDeps = detectNpmDeps(content, projectDeps)
909
919
  const registryDeps = detectRegistryDeps(content)
910
920
  const target = type === "layout" ? "src/layouts/" : "src/slides/"
911
921
 
@@ -39,11 +39,8 @@ export function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, o
39
39
 
40
40
  <div className="flex-1 overflow-y-auto p-2">
41
41
  {annotations.length === 0 && (
42
- <div className="flex flex-col items-center gap-2 px-4 py-8 text-center">
43
- <div className="flex h-10 w-10 items-center justify-center rounded-xl bg-white/[0.04]">
44
- <MessageCircle className="h-5 w-5 text-neutral-600" />
45
- </div>
46
- <p className="text-sm text-neutral-500">Click on slide elements to add annotations</p>
42
+ <div className="flex flex-col gap-4 px-3 py-6">
43
+ <WorkflowHint />
47
44
  </div>
48
45
  )}
49
46
 
@@ -83,6 +80,35 @@ export function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, o
83
80
  </div>
84
81
  )}
85
82
  </div>
83
+
84
+ {annotations.length > 0 && (
85
+ <>
86
+ <div className="h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" />
87
+ <div className="px-3 py-3">
88
+ <WorkflowHint />
89
+ </div>
90
+ </>
91
+ )}
92
+ </div>
93
+ )
94
+ }
95
+
96
+ function WorkflowHint() {
97
+ return (
98
+ <div className="flex flex-col gap-2">
99
+ <p className="text-[11px] font-medium tracking-wider text-neutral-500 uppercase">Workflow</p>
100
+ <div className="flex items-start gap-2.5">
101
+ <div className="mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]">1</div>
102
+ <p className="text-[12px] leading-snug text-neutral-400">Annotate everything you want to fix</p>
103
+ </div>
104
+ <div className="flex items-start gap-2.5">
105
+ <div className="mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]">2</div>
106
+ <p className="text-[12px] leading-snug text-neutral-400">Switch to your coding agent</p>
107
+ </div>
108
+ <div className="flex items-start gap-2.5">
109
+ <div className="mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]">3</div>
110
+ <p className="text-[12px] leading-snug text-neutral-400">"Fix open annotations"</p>
111
+ </div>
86
112
  </div>
87
113
  )
88
114
  }
@@ -187,7 +187,7 @@ export function SlideDeck({ slides, transition, directionalTransition, annotatio
187
187
  return
188
188
  }
189
189
 
190
- if (viewMode !== "slide" || isAnnotationMode) return
190
+ if (viewMode !== "slide" || (isAnnotationMode && !isPresentationMode)) return
191
191
 
192
192
  if (e.key === "ArrowRight" || e.key === " ") {
193
193
  e.preventDefault()
@@ -200,7 +200,7 @@ export function SlideDeck({ slides, transition, directionalTransition, annotatio
200
200
 
201
201
  window.addEventListener("keydown", handleKeyDown)
202
202
  return () => window.removeEventListener("keydown", handleKeyDown)
203
- }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode])
203
+ }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode, isPresentationMode])
204
204
 
205
205
  return (
206
206
  <div className="min-h-screen w-full bg-neutral-950 text-foreground">
@@ -190,36 +190,53 @@ export async function fetchOrganizations(auth) {
190
190
  * @param {{ registry: string, apiKey: string }} auth - Auth credentials
191
191
  * @returns {Promise<object>} Registry item JSON
192
192
  */
193
- export async function fetchRegistryItem(nameOrUrl, auth) {
193
+ export async function fetchRegistryItem(nameOrUrl, auth, { retries = 2 } = {}) {
194
194
  const url = nameOrUrl.startsWith("http")
195
195
  ? nameOrUrl
196
196
  : `${auth.registry}/api/r/${nameOrUrl}.json`
197
197
 
198
- const res = await fetch(url, {
199
- headers: authHeaders(auth)
200
- })
198
+ let lastError
199
+ for (let attempt = 0; attempt <= retries; attempt++) {
200
+ try {
201
+ const res = await fetch(url, {
202
+ headers: authHeaders(auth)
203
+ })
201
204
 
202
- if (res.status === 401) {
203
- throw new Error("Authentication failed. Run `promptslide login` to re-authenticate.")
204
- }
205
- if (res.status === 403) {
206
- const body = await res.json().catch(() => ({}))
207
- if (body.status === "pending_review") {
208
- throw new Error(`Item "${nameOrUrl}" is pending review. An admin must approve it first.`)
209
- }
210
- if (body.status === "rejected") {
211
- throw new Error(`Item "${nameOrUrl}" was rejected by an admin.`)
205
+ if (res.status === 401) {
206
+ throw new Error("Authentication failed. Run `promptslide login` to re-authenticate.")
207
+ }
208
+ if (res.status === 403) {
209
+ const body = await res.json().catch(() => ({}))
210
+ if (body.status === "pending_review") {
211
+ throw new Error(`Item "${nameOrUrl}" is pending review. An admin must approve it first.`)
212
+ }
213
+ if (body.status === "rejected") {
214
+ throw new Error(`Item "${nameOrUrl}" was rejected by an admin.`)
215
+ }
216
+ throw new Error("Access denied. This item belongs to a different organization.")
217
+ }
218
+ if (res.status === 404) {
219
+ throw new Error(`Item not found: ${nameOrUrl}`)
220
+ }
221
+ if (!res.ok) {
222
+ throw new Error(`Registry error (${res.status}): ${await res.text()}`)
223
+ }
224
+
225
+ return res.json()
226
+ } catch (err) {
227
+ lastError = err
228
+ // Only retry on network errors (fetch failed), not on HTTP-level errors
229
+ if (err.message.includes("Authentication") || err.message.includes("Access denied") ||
230
+ err.message.includes("not found") || err.message.includes("pending review") ||
231
+ err.message.includes("rejected") || err.message.includes("Registry error")) {
232
+ throw err
233
+ }
234
+ if (attempt < retries) {
235
+ await new Promise(r => setTimeout(r, 500 * (attempt + 1)))
236
+ }
212
237
  }
213
- throw new Error("Access denied. This item belongs to a different organization.")
214
- }
215
- if (res.status === 404) {
216
- throw new Error(`Item not found: ${nameOrUrl}`)
217
238
  }
218
- if (!res.ok) {
219
- throw new Error(`Registry error (${res.status}): ${await res.text()}`)
220
- }
221
-
222
- return res.json()
239
+ throw lastError
223
240
  }
224
241
 
225
242
  /**
@@ -8,7 +8,7 @@ Slide deck framework: Vite + React 19 + Tailwind v4 + Framer Motion. Each slide
8
8
 
9
9
  Do not jump straight to writing slides. First confirm the visual direction with the user — theme colors, fonts, and overall style. Then, for each slide, think about what design approach fits the content before coding. Not everything needs cards or grids — let the content shape the layout.
10
10
 
11
- If the PromptSlide skill is installed, follow its workflow — it includes design planning steps that should happen before writing any slides.
11
+ If the `promptslide` skill is installed, follow its workflow and **always consult the skill before writing slide code** — it documents framework-specific behavior and constraints that aren't obvious from the component APIs alone.
12
12
 
13
13
  ---
14
14
 
@@ -87,6 +87,6 @@ Layouts in `src/layouts/` and theme colors in `src/globals.css` are yours to cus
87
87
  - **Slide dimensions**: 1280×720 (16:9). Content scales automatically in presentation mode.
88
88
  - **Semantic colors**: Use `text-foreground`, `text-muted-foreground`, `text-primary`, `bg-background`, `bg-card`, `border-border`.
89
89
  - **Icons**: Import from `lucide-react` (e.g., `import { ArrowRight } from "lucide-react"`).
90
- - **Animations**: Use `<Animated step={n}>` for click-to-reveal. The `steps` value in `deck-config.ts` must equal the highest step number used. Available: `fade`, `slide-up`, `slide-down`, `slide-left`, `slide-right`, `scale`.
90
+ - **Animations**: Use `<Animated step={n}>` for click-to-reveal. The `steps` value in `deck-config.ts` must equal the highest step number used. Available: `fade`, `slide-up`, `slide-down`, `slide-left`, `slide-right`, `scale`. **Important:** `<Animated>` renders a wrapper div — when inside a grid/flex container, pass layout classes (`h-full`, `col-span-*`, etc.) via `className` on the `<Animated>`, not only on the inner child.
91
91
  - **PDF compatibility**: No `blur()` or `backdrop-filter` (dropped by Chromium). No gradients (use solid colors with opacity). No `box-shadow` (doesn't export correctly) — use borders or background tints instead.
92
92
  - **Brand color**: Edit `--primary` in `src/globals.css`. Configure logo and fonts in `src/theme.ts`.