promptslide 0.3.15 → 0.3.17
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 +14 -0
- package/README.md +16 -12
- package/dist/index.d.ts +17 -7
- package/dist/index.js +987 -211
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/commands/add.mjs +9 -2
- package/src/commands/clone.mjs +62 -22
- package/src/commands/create.mjs +8 -1
- package/src/commands/publish.mjs +34 -8
- package/src/commands/pull.mjs +66 -29
- package/src/commands/studio.mjs +3 -0
- package/src/commands/to-image.mjs +18 -5
- package/src/commands/to-pdf.mjs +27 -0
- package/src/core/animated.tsx +3 -3
- package/src/core/annotations/adapters/http.ts +8 -0
- package/src/core/annotations/annotation-overlay.tsx +77 -32
- package/src/core/annotations/annotation-panel.tsx +184 -51
- package/src/core/annotations/types.ts +5 -0
- package/src/core/annotations/use-annotations.ts +57 -7
- package/src/core/slide-deck.tsx +657 -115
- package/src/core/transitions.ts +5 -3
- package/src/core/types.ts +2 -0
- package/src/core/use-slide-navigation.ts +93 -3
- package/src/index.mjs +6 -0
- package/src/utils/deck-config.mjs +30 -11
- package/src/utils/deck-sync.mjs +78 -0
- package/src/utils/export.mjs +151 -52
- package/src/utils/registry.mjs +23 -0
- package/src/utils/studio-discovery.mjs +37 -0
- package/src/vite/plugin.mjs +146 -16
- package/templates/default/AGENTS.md +3 -2
- package/templates/default/src/deck-config.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -115,7 +115,7 @@ function Animated({
|
|
|
115
115
|
return /* @__PURE__ */ jsx2(
|
|
116
116
|
motion.div,
|
|
117
117
|
{
|
|
118
|
-
initial: "hidden",
|
|
118
|
+
initial: isVisible ? false : "hidden",
|
|
119
119
|
animate: isVisible ? "visible" : "hidden",
|
|
120
120
|
variants: animationVariants[animation],
|
|
121
121
|
transition: {
|
|
@@ -149,7 +149,7 @@ function AnimatedGroup({
|
|
|
149
149
|
return /* @__PURE__ */ jsx2(
|
|
150
150
|
motion.div,
|
|
151
151
|
{
|
|
152
|
-
initial: "hidden",
|
|
152
|
+
initial: isVisible ? false : "hidden",
|
|
153
153
|
animate: isVisible ? "visible" : "hidden",
|
|
154
154
|
variants: containerVariants,
|
|
155
155
|
className,
|
|
@@ -160,9 +160,11 @@ function AnimatedGroup({
|
|
|
160
160
|
|
|
161
161
|
// src/core/transitions.ts
|
|
162
162
|
var fadeVariants = {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
// Render the next slide fully beneath the current one, then fade the
|
|
164
|
+
// outgoing slide away. This keeps the frame opaque throughout the change.
|
|
165
|
+
enter: { opacity: 1, zIndex: 0 },
|
|
166
|
+
center: { opacity: 1, zIndex: 0 },
|
|
167
|
+
exit: { opacity: 0, zIndex: 1 }
|
|
166
168
|
};
|
|
167
169
|
var slideLeftVariants = {
|
|
168
170
|
enter: { x: SLIDE_DISTANCE, opacity: 0 },
|
|
@@ -352,20 +354,102 @@ function MorphText({
|
|
|
352
354
|
}
|
|
353
355
|
|
|
354
356
|
// src/core/use-slide-navigation.ts
|
|
355
|
-
import { useCallback, useEffect, useState } from "react";
|
|
357
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
358
|
+
var POSITION_KEY = "promptslide:position";
|
|
359
|
+
function slideKey(slides, index) {
|
|
360
|
+
return slides[index]?.id || String(index + 1);
|
|
361
|
+
}
|
|
362
|
+
function slideFromHash(slides) {
|
|
363
|
+
if (typeof window === "undefined" || !window.location.hash) return null;
|
|
364
|
+
let hash;
|
|
365
|
+
try {
|
|
366
|
+
hash = decodeURIComponent(window.location.hash.slice(1));
|
|
367
|
+
} catch {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const byId = slides.findIndex((slide) => slide.id === hash);
|
|
371
|
+
if (byId >= 0) return byId;
|
|
372
|
+
if (/^[1-9]\d*$/.test(hash)) {
|
|
373
|
+
const index = Number(hash) - 1;
|
|
374
|
+
if (index < slides.length) return index;
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
function savedStep(slides, index) {
|
|
379
|
+
if (typeof window === "undefined") return 0;
|
|
380
|
+
try {
|
|
381
|
+
const saved = JSON.parse(window.sessionStorage.getItem(POSITION_KEY) || "null");
|
|
382
|
+
if (saved?.slide === slideKey(slides, index) && Number.isInteger(saved.step)) {
|
|
383
|
+
return Math.max(0, Math.min(saved.step, slides[index]?.steps ?? 0));
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
356
389
|
function useSlideNavigation({
|
|
357
390
|
slides,
|
|
358
391
|
initialSlide = 0,
|
|
359
392
|
onSlideChange
|
|
360
393
|
}) {
|
|
361
|
-
const [currentSlide, setCurrentSlide] = useState(initialSlide);
|
|
362
|
-
const [animationStep, setAnimationStep] = useState(
|
|
394
|
+
const [currentSlide, setCurrentSlide] = useState(() => slideFromHash(slides) ?? initialSlide);
|
|
395
|
+
const [animationStep, setAnimationStep] = useState(
|
|
396
|
+
() => savedStep(slides, slideFromHash(slides) ?? initialSlide)
|
|
397
|
+
);
|
|
398
|
+
const slidesRef = useRef(slides);
|
|
399
|
+
const activeKeyRef = useRef(slideKey(slides, slideFromHash(slides) ?? initialSlide));
|
|
363
400
|
const [navState, setNavState] = useState({
|
|
364
401
|
status: "idle",
|
|
365
402
|
direction: 0
|
|
366
403
|
});
|
|
367
404
|
const [queuedAction, setQueuedAction] = useState(null);
|
|
368
405
|
const totalSteps = slides[currentSlide]?.steps ?? 0;
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
if (typeof window === "undefined" || slides.length === 0) return;
|
|
408
|
+
if (slidesRef.current !== slides) {
|
|
409
|
+
slidesRef.current = slides;
|
|
410
|
+
const previousKey = activeKeyRef.current;
|
|
411
|
+
const movedIndex = slides.findIndex((slide) => slide.id && slide.id === previousKey);
|
|
412
|
+
if (movedIndex >= 0 && movedIndex !== currentSlide) {
|
|
413
|
+
setCurrentSlide(movedIndex);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (movedIndex < 0 && !/^[1-9]\d*$/.test(previousKey) && currentSlide !== 0) {
|
|
417
|
+
setAnimationStep(0);
|
|
418
|
+
setCurrentSlide(0);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (!slides[currentSlide]) {
|
|
423
|
+
setCurrentSlide(0);
|
|
424
|
+
setAnimationStep(0);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const key = slideKey(slides, currentSlide);
|
|
428
|
+
activeKeyRef.current = key;
|
|
429
|
+
const hash = `#${encodeURIComponent(key)}`;
|
|
430
|
+
if (window.location.hash !== hash) {
|
|
431
|
+
window.history.replaceState(window.history.state, "", hash);
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
window.sessionStorage.setItem(
|
|
435
|
+
POSITION_KEY,
|
|
436
|
+
JSON.stringify({ slide: key, step: animationStep })
|
|
437
|
+
);
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
}, [slides, currentSlide, animationStep]);
|
|
441
|
+
useEffect(() => {
|
|
442
|
+
const handleHashChange = () => {
|
|
443
|
+
const index = slideFromHash(slides) ?? 0;
|
|
444
|
+
if (index === currentSlide) return;
|
|
445
|
+
setNavState({ status: "transitioning", direction: index > currentSlide ? 1 : -1 });
|
|
446
|
+
setAnimationStep(0);
|
|
447
|
+
setCurrentSlide(index);
|
|
448
|
+
onSlideChange?.(index);
|
|
449
|
+
};
|
|
450
|
+
window.addEventListener("hashchange", handleHashChange);
|
|
451
|
+
return () => window.removeEventListener("hashchange", handleHashChange);
|
|
452
|
+
}, [slides, currentSlide, onSlideChange]);
|
|
369
453
|
const onTransitionComplete = useCallback(() => {
|
|
370
454
|
setNavState((prev) => {
|
|
371
455
|
if (prev.status === "transitioning") {
|
|
@@ -668,19 +752,30 @@ function SlideEmbed({ slides, transition, directionalTransition }) {
|
|
|
668
752
|
|
|
669
753
|
// src/core/slide-deck.tsx
|
|
670
754
|
import { LayoutGroup } from "framer-motion";
|
|
671
|
-
import {
|
|
672
|
-
|
|
755
|
+
import {
|
|
756
|
+
ChevronLeft,
|
|
757
|
+
ChevronRight,
|
|
758
|
+
Download,
|
|
759
|
+
Grid3X3,
|
|
760
|
+
List,
|
|
761
|
+
MessageCircle,
|
|
762
|
+
Monitor,
|
|
763
|
+
PanelLeftClose,
|
|
764
|
+
PanelLeftOpen,
|
|
765
|
+
Play
|
|
766
|
+
} from "lucide-react";
|
|
767
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useLayoutEffect, useMemo as useMemo4, useRef as useRef6, useState as useState7 } from "react";
|
|
673
768
|
|
|
674
769
|
// src/core/annotations/annotation-overlay.tsx
|
|
675
|
-
import { useCallback as useCallback3, useEffect as useEffect4, useRef as
|
|
770
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
|
|
676
771
|
|
|
677
772
|
// src/core/annotations/annotation-form.tsx
|
|
678
|
-
import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
|
|
773
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
679
774
|
import { ArrowUp, X } from "lucide-react";
|
|
680
775
|
import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
681
776
|
function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }) {
|
|
682
777
|
const [text, setText] = useState3("");
|
|
683
|
-
const textareaRef =
|
|
778
|
+
const textareaRef = useRef2(null);
|
|
684
779
|
useEffect3(() => {
|
|
685
780
|
textareaRef.current?.focus();
|
|
686
781
|
}, []);
|
|
@@ -829,15 +924,30 @@ function findByTextContent(root, text) {
|
|
|
829
924
|
|
|
830
925
|
// src/core/annotations/annotation-overlay.tsx
|
|
831
926
|
import { Fragment, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
832
|
-
function AnnotationOverlay({
|
|
927
|
+
function AnnotationOverlay({
|
|
928
|
+
slides,
|
|
929
|
+
currentSlide,
|
|
930
|
+
slideContainerRef,
|
|
931
|
+
selectedId,
|
|
932
|
+
hoveredId,
|
|
933
|
+
onSelectId,
|
|
934
|
+
onShowPanel,
|
|
935
|
+
slideAnnotations,
|
|
936
|
+
addAnnotation
|
|
937
|
+
}) {
|
|
833
938
|
const [pending, setPending] = useState4(null);
|
|
834
939
|
const [hoveredElement, setHoveredElement] = useState4(null);
|
|
835
|
-
const overlayRef =
|
|
940
|
+
const overlayRef = useRef3(null);
|
|
836
941
|
const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`;
|
|
837
942
|
const resolvedAnnotations = slideAnnotations.map((a, i) => {
|
|
838
943
|
const container = slideContainerRef.current;
|
|
839
944
|
if (!container) {
|
|
840
|
-
return {
|
|
945
|
+
return {
|
|
946
|
+
annotation: a,
|
|
947
|
+
xPercent: a.target.position.xPercent,
|
|
948
|
+
yPercent: a.target.position.yPercent,
|
|
949
|
+
number: i + 1
|
|
950
|
+
};
|
|
841
951
|
}
|
|
842
952
|
const { element } = resolveTarget(a.target, container);
|
|
843
953
|
if (element) {
|
|
@@ -847,7 +957,12 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
847
957
|
const yPercent = (rect.top + rect.height / 2 - containerRect.top) / containerRect.height * 100;
|
|
848
958
|
return { annotation: a, xPercent, yPercent, number: i + 1 };
|
|
849
959
|
}
|
|
850
|
-
return {
|
|
960
|
+
return {
|
|
961
|
+
annotation: a,
|
|
962
|
+
xPercent: a.target.position.xPercent,
|
|
963
|
+
yPercent: a.target.position.yPercent,
|
|
964
|
+
number: i + 1
|
|
965
|
+
};
|
|
851
966
|
});
|
|
852
967
|
const handleOverlayClick = useCallback3(
|
|
853
968
|
(e) => {
|
|
@@ -881,11 +996,11 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
881
996
|
const handleSubmit = useCallback3(
|
|
882
997
|
(text) => {
|
|
883
998
|
if (!pending) return;
|
|
884
|
-
addAnnotation(currentSlide, slideTitle, pending.target, text);
|
|
999
|
+
addAnnotation(currentSlide, slideTitle, pending.target, text, slides[currentSlide]?.id);
|
|
885
1000
|
setPending(null);
|
|
886
1001
|
onShowPanel();
|
|
887
1002
|
},
|
|
888
|
-
[pending, currentSlide, slideTitle, addAnnotation, onShowPanel]
|
|
1003
|
+
[pending, currentSlide, slideTitle, slides, addAnnotation, onShowPanel]
|
|
889
1004
|
);
|
|
890
1005
|
const handleMouseMove = useCallback3(
|
|
891
1006
|
(e) => {
|
|
@@ -902,12 +1017,14 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
902
1017
|
if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {
|
|
903
1018
|
const containerRect = container.getBoundingClientRect();
|
|
904
1019
|
const elRect = elementUnder.getBoundingClientRect();
|
|
1020
|
+
const scaleX = containerRect.width / container.offsetWidth;
|
|
1021
|
+
const scaleY = containerRect.height / container.offsetHeight;
|
|
905
1022
|
setHoveredElement(
|
|
906
1023
|
new DOMRect(
|
|
907
|
-
elRect.left - containerRect.left,
|
|
908
|
-
elRect.top - containerRect.top,
|
|
909
|
-
elRect.width,
|
|
910
|
-
elRect.height
|
|
1024
|
+
(elRect.left - containerRect.left) / scaleX,
|
|
1025
|
+
(elRect.top - containerRect.top) / scaleY,
|
|
1026
|
+
elRect.width / scaleX,
|
|
1027
|
+
elRect.height / scaleY
|
|
911
1028
|
)
|
|
912
1029
|
);
|
|
913
1030
|
} else {
|
|
@@ -941,7 +1058,9 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
941
1058
|
role: "button",
|
|
942
1059
|
tabIndex: 0,
|
|
943
1060
|
className: "absolute inset-0 z-20",
|
|
944
|
-
style: {
|
|
1061
|
+
style: {
|
|
1062
|
+
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`
|
|
1063
|
+
},
|
|
945
1064
|
onClick: handleOverlayClick,
|
|
946
1065
|
onKeyDown: (e) => {
|
|
947
1066
|
if (e.key === "Escape") {
|
|
@@ -953,7 +1072,7 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
953
1072
|
onMouseLeave: () => setHoveredElement(null)
|
|
954
1073
|
}
|
|
955
1074
|
),
|
|
956
|
-
resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => /* @__PURE__ */ jsx10(
|
|
1075
|
+
resolvedAnnotations.filter(({ annotation }) => annotation.status === "open" || annotation.id === hoveredId).map(({ annotation, xPercent, yPercent, number }) => /* @__PURE__ */ jsx10(
|
|
957
1076
|
AnnotationPin,
|
|
958
1077
|
{
|
|
959
1078
|
number,
|
|
@@ -982,61 +1101,96 @@ function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId
|
|
|
982
1101
|
}
|
|
983
1102
|
|
|
984
1103
|
// src/core/annotations/annotation-panel.tsx
|
|
985
|
-
import {
|
|
1104
|
+
import { Check, Pencil, PanelRightClose, RotateCcw, Trash2 } from "lucide-react";
|
|
1105
|
+
import { useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
|
|
986
1106
|
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
987
|
-
function AnnotationPanel({
|
|
1107
|
+
function AnnotationPanel({
|
|
1108
|
+
annotations,
|
|
1109
|
+
unlinkedAnnotations = [],
|
|
1110
|
+
selectedId,
|
|
1111
|
+
onSelect,
|
|
1112
|
+
onHover,
|
|
1113
|
+
onDelete,
|
|
1114
|
+
onUpdate,
|
|
1115
|
+
onClose
|
|
1116
|
+
}) {
|
|
988
1117
|
const open = annotations.filter((a) => a.status === "open");
|
|
989
1118
|
const resolved = annotations.filter((a) => a.status === "resolved");
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
/* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-white", children: "Annotations" }),
|
|
995
|
-
open.length > 0 && /* @__PURE__ */ jsx11("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]", children: open.length })
|
|
996
|
-
] }),
|
|
1119
|
+
const total = annotations.length + unlinkedAnnotations.length;
|
|
1120
|
+
const numberFor = (annotation) => annotations.findIndex((a) => a.id === annotation.id) + 1;
|
|
1121
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex h-full w-80 max-w-[90vw] flex-shrink-0 flex-col border-l border-neutral-800 bg-neutral-950 text-white", children: [
|
|
1122
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between border-b border-neutral-800 px-3 py-2", children: [
|
|
997
1123
|
/* @__PURE__ */ jsx11(
|
|
998
1124
|
"button",
|
|
999
1125
|
{
|
|
1000
1126
|
onClick: onClose,
|
|
1001
|
-
className: "rounded
|
|
1002
|
-
|
|
1127
|
+
className: "rounded p-2 text-neutral-400 hover:bg-neutral-800 hover:text-white",
|
|
1128
|
+
"aria-label": "Hide comments",
|
|
1129
|
+
title: "Hide comments (C)",
|
|
1130
|
+
children: /* @__PURE__ */ jsx11(PanelRightClose, { className: "h-4 w-4" })
|
|
1003
1131
|
}
|
|
1004
|
-
)
|
|
1132
|
+
),
|
|
1133
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-right text-sm font-semibold", children: [
|
|
1134
|
+
"Comments (",
|
|
1135
|
+
total,
|
|
1136
|
+
")"
|
|
1137
|
+
] })
|
|
1005
1138
|
] }),
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
|
|
1008
|
-
annotations.length === 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-4 px-3 py-6", children: /* @__PURE__ */ jsx11(WorkflowHint, {}) }),
|
|
1139
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: [
|
|
1140
|
+
annotations.length === 0 && unlinkedAnnotations.length === 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-4 px-3 py-6", children: /* @__PURE__ */ jsx11(WorkflowHint, {}) }),
|
|
1009
1141
|
open.length > 0 && /* @__PURE__ */ jsxs5("div", { children: [
|
|
1010
1142
|
/* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Open" }),
|
|
1011
|
-
open.map((a
|
|
1143
|
+
open.map((a) => /* @__PURE__ */ jsx11(
|
|
1012
1144
|
AnnotationItem,
|
|
1013
1145
|
{
|
|
1014
1146
|
annotation: a,
|
|
1015
|
-
number:
|
|
1147
|
+
number: numberFor(a),
|
|
1016
1148
|
isSelected: a.id === selectedId,
|
|
1017
1149
|
onSelect: () => onSelect(a.id),
|
|
1018
|
-
|
|
1150
|
+
onHover: (hovered) => onHover(hovered ? a.id : null),
|
|
1151
|
+
onDelete: () => onDelete(a.id),
|
|
1152
|
+
onUpdate: (patch) => onUpdate(a.id, patch)
|
|
1019
1153
|
},
|
|
1020
1154
|
a.id
|
|
1021
1155
|
))
|
|
1022
1156
|
] }),
|
|
1023
1157
|
resolved.length > 0 && /* @__PURE__ */ jsxs5("div", { className: open.length > 0 ? "mt-3" : "", children: [
|
|
1024
1158
|
/* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Resolved" }),
|
|
1025
|
-
resolved.map((a
|
|
1159
|
+
resolved.map((a) => /* @__PURE__ */ jsx11(
|
|
1026
1160
|
AnnotationItem,
|
|
1027
1161
|
{
|
|
1028
1162
|
annotation: a,
|
|
1029
|
-
number:
|
|
1163
|
+
number: numberFor(a),
|
|
1030
1164
|
isSelected: a.id === selectedId,
|
|
1031
1165
|
onSelect: () => onSelect(a.id),
|
|
1032
|
-
|
|
1166
|
+
onHover: (hovered) => onHover(hovered ? a.id : null),
|
|
1167
|
+
onDelete: () => onDelete(a.id),
|
|
1168
|
+
onUpdate: (patch) => onUpdate(a.id, patch)
|
|
1033
1169
|
},
|
|
1034
1170
|
a.id
|
|
1035
1171
|
))
|
|
1172
|
+
] }),
|
|
1173
|
+
unlinkedAnnotations.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "mt-3", children: [
|
|
1174
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Removed slides" }),
|
|
1175
|
+
unlinkedAnnotations.map((a, i) => /* @__PURE__ */ jsxs5("div", { children: [
|
|
1176
|
+
/* @__PURE__ */ jsx11("p", { className: "px-2 text-[11px] text-neutral-500", children: a.slideTitle || a.slideId }),
|
|
1177
|
+
/* @__PURE__ */ jsx11(
|
|
1178
|
+
AnnotationItem,
|
|
1179
|
+
{
|
|
1180
|
+
annotation: a,
|
|
1181
|
+
number: i + 1,
|
|
1182
|
+
isSelected: a.id === selectedId,
|
|
1183
|
+
onSelect: () => onSelect(a.id),
|
|
1184
|
+
onHover: (hovered) => onHover(hovered ? a.id : null),
|
|
1185
|
+
onDelete: () => onDelete(a.id),
|
|
1186
|
+
onUpdate: (patch) => onUpdate(a.id, patch)
|
|
1187
|
+
}
|
|
1188
|
+
)
|
|
1189
|
+
] }, a.id))
|
|
1036
1190
|
] })
|
|
1037
1191
|
] }),
|
|
1038
|
-
annotations.length > 0 && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1039
|
-
/* @__PURE__ */ jsx11("div", { className: "
|
|
1192
|
+
annotations.length + unlinkedAnnotations.length > 0 && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1193
|
+
/* @__PURE__ */ jsx11("div", { className: "border-t border-neutral-800" }),
|
|
1040
1194
|
/* @__PURE__ */ jsx11("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx11(WorkflowHint, {}) })
|
|
1041
1195
|
] })
|
|
1042
1196
|
] });
|
|
@@ -1046,7 +1200,7 @@ function WorkflowHint() {
|
|
|
1046
1200
|
/* @__PURE__ */ jsx11("p", { className: "text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Workflow" }),
|
|
1047
1201
|
/* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
|
|
1048
1202
|
/* @__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: "
|
|
1203
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[12px] leading-snug text-neutral-400", children: "Comment on everything you want to fix" })
|
|
1050
1204
|
] }),
|
|
1051
1205
|
/* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
|
|
1052
1206
|
/* @__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" }),
|
|
@@ -1054,7 +1208,7 @@ function WorkflowHint() {
|
|
|
1054
1208
|
] }),
|
|
1055
1209
|
/* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
|
|
1056
1210
|
/* @__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
|
|
1211
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[12px] leading-snug text-neutral-400", children: '"Fix open comments"' })
|
|
1058
1212
|
] })
|
|
1059
1213
|
] });
|
|
1060
1214
|
}
|
|
@@ -1063,30 +1217,81 @@ function AnnotationItem({
|
|
|
1063
1217
|
number,
|
|
1064
1218
|
isSelected,
|
|
1065
1219
|
onSelect,
|
|
1066
|
-
|
|
1220
|
+
onHover,
|
|
1221
|
+
onDelete,
|
|
1222
|
+
onUpdate
|
|
1067
1223
|
}) {
|
|
1224
|
+
const [isEditing, setIsEditing] = useState5(false);
|
|
1225
|
+
const [draft, setDraft] = useState5(annotation.body);
|
|
1226
|
+
const editRef = useRef4(null);
|
|
1227
|
+
useEffect5(() => {
|
|
1228
|
+
if (isEditing) editRef.current?.focus();
|
|
1229
|
+
}, [isEditing]);
|
|
1230
|
+
const saveEdit = () => {
|
|
1231
|
+
const body = draft.trim();
|
|
1232
|
+
if (!body) return;
|
|
1233
|
+
if (body !== annotation.body) onUpdate({ body });
|
|
1234
|
+
setIsEditing(false);
|
|
1235
|
+
};
|
|
1068
1236
|
return /* @__PURE__ */ jsxs5(
|
|
1069
1237
|
"div",
|
|
1070
1238
|
{
|
|
1071
1239
|
role: "button",
|
|
1072
1240
|
tabIndex: 0,
|
|
1073
1241
|
onClick: onSelect,
|
|
1242
|
+
onMouseEnter: () => onHover(true),
|
|
1243
|
+
onMouseLeave: () => onHover(false),
|
|
1074
1244
|
onKeyDown: (e) => {
|
|
1075
|
-
if (e.key === "Enter" || e.key === " ") onSelect();
|
|
1245
|
+
if (e.target === e.currentTarget && (e.key === "Enter" || e.key === " ")) onSelect();
|
|
1076
1246
|
},
|
|
1077
1247
|
className: `group relative mb-0.5 cursor-pointer rounded-xl p-2.5 transition-all duration-150 ${isSelected ? "bg-[#FF6B35]/10 ring-1 ring-[#FF6B35]/20" : "hover:bg-white/[0.04]"}`,
|
|
1078
1248
|
children: [
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1249
|
+
/* @__PURE__ */ jsxs5("div", { className: "absolute top-2 right-2 z-10 flex gap-0.5 rounded-full border border-neutral-700 bg-neutral-900/70 p-0.5 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100 group-focus-within:opacity-100", children: [
|
|
1250
|
+
/* @__PURE__ */ jsx11(
|
|
1251
|
+
"button",
|
|
1252
|
+
{
|
|
1253
|
+
type: "button",
|
|
1254
|
+
title: "Edit comment",
|
|
1255
|
+
"aria-label": "Edit comment",
|
|
1256
|
+
onClick: (e) => {
|
|
1257
|
+
e.stopPropagation();
|
|
1258
|
+
setDraft(annotation.body);
|
|
1259
|
+
setIsEditing(true);
|
|
1260
|
+
},
|
|
1261
|
+
className: "rounded-full p-1 text-neutral-400 hover:bg-white/[0.08] hover:text-white",
|
|
1262
|
+
children: /* @__PURE__ */ jsx11(Pencil, { className: "h-3.5 w-3.5" })
|
|
1263
|
+
}
|
|
1264
|
+
),
|
|
1265
|
+
/* @__PURE__ */ jsx11(
|
|
1266
|
+
"button",
|
|
1267
|
+
{
|
|
1268
|
+
type: "button",
|
|
1269
|
+
title: annotation.status === "open" ? "Resolve comment" : "Reopen comment",
|
|
1270
|
+
"aria-label": annotation.status === "open" ? "Resolve comment" : "Reopen comment",
|
|
1271
|
+
onClick: (e) => {
|
|
1272
|
+
e.stopPropagation();
|
|
1273
|
+
onHover(false);
|
|
1274
|
+
onUpdate({ status: annotation.status === "open" ? "resolved" : "open" });
|
|
1275
|
+
},
|
|
1276
|
+
className: "rounded-full p-1 text-neutral-400 hover:bg-white/[0.08] hover:text-white",
|
|
1277
|
+
children: annotation.status === "open" ? /* @__PURE__ */ jsx11(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx11(RotateCcw, { className: "h-3.5 w-3.5" })
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
/* @__PURE__ */ jsx11(
|
|
1281
|
+
"button",
|
|
1282
|
+
{
|
|
1283
|
+
type: "button",
|
|
1284
|
+
title: "Delete comment",
|
|
1285
|
+
"aria-label": "Delete comment",
|
|
1286
|
+
onClick: (e) => {
|
|
1287
|
+
e.stopPropagation();
|
|
1288
|
+
onDelete();
|
|
1289
|
+
},
|
|
1290
|
+
className: "rounded-full p-1 text-neutral-400 hover:bg-white/[0.08] hover:text-white",
|
|
1291
|
+
children: /* @__PURE__ */ jsx11(Trash2, { className: "h-3.5 w-3.5" })
|
|
1292
|
+
}
|
|
1293
|
+
)
|
|
1294
|
+
] }),
|
|
1090
1295
|
/* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
|
|
1091
1296
|
/* @__PURE__ */ jsx11(
|
|
1092
1297
|
"div",
|
|
@@ -1095,8 +1300,52 @@ function AnnotationItem({
|
|
|
1095
1300
|
children: number
|
|
1096
1301
|
}
|
|
1097
1302
|
),
|
|
1098
|
-
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 pr-4", children: [
|
|
1099
|
-
/* @__PURE__ */
|
|
1303
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1 pr-4", children: [
|
|
1304
|
+
isEditing ? /* @__PURE__ */ jsxs5("div", { children: [
|
|
1305
|
+
/* @__PURE__ */ jsx11(
|
|
1306
|
+
"textarea",
|
|
1307
|
+
{
|
|
1308
|
+
ref: editRef,
|
|
1309
|
+
value: draft,
|
|
1310
|
+
onClick: (e) => e.stopPropagation(),
|
|
1311
|
+
onChange: (e) => setDraft(e.target.value),
|
|
1312
|
+
onKeyDown: (e) => {
|
|
1313
|
+
if (e.key === "Escape") setIsEditing(false);
|
|
1314
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") saveEdit();
|
|
1315
|
+
},
|
|
1316
|
+
className: "w-full resize-y rounded border border-neutral-700 bg-neutral-900 p-2 text-[13px] text-white outline-none focus:border-[#FF6B35]",
|
|
1317
|
+
rows: 3,
|
|
1318
|
+
maxLength: 4096
|
|
1319
|
+
}
|
|
1320
|
+
),
|
|
1321
|
+
/* @__PURE__ */ jsxs5("div", { className: "mt-1 flex gap-2 text-xs", children: [
|
|
1322
|
+
/* @__PURE__ */ jsx11(
|
|
1323
|
+
"button",
|
|
1324
|
+
{
|
|
1325
|
+
type: "button",
|
|
1326
|
+
onClick: (e) => {
|
|
1327
|
+
e.stopPropagation();
|
|
1328
|
+
saveEdit();
|
|
1329
|
+
},
|
|
1330
|
+
disabled: !draft.trim(),
|
|
1331
|
+
className: "text-[#FF6B35] disabled:opacity-40",
|
|
1332
|
+
children: "Save"
|
|
1333
|
+
}
|
|
1334
|
+
),
|
|
1335
|
+
/* @__PURE__ */ jsx11(
|
|
1336
|
+
"button",
|
|
1337
|
+
{
|
|
1338
|
+
type: "button",
|
|
1339
|
+
onClick: (e) => {
|
|
1340
|
+
e.stopPropagation();
|
|
1341
|
+
setIsEditing(false);
|
|
1342
|
+
},
|
|
1343
|
+
className: "text-neutral-400",
|
|
1344
|
+
children: "Cancel"
|
|
1345
|
+
}
|
|
1346
|
+
)
|
|
1347
|
+
] })
|
|
1348
|
+
] }) : /* @__PURE__ */ jsx11("p", { className: "text-[13px] leading-relaxed text-neutral-200", children: annotation.body }),
|
|
1100
1349
|
annotation.target.contentNearPin && /* @__PURE__ */ jsx11("p", { className: "mt-1 truncate text-[11px] text-neutral-600", children: annotation.target.contentNearPin }),
|
|
1101
1350
|
annotation.resolution && /* @__PURE__ */ jsx11("p", { className: "mt-1.5 text-[11px] text-emerald-400/80 italic", children: annotation.resolution })
|
|
1102
1351
|
] })
|
|
@@ -1107,7 +1356,7 @@ function AnnotationItem({
|
|
|
1107
1356
|
}
|
|
1108
1357
|
|
|
1109
1358
|
// src/core/annotations/use-annotations.ts
|
|
1110
|
-
import { useCallback as useCallback4, useEffect as
|
|
1359
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef5, useState as useState6 } from "react";
|
|
1111
1360
|
|
|
1112
1361
|
// src/core/annotations/adapters/http.ts
|
|
1113
1362
|
var ENDPOINT = "/__promptslide_annotations";
|
|
@@ -1143,23 +1392,47 @@ function createHttpAdapter() {
|
|
|
1143
1392
|
async remove(id) {
|
|
1144
1393
|
cache = cache.filter((a) => a.id !== id);
|
|
1145
1394
|
await saveAll(cache);
|
|
1395
|
+
},
|
|
1396
|
+
async update(id, patch) {
|
|
1397
|
+
cache = cache.map((a) => a.id === id ? { ...a, ...patch } : a);
|
|
1398
|
+
await saveAll(cache);
|
|
1399
|
+
},
|
|
1400
|
+
async replaceAll(annotations) {
|
|
1401
|
+
cache = annotations;
|
|
1402
|
+
await saveAll(cache);
|
|
1146
1403
|
}
|
|
1147
1404
|
};
|
|
1148
1405
|
}
|
|
1149
1406
|
|
|
1150
1407
|
// src/core/annotations/use-annotations.ts
|
|
1151
|
-
function useAnnotations(adapter) {
|
|
1152
|
-
const
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1408
|
+
function useAnnotations(slidesOrAdapter, adapter) {
|
|
1409
|
+
const slides = Array.isArray(slidesOrAdapter) ? slidesOrAdapter : void 0;
|
|
1410
|
+
const storage = Array.isArray(slidesOrAdapter) ? adapter : slidesOrAdapter ?? adapter;
|
|
1411
|
+
const adapterRef = useRef5(storage ?? createHttpAdapter());
|
|
1412
|
+
const slidesRef = useRef5(slides);
|
|
1413
|
+
slidesRef.current = slides;
|
|
1414
|
+
const [annotations, setAnnotations] = useState6([]);
|
|
1415
|
+
useEffect6(() => {
|
|
1416
|
+
adapterRef.current.load().then((loaded) => {
|
|
1417
|
+
const configuredSlides = slidesRef.current;
|
|
1418
|
+
const migrated = loaded.map((annotation) => {
|
|
1419
|
+
if (annotation.slideId || !configuredSlides) return annotation;
|
|
1420
|
+
const id = configuredSlides[annotation.slideIndex]?.id;
|
|
1421
|
+
return id ? { ...annotation, slideId: id } : annotation;
|
|
1422
|
+
});
|
|
1423
|
+
setAnnotations(migrated);
|
|
1424
|
+
if (migrated.some((annotation, index) => annotation !== loaded[index])) {
|
|
1425
|
+
void adapterRef.current.replaceAll?.(migrated);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1156
1428
|
return adapterRef.current.subscribe?.(setAnnotations);
|
|
1157
1429
|
}, []);
|
|
1158
1430
|
const addAnnotation = useCallback4(
|
|
1159
|
-
(slideIndex, slideTitle, target, body) => {
|
|
1431
|
+
(slideIndex, slideTitle, target, body, slideId) => {
|
|
1160
1432
|
const annotation = {
|
|
1161
1433
|
id: crypto.randomUUID(),
|
|
1162
1434
|
slideIndex,
|
|
1435
|
+
...slideId && { slideId },
|
|
1163
1436
|
slideTitle,
|
|
1164
1437
|
target,
|
|
1165
1438
|
body,
|
|
@@ -1175,15 +1448,36 @@ function useAnnotations(adapter) {
|
|
|
1175
1448
|
setAnnotations((prev) => prev.filter((a) => a.id !== id));
|
|
1176
1449
|
adapterRef.current.remove(id);
|
|
1177
1450
|
}, []);
|
|
1451
|
+
const updateAnnotation = useCallback4(
|
|
1452
|
+
(id, patch) => {
|
|
1453
|
+
const current = annotations.find((a) => a.id === id);
|
|
1454
|
+
if (!current) return;
|
|
1455
|
+
const updated = { body: patch.body ?? current.body, status: patch.status ?? current.status };
|
|
1456
|
+
setAnnotations((prev) => prev.map((a) => a.id === id ? { ...a, ...updated } : a));
|
|
1457
|
+
void adapterRef.current.update?.(id, updated);
|
|
1458
|
+
},
|
|
1459
|
+
[annotations]
|
|
1460
|
+
);
|
|
1178
1461
|
const getSlideAnnotations = useCallback4(
|
|
1179
1462
|
(slideIndex) => annotations.filter((a) => a.slideIndex === slideIndex),
|
|
1180
1463
|
[annotations]
|
|
1181
1464
|
);
|
|
1182
|
-
const openCount = useMemo3(
|
|
1465
|
+
const openCount = useMemo3(
|
|
1466
|
+
() => annotations.filter((a) => a.status === "open").length,
|
|
1467
|
+
[annotations]
|
|
1468
|
+
);
|
|
1183
1469
|
const updateAnnotations = useCallback4((updated) => {
|
|
1184
1470
|
setAnnotations(updated);
|
|
1185
1471
|
}, []);
|
|
1186
|
-
return {
|
|
1472
|
+
return {
|
|
1473
|
+
annotations,
|
|
1474
|
+
addAnnotation,
|
|
1475
|
+
deleteAnnotation,
|
|
1476
|
+
updateAnnotation,
|
|
1477
|
+
getSlideAnnotations,
|
|
1478
|
+
openCount,
|
|
1479
|
+
updateAnnotations
|
|
1480
|
+
};
|
|
1187
1481
|
}
|
|
1188
1482
|
|
|
1189
1483
|
// src/core/utils.ts
|
|
@@ -1194,14 +1488,96 @@ function cn(...inputs) {
|
|
|
1194
1488
|
}
|
|
1195
1489
|
|
|
1196
1490
|
// src/core/slide-deck.tsx
|
|
1197
|
-
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1491
|
+
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1492
|
+
var NARROW_VIEWPORT_WIDTH = 768;
|
|
1493
|
+
var DEFAULT_THUMBNAIL_WIDTH = 240;
|
|
1494
|
+
var MIN_THUMBNAIL_WIDTH = 176;
|
|
1495
|
+
var MAX_THUMBNAIL_WIDTH = 480;
|
|
1496
|
+
var THUMBNAIL_WIDTH_STORAGE_KEY = "promptslide:thumbnail-width";
|
|
1497
|
+
function GridThumbnail({
|
|
1498
|
+
slide,
|
|
1499
|
+
index,
|
|
1500
|
+
total
|
|
1501
|
+
}) {
|
|
1502
|
+
const viewportRef = useRef6(null);
|
|
1503
|
+
const canvasRef = useRef6(null);
|
|
1504
|
+
const SlideComponent = slide.component;
|
|
1505
|
+
useLayoutEffect(() => {
|
|
1506
|
+
const viewport = viewportRef.current;
|
|
1507
|
+
const canvas = canvasRef.current;
|
|
1508
|
+
if (!viewport || !canvas) return;
|
|
1509
|
+
const resize = () => {
|
|
1510
|
+
canvas.style.transform = `scale(${viewport.clientWidth / SLIDE_DIMENSIONS.width})`;
|
|
1511
|
+
};
|
|
1512
|
+
resize();
|
|
1513
|
+
const observer = new ResizeObserver(resize);
|
|
1514
|
+
observer.observe(viewport);
|
|
1515
|
+
return () => observer.disconnect();
|
|
1516
|
+
}, []);
|
|
1517
|
+
return /* @__PURE__ */ jsx12("div", { ref: viewportRef, className: "absolute inset-0 overflow-hidden", children: /* @__PURE__ */ jsx12(
|
|
1518
|
+
"div",
|
|
1519
|
+
{
|
|
1520
|
+
ref: canvasRef,
|
|
1521
|
+
className: "absolute top-0 left-0 origin-top-left",
|
|
1522
|
+
style: { width: SLIDE_DIMENSIONS.width, height: SLIDE_DIMENSIONS.height },
|
|
1523
|
+
children: /* @__PURE__ */ jsx12(AnimationProvider, { currentStep: slide.steps, totalSteps: slide.steps, showAllAnimations: true, children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slide.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: total }) }) })
|
|
1524
|
+
}
|
|
1525
|
+
) });
|
|
1526
|
+
}
|
|
1527
|
+
function ListSlide({ slide, index, total }) {
|
|
1528
|
+
const viewportRef = useRef6(null);
|
|
1529
|
+
const canvasRef = useRef6(null);
|
|
1530
|
+
const SlideComponent = slide.component;
|
|
1531
|
+
useLayoutEffect(() => {
|
|
1532
|
+
const viewport = viewportRef.current;
|
|
1533
|
+
const canvas = canvasRef.current;
|
|
1534
|
+
if (!viewport || !canvas) return;
|
|
1535
|
+
const resize = () => {
|
|
1536
|
+
canvas.style.transform = `scale(${viewport.clientWidth / SLIDE_DIMENSIONS.width})`;
|
|
1537
|
+
};
|
|
1538
|
+
resize();
|
|
1539
|
+
const observer = new ResizeObserver(resize);
|
|
1540
|
+
observer.observe(viewport);
|
|
1541
|
+
return () => observer.disconnect();
|
|
1542
|
+
}, []);
|
|
1543
|
+
return /* @__PURE__ */ jsx12(
|
|
1544
|
+
"div",
|
|
1545
|
+
{
|
|
1546
|
+
ref: viewportRef,
|
|
1547
|
+
className: "relative aspect-video w-full overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-sm print:m-0 print:h-[1080px] print:w-[1920px] print:break-after-page print:rounded-none print:border-0 print:shadow-none",
|
|
1548
|
+
children: /* @__PURE__ */ jsx12(
|
|
1549
|
+
"div",
|
|
1550
|
+
{
|
|
1551
|
+
ref: canvasRef,
|
|
1552
|
+
className: "list-slide-canvas absolute top-0 left-0 origin-top-left",
|
|
1553
|
+
style: { width: SLIDE_DIMENSIONS.width, height: SLIDE_DIMENSIONS.height },
|
|
1554
|
+
children: /* @__PURE__ */ jsx12(AnimationProvider, { currentStep: slide.steps, totalSteps: slide.steps, showAllAnimations: true, children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slide.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: total }) }) })
|
|
1555
|
+
}
|
|
1556
|
+
)
|
|
1557
|
+
}
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1198
1560
|
function SlideExportView({ slides, slideIndex }) {
|
|
1199
|
-
const [ready, setReady] =
|
|
1561
|
+
const [ready, setReady] = useState7(false);
|
|
1200
1562
|
const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1));
|
|
1201
1563
|
const slideConfig = slides[clampedIndex];
|
|
1202
1564
|
const SlideComponent = slideConfig.component;
|
|
1203
|
-
|
|
1204
|
-
|
|
1565
|
+
useEffect7(() => {
|
|
1566
|
+
let cancelled = false;
|
|
1567
|
+
const markReady = async () => {
|
|
1568
|
+
await document.fonts.ready;
|
|
1569
|
+
await Promise.all(Array.from(document.images, (image) => image.decode().catch(() => {
|
|
1570
|
+
})));
|
|
1571
|
+
requestAnimationFrame(
|
|
1572
|
+
() => requestAnimationFrame(() => {
|
|
1573
|
+
if (!cancelled) setReady(true);
|
|
1574
|
+
})
|
|
1575
|
+
);
|
|
1576
|
+
};
|
|
1577
|
+
void markReady();
|
|
1578
|
+
return () => {
|
|
1579
|
+
cancelled = true;
|
|
1580
|
+
};
|
|
1205
1581
|
}, []);
|
|
1206
1582
|
return /* @__PURE__ */ jsx12(
|
|
1207
1583
|
"div",
|
|
@@ -1226,8 +1602,16 @@ function SlideExportView({ slides, slideIndex }) {
|
|
|
1226
1602
|
}
|
|
1227
1603
|
);
|
|
1228
1604
|
}
|
|
1229
|
-
function SlideDeck({
|
|
1230
|
-
|
|
1605
|
+
function SlideDeck({
|
|
1606
|
+
slides,
|
|
1607
|
+
transition,
|
|
1608
|
+
directionalTransition,
|
|
1609
|
+
annotations,
|
|
1610
|
+
onAnnotationAdd,
|
|
1611
|
+
onAnnotationDelete,
|
|
1612
|
+
onAnnotationUpdate
|
|
1613
|
+
}) {
|
|
1614
|
+
const [exportParams] = useState7(() => {
|
|
1231
1615
|
if (typeof window === "undefined") return null;
|
|
1232
1616
|
const params = new URLSearchParams(window.location.search);
|
|
1233
1617
|
if (params.get("export") !== "true") return null;
|
|
@@ -1236,24 +1620,158 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1236
1620
|
if (exportParams) {
|
|
1237
1621
|
return /* @__PURE__ */ jsx12(SlideExportView, { slides, slideIndex: exportParams.slideIndex });
|
|
1238
1622
|
}
|
|
1239
|
-
const internal = useAnnotations();
|
|
1623
|
+
const internal = useAnnotations(slides);
|
|
1240
1624
|
const isExternallyManaged = annotations !== void 0;
|
|
1241
1625
|
const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations;
|
|
1242
1626
|
const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation;
|
|
1243
1627
|
const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation;
|
|
1244
|
-
const
|
|
1245
|
-
const
|
|
1246
|
-
(
|
|
1628
|
+
const effectiveUpdate = isExternallyManaged ? onAnnotationUpdate : internal.updateAnnotation;
|
|
1629
|
+
const openCount = useMemo4(
|
|
1630
|
+
() => effectiveAnnotations.filter((a) => a.status === "open").length,
|
|
1247
1631
|
[effectiveAnnotations]
|
|
1248
1632
|
);
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1633
|
+
const getSlideAnnotations = useCallback5(
|
|
1634
|
+
(slideIndex) => effectiveAnnotations.filter(
|
|
1635
|
+
(a) => a.slideId ? a.slideId === slides[slideIndex]?.id : a.slideIndex === slideIndex
|
|
1636
|
+
),
|
|
1637
|
+
[effectiveAnnotations, slides]
|
|
1638
|
+
);
|
|
1639
|
+
const unlinkedAnnotations = useMemo4(
|
|
1640
|
+
() => effectiveAnnotations.filter((a) => a.slideId && !slides.some((slide) => slide.id === a.slideId)),
|
|
1641
|
+
[effectiveAnnotations, slides]
|
|
1642
|
+
);
|
|
1643
|
+
const [viewMode, setViewMode] = useState7("slide");
|
|
1644
|
+
const [isPresentationMode, setIsPresentationMode] = useState7(false);
|
|
1645
|
+
const [viewportWidth, setViewportWidth] = useState7(
|
|
1646
|
+
() => typeof window === "undefined" ? 1280 : window.innerWidth
|
|
1647
|
+
);
|
|
1648
|
+
const [showThumbnailRail, setShowThumbnailRail] = useState7(
|
|
1649
|
+
() => typeof window === "undefined" ? true : window.innerWidth >= NARROW_VIEWPORT_WIDTH
|
|
1650
|
+
);
|
|
1651
|
+
const [thumbnailWidth, setThumbnailWidth] = useState7(() => {
|
|
1652
|
+
if (typeof window === "undefined") return DEFAULT_THUMBNAIL_WIDTH;
|
|
1653
|
+
const savedWidth = Number(window.localStorage.getItem(THUMBNAIL_WIDTH_STORAGE_KEY));
|
|
1654
|
+
return Number.isFinite(savedWidth) && savedWidth >= MIN_THUMBNAIL_WIDTH ? Math.min(savedWidth, MAX_THUMBNAIL_WIDTH) : DEFAULT_THUMBNAIL_WIDTH;
|
|
1655
|
+
});
|
|
1656
|
+
const [isAnnotationMode, setIsAnnotationMode] = useState7(false);
|
|
1657
|
+
const [showAnnotationPanel, setShowAnnotationPanel] = useState7(false);
|
|
1658
|
+
const [selectedAnnotationId, setSelectedAnnotationId] = useState7(null);
|
|
1659
|
+
const [hoveredAnnotationId, setHoveredAnnotationId] = useState7(null);
|
|
1660
|
+
const [isResizingThumbnails, setIsResizingThumbnails] = useState7(false);
|
|
1661
|
+
const [isDragCollapsed, setIsDragCollapsed] = useState7(false);
|
|
1662
|
+
const [isAnimatingDragBoundary, setIsAnimatingDragBoundary] = useState7(false);
|
|
1663
|
+
const [scale, setScale] = useState7(1);
|
|
1664
|
+
const containerRef = useRef6(null);
|
|
1665
|
+
const slideContainerRef = useRef6(null);
|
|
1666
|
+
const previewViewportRef = useRef6(null);
|
|
1667
|
+
const activeThumbnailRef = useRef6(null);
|
|
1668
|
+
const preferredRailOpenRef = useRef6(true);
|
|
1669
|
+
const wasNarrowViewportRef = useRef6(viewportWidth < NARROW_VIEWPORT_WIDTH);
|
|
1670
|
+
const resizeStartRef = useRef6(null);
|
|
1671
|
+
const previousUserSelectRef = useRef6("");
|
|
1672
|
+
const previousCursorRef = useRef6("");
|
|
1673
|
+
const dragCollapsedRef = useRef6(false);
|
|
1674
|
+
const dragAnimationTimerRef = useRef6(null);
|
|
1675
|
+
const isNarrowViewport = viewportWidth < NARROW_VIEWPORT_WIDTH;
|
|
1676
|
+
const maxThumbnailWidth = isNarrowViewport ? Math.max(120, Math.floor(viewportWidth * 0.8)) : Math.min(MAX_THUMBNAIL_WIDTH, viewportWidth - 360);
|
|
1677
|
+
const minThumbnailWidth = Math.min(MIN_THUMBNAIL_WIDTH, maxThumbnailWidth);
|
|
1678
|
+
const visibleThumbnailWidth = Math.min(thumbnailWidth, maxThumbnailWidth);
|
|
1679
|
+
const isThumbnailRailVisible = showThumbnailRail && !isDragCollapsed;
|
|
1680
|
+
const setThumbnailRailOpen = useCallback5((open) => {
|
|
1681
|
+
preferredRailOpenRef.current = open;
|
|
1682
|
+
setShowThumbnailRail(open);
|
|
1683
|
+
}, []);
|
|
1684
|
+
useEffect7(() => {
|
|
1685
|
+
const handleResize = () => setViewportWidth(window.innerWidth);
|
|
1686
|
+
window.addEventListener("resize", handleResize);
|
|
1687
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
1688
|
+
}, []);
|
|
1689
|
+
useEffect7(() => {
|
|
1690
|
+
if (isNarrowViewport !== wasNarrowViewportRef.current) {
|
|
1691
|
+
setShowThumbnailRail(isNarrowViewport ? false : preferredRailOpenRef.current);
|
|
1692
|
+
if (isNarrowViewport) {
|
|
1693
|
+
setIsAnnotationMode(false);
|
|
1694
|
+
setShowAnnotationPanel(false);
|
|
1695
|
+
setSelectedAnnotationId(null);
|
|
1696
|
+
}
|
|
1697
|
+
wasNarrowViewportRef.current = isNarrowViewport;
|
|
1698
|
+
}
|
|
1699
|
+
}, [isNarrowViewport]);
|
|
1700
|
+
useEffect7(() => {
|
|
1701
|
+
window.localStorage.setItem(THUMBNAIL_WIDTH_STORAGE_KEY, String(thumbnailWidth));
|
|
1702
|
+
}, [thumbnailWidth]);
|
|
1703
|
+
useEffect7(
|
|
1704
|
+
() => () => {
|
|
1705
|
+
if (resizeStartRef.current) {
|
|
1706
|
+
document.body.style.userSelect = previousUserSelectRef.current;
|
|
1707
|
+
document.body.style.cursor = previousCursorRef.current;
|
|
1708
|
+
}
|
|
1709
|
+
if (dragAnimationTimerRef.current) clearTimeout(dragAnimationTimerRef.current);
|
|
1710
|
+
},
|
|
1711
|
+
[]
|
|
1712
|
+
);
|
|
1713
|
+
useEffect7(() => {
|
|
1714
|
+
if ((!showThumbnailRail || isPresentationMode || viewMode !== "slide") && resizeStartRef.current) {
|
|
1715
|
+
resizeStartRef.current = null;
|
|
1716
|
+
dragCollapsedRef.current = false;
|
|
1717
|
+
setIsResizingThumbnails(false);
|
|
1718
|
+
setIsDragCollapsed(false);
|
|
1719
|
+
setIsAnimatingDragBoundary(false);
|
|
1720
|
+
if (dragAnimationTimerRef.current) clearTimeout(dragAnimationTimerRef.current);
|
|
1721
|
+
document.body.style.userSelect = previousUserSelectRef.current;
|
|
1722
|
+
document.body.style.cursor = previousCursorRef.current;
|
|
1723
|
+
}
|
|
1724
|
+
}, [showThumbnailRail, isPresentationMode, viewMode]);
|
|
1725
|
+
useEffect7(() => {
|
|
1726
|
+
if (!isResizingThumbnails) return;
|
|
1727
|
+
const getDraggedWidth = (clientX) => {
|
|
1728
|
+
const start = resizeStartRef.current;
|
|
1729
|
+
return start ? start.width + clientX - start.pointerX : null;
|
|
1730
|
+
};
|
|
1731
|
+
const handlePointerMove = (event) => {
|
|
1732
|
+
if (event.pointerId !== resizeStartRef.current?.pointerId) return;
|
|
1733
|
+
const nextWidth = getDraggedWidth(event.clientX);
|
|
1734
|
+
if (nextWidth === null) return;
|
|
1735
|
+
const collapsed = nextWidth < minThumbnailWidth;
|
|
1736
|
+
if (collapsed !== dragCollapsedRef.current) {
|
|
1737
|
+
dragCollapsedRef.current = collapsed;
|
|
1738
|
+
setIsDragCollapsed(collapsed);
|
|
1739
|
+
setIsAnimatingDragBoundary(true);
|
|
1740
|
+
if (dragAnimationTimerRef.current) clearTimeout(dragAnimationTimerRef.current);
|
|
1741
|
+
dragAnimationTimerRef.current = setTimeout(() => {
|
|
1742
|
+
setIsAnimatingDragBoundary(false);
|
|
1743
|
+
dragAnimationTimerRef.current = null;
|
|
1744
|
+
}, 200);
|
|
1745
|
+
}
|
|
1746
|
+
if (!collapsed) setThumbnailWidth(Math.min(maxThumbnailWidth, nextWidth));
|
|
1747
|
+
};
|
|
1748
|
+
const stopResize = (event) => {
|
|
1749
|
+
if (event && event.pointerId !== resizeStartRef.current?.pointerId) return;
|
|
1750
|
+
const nextWidth = event ? getDraggedWidth(event.clientX) : null;
|
|
1751
|
+
const collapsed = event?.type === "pointerup" && nextWidth !== null && nextWidth < minThumbnailWidth;
|
|
1752
|
+
resizeStartRef.current = null;
|
|
1753
|
+
dragCollapsedRef.current = false;
|
|
1754
|
+
document.body.style.userSelect = previousUserSelectRef.current;
|
|
1755
|
+
document.body.style.cursor = previousCursorRef.current;
|
|
1756
|
+
setIsResizingThumbnails(false);
|
|
1757
|
+
setIsDragCollapsed(false);
|
|
1758
|
+
if (collapsed) setThumbnailRailOpen(false);
|
|
1759
|
+
else if (nextWidth !== null) {
|
|
1760
|
+
setThumbnailWidth(Math.max(minThumbnailWidth, Math.min(maxThumbnailWidth, nextWidth)));
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
const handleBlur = () => stopResize();
|
|
1764
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
1765
|
+
window.addEventListener("pointerup", stopResize);
|
|
1766
|
+
window.addEventListener("pointercancel", stopResize);
|
|
1767
|
+
window.addEventListener("blur", handleBlur);
|
|
1768
|
+
return () => {
|
|
1769
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
1770
|
+
window.removeEventListener("pointerup", stopResize);
|
|
1771
|
+
window.removeEventListener("pointercancel", stopResize);
|
|
1772
|
+
window.removeEventListener("blur", handleBlur);
|
|
1773
|
+
};
|
|
1774
|
+
}, [isResizingThumbnails, minThumbnailWidth, maxThumbnailWidth, setThumbnailRailOpen]);
|
|
1257
1775
|
const {
|
|
1258
1776
|
currentSlide,
|
|
1259
1777
|
animationStep,
|
|
@@ -1274,22 +1792,27 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1274
1792
|
await document.exitFullscreen();
|
|
1275
1793
|
}
|
|
1276
1794
|
}, []);
|
|
1277
|
-
|
|
1795
|
+
useEffect7(() => {
|
|
1278
1796
|
const handleFullscreenChange = () => {
|
|
1279
1797
|
setIsPresentationMode(!!document.fullscreenElement);
|
|
1280
1798
|
};
|
|
1281
1799
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
1282
1800
|
return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
1283
1801
|
}, []);
|
|
1284
|
-
|
|
1802
|
+
useEffect7(() => {
|
|
1803
|
+
if (viewMode === "slide" && !isPresentationMode && showThumbnailRail) {
|
|
1804
|
+
activeThumbnailRef.current?.scrollIntoView({ block: "nearest" });
|
|
1805
|
+
}
|
|
1806
|
+
}, [currentSlide, isPresentationMode, showThumbnailRail, viewMode]);
|
|
1807
|
+
useEffect7(() => {
|
|
1285
1808
|
const calculateScale = () => {
|
|
1286
1809
|
if (!isPresentationMode) {
|
|
1287
1810
|
setScale(1);
|
|
1288
1811
|
return;
|
|
1289
1812
|
}
|
|
1290
|
-
const
|
|
1813
|
+
const availableWidth = window.innerWidth;
|
|
1291
1814
|
const viewportHeight = window.innerHeight;
|
|
1292
|
-
const scaleX =
|
|
1815
|
+
const scaleX = availableWidth / SLIDE_DIMENSIONS.width;
|
|
1293
1816
|
const scaleY = viewportHeight / SLIDE_DIMENSIONS.height;
|
|
1294
1817
|
setScale(Math.min(scaleX, scaleY));
|
|
1295
1818
|
};
|
|
@@ -1297,7 +1820,46 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1297
1820
|
window.addEventListener("resize", calculateScale);
|
|
1298
1821
|
return () => window.removeEventListener("resize", calculateScale);
|
|
1299
1822
|
}, [isPresentationMode]);
|
|
1300
|
-
|
|
1823
|
+
useLayoutEffect(() => {
|
|
1824
|
+
const viewport = previewViewportRef.current;
|
|
1825
|
+
const canvas = slideContainerRef.current;
|
|
1826
|
+
if (!viewport || !canvas) return;
|
|
1827
|
+
const resize = (width, height) => {
|
|
1828
|
+
const fitScale = Math.min(width / SLIDE_DIMENSIONS.width, height / SLIDE_DIMENSIONS.height);
|
|
1829
|
+
canvas.style.transform = `scale(${fitScale})`;
|
|
1830
|
+
};
|
|
1831
|
+
resize(viewport.clientWidth, viewport.clientHeight);
|
|
1832
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
1833
|
+
if (!entry) return;
|
|
1834
|
+
resize(entry.contentRect.width, entry.contentRect.height);
|
|
1835
|
+
});
|
|
1836
|
+
observer.observe(viewport);
|
|
1837
|
+
return () => observer.disconnect();
|
|
1838
|
+
}, [viewMode, isPresentationMode]);
|
|
1839
|
+
const toggleCommentMode = useCallback5(() => {
|
|
1840
|
+
const next = !isAnnotationMode;
|
|
1841
|
+
setIsAnnotationMode(next);
|
|
1842
|
+
setShowAnnotationPanel(next);
|
|
1843
|
+
if (!next) {
|
|
1844
|
+
setSelectedAnnotationId(null);
|
|
1845
|
+
setHoveredAnnotationId(null);
|
|
1846
|
+
}
|
|
1847
|
+
}, [isAnnotationMode]);
|
|
1848
|
+
const handleExportPdf = useCallback5(async () => {
|
|
1849
|
+
try {
|
|
1850
|
+
const response = await fetch("/__promptslide_pdf");
|
|
1851
|
+
if (response.ok && response.headers.get("content-type")?.includes("application/pdf")) {
|
|
1852
|
+
const blob = await response.blob();
|
|
1853
|
+
const url = URL.createObjectURL(blob);
|
|
1854
|
+
const link = document.createElement("a");
|
|
1855
|
+
link.href = url;
|
|
1856
|
+
link.download = "slides.pdf";
|
|
1857
|
+
link.click();
|
|
1858
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
} catch {
|
|
1862
|
+
}
|
|
1301
1863
|
const previousMode = viewMode;
|
|
1302
1864
|
setViewMode("list");
|
|
1303
1865
|
setTimeout(() => {
|
|
@@ -1308,17 +1870,49 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1308
1870
|
window.addEventListener("afterprint", handleAfterPrint);
|
|
1309
1871
|
window.print();
|
|
1310
1872
|
}, 100);
|
|
1311
|
-
};
|
|
1312
|
-
|
|
1873
|
+
}, [viewMode]);
|
|
1874
|
+
useEffect7(() => {
|
|
1313
1875
|
const handleKeyDown = (e) => {
|
|
1314
|
-
|
|
1315
|
-
|
|
1876
|
+
const target = e.target;
|
|
1877
|
+
if (target?.closest("input, textarea, [contenteditable='true']")) return;
|
|
1878
|
+
if (e.altKey || e.ctrlKey || e.metaKey) return;
|
|
1879
|
+
const key = e.key.toLowerCase();
|
|
1880
|
+
if (e.repeat && "fvglcdt".includes(key)) return;
|
|
1881
|
+
if (key === "f") {
|
|
1882
|
+
e.preventDefault();
|
|
1883
|
+
void togglePresentationMode();
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
if (key === "v" && !isPresentationMode) {
|
|
1887
|
+
e.preventDefault();
|
|
1888
|
+
setViewMode("slide");
|
|
1316
1889
|
return;
|
|
1317
1890
|
}
|
|
1318
|
-
if (
|
|
1891
|
+
if (key === "g") {
|
|
1892
|
+
e.preventDefault();
|
|
1319
1893
|
setViewMode((prev) => prev === "grid" ? "slide" : "grid");
|
|
1320
1894
|
return;
|
|
1321
1895
|
}
|
|
1896
|
+
if (key === "l") {
|
|
1897
|
+
e.preventDefault();
|
|
1898
|
+
setViewMode((prev) => prev === "list" ? "slide" : "list");
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
if (key === "c" && !isPresentationMode) {
|
|
1902
|
+
e.preventDefault();
|
|
1903
|
+
toggleCommentMode();
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
if (key === "d" && !isPresentationMode) {
|
|
1907
|
+
e.preventDefault();
|
|
1908
|
+
void handleExportPdf();
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
if (viewMode === "slide" && !isPresentationMode && key === "t") {
|
|
1912
|
+
e.preventDefault();
|
|
1913
|
+
setThumbnailRailOpen(!showThumbnailRail);
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1322
1916
|
if (viewMode !== "slide" || isAnnotationMode && !isPresentationMode) return;
|
|
1323
1917
|
if (e.key === "ArrowRight" || e.key === " ") {
|
|
1324
1918
|
e.preventDefault();
|
|
@@ -1330,10 +1924,24 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1330
1924
|
};
|
|
1331
1925
|
window.addEventListener("keydown", handleKeyDown);
|
|
1332
1926
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1333
|
-
}, [
|
|
1927
|
+
}, [
|
|
1928
|
+
advance,
|
|
1929
|
+
goBack,
|
|
1930
|
+
viewMode,
|
|
1931
|
+
togglePresentationMode,
|
|
1932
|
+
isAnnotationMode,
|
|
1933
|
+
isPresentationMode,
|
|
1934
|
+
setThumbnailRailOpen,
|
|
1935
|
+
showThumbnailRail,
|
|
1936
|
+
toggleCommentMode,
|
|
1937
|
+
handleExportPdf
|
|
1938
|
+
]);
|
|
1334
1939
|
return /* @__PURE__ */ jsxs6("div", { className: "min-h-screen w-full bg-neutral-950 text-foreground", children: [
|
|
1335
1940
|
/* @__PURE__ */ jsx12("style", { children: `
|
|
1336
1941
|
@media print {
|
|
1942
|
+
.list-slide-canvas {
|
|
1943
|
+
transform: scale(1.5) !important;
|
|
1944
|
+
}
|
|
1337
1945
|
@page {
|
|
1338
1946
|
size: 1920px 1080px;
|
|
1339
1947
|
margin: 0;
|
|
@@ -1357,9 +1965,9 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1357
1965
|
"div",
|
|
1358
1966
|
{
|
|
1359
1967
|
className: cn(
|
|
1360
|
-
"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",
|
|
1361
|
-
isPresentationMode && "hidden",
|
|
1362
|
-
isAnnotationMode && showAnnotationPanel ? "right-[
|
|
1968
|
+
"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] duration-200 ease-out print:hidden",
|
|
1969
|
+
(isPresentationMode || isNarrowViewport && isAnnotationMode && showAnnotationPanel) && "hidden",
|
|
1970
|
+
isAnnotationMode && showAnnotationPanel ? "right-[21rem]" : "right-4"
|
|
1363
1971
|
),
|
|
1364
1972
|
children: [
|
|
1365
1973
|
/* @__PURE__ */ jsx12(
|
|
@@ -1370,7 +1978,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1370
1978
|
"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
1371
1979
|
viewMode === "slide" && "bg-neutral-800 text-white"
|
|
1372
1980
|
),
|
|
1373
|
-
title: "Presentation View",
|
|
1981
|
+
title: "Presentation View (V)",
|
|
1374
1982
|
children: /* @__PURE__ */ jsx12(Monitor, { className: "h-4 w-4" })
|
|
1375
1983
|
}
|
|
1376
1984
|
),
|
|
@@ -1382,7 +1990,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1382
1990
|
"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
1383
1991
|
viewMode === "list" && "bg-neutral-800 text-white"
|
|
1384
1992
|
),
|
|
1385
|
-
title: "List View",
|
|
1993
|
+
title: "List View (L)",
|
|
1386
1994
|
children: /* @__PURE__ */ jsx12(List, { className: "h-4 w-4" })
|
|
1387
1995
|
}
|
|
1388
1996
|
),
|
|
@@ -1394,7 +2002,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1394
2002
|
"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
1395
2003
|
viewMode === "grid" && "bg-neutral-800 text-white"
|
|
1396
2004
|
),
|
|
1397
|
-
title: "Grid View",
|
|
2005
|
+
title: "Grid View (G)",
|
|
1398
2006
|
children: /* @__PURE__ */ jsx12(Grid3X3, { className: "h-4 w-4" })
|
|
1399
2007
|
}
|
|
1400
2008
|
),
|
|
@@ -1402,21 +2010,14 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1402
2010
|
/* @__PURE__ */ jsxs6(
|
|
1403
2011
|
"button",
|
|
1404
2012
|
{
|
|
1405
|
-
onClick:
|
|
1406
|
-
setIsAnnotationMode((prev) => {
|
|
1407
|
-
const next = !prev;
|
|
1408
|
-
setShowAnnotationPanel(next);
|
|
1409
|
-
if (!next) setSelectedAnnotationId(null);
|
|
1410
|
-
return next;
|
|
1411
|
-
});
|
|
1412
|
-
},
|
|
2013
|
+
onClick: toggleCommentMode,
|
|
1413
2014
|
className: cn(
|
|
1414
|
-
"relative
|
|
1415
|
-
isAnnotationMode
|
|
2015
|
+
"relative inline-flex h-8 items-center justify-center rounded-md transition-colors",
|
|
2016
|
+
isAnnotationMode ? "border border-[#FF6B35]/50 bg-[#FF6B35]/15 p-[7px] text-[#FF6B35] hover:border-[#FF6B35] hover:bg-[#FF6B35]/25" : "p-2 text-neutral-400 hover:bg-neutral-800 hover:text-white"
|
|
1416
2017
|
),
|
|
1417
|
-
title: "
|
|
2018
|
+
title: "Comment (C)",
|
|
1418
2019
|
children: [
|
|
1419
|
-
/* @__PURE__ */ jsx12(
|
|
2020
|
+
/* @__PURE__ */ jsx12(MessageCircle, { className: "h-4 w-4" }),
|
|
1420
2021
|
openCount > 0 && /* @__PURE__ */ jsx12("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", children: openCount })
|
|
1421
2022
|
]
|
|
1422
2023
|
}
|
|
@@ -1427,17 +2028,20 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1427
2028
|
{
|
|
1428
2029
|
onClick: handleExportPdf,
|
|
1429
2030
|
className: "rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
1430
|
-
title: "Download PDF",
|
|
2031
|
+
title: "Download PDF (D)",
|
|
1431
2032
|
children: /* @__PURE__ */ jsx12(Download, { className: "h-4 w-4" })
|
|
1432
2033
|
}
|
|
1433
2034
|
),
|
|
1434
|
-
/* @__PURE__ */
|
|
2035
|
+
/* @__PURE__ */ jsxs6(
|
|
1435
2036
|
"button",
|
|
1436
2037
|
{
|
|
1437
2038
|
onClick: togglePresentationMode,
|
|
1438
|
-
className: "rounded-md
|
|
2039
|
+
className: "inline-flex h-8 items-center justify-center gap-1.5 rounded-md border border-[#FF6B35]/50 bg-[#FF6B35]/15 px-3 text-sm font-semibold text-[#FF6B35] transition-colors hover:border-[#FF6B35] hover:bg-[#FF6B35]/25",
|
|
1439
2040
|
title: "Present (F)",
|
|
1440
|
-
children:
|
|
2041
|
+
children: [
|
|
2042
|
+
/* @__PURE__ */ jsx12(Play, { className: "h-3.5 w-3.5 fill-current" }),
|
|
2043
|
+
/* @__PURE__ */ jsx12("span", { children: "Present" })
|
|
2044
|
+
]
|
|
1441
2045
|
}
|
|
1442
2046
|
)
|
|
1443
2047
|
]
|
|
@@ -1446,11 +2050,157 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1446
2050
|
viewMode === "slide" && /* @__PURE__ */ jsxs6(
|
|
1447
2051
|
"div",
|
|
1448
2052
|
{
|
|
1449
|
-
className: cn(
|
|
1450
|
-
"flex h-screen w-full print:hidden",
|
|
1451
|
-
isPresentationMode ? "bg-black" : ""
|
|
1452
|
-
),
|
|
2053
|
+
className: cn("flex h-screen w-full print:hidden", isPresentationMode ? "bg-black" : ""),
|
|
1453
2054
|
children: [
|
|
2055
|
+
!isPresentationMode && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2056
|
+
/* @__PURE__ */ jsxs6(
|
|
2057
|
+
"div",
|
|
2058
|
+
{
|
|
2059
|
+
className: cn(
|
|
2060
|
+
"relative z-10 h-screen shrink-0",
|
|
2061
|
+
isNarrowViewport && "fixed inset-y-0 left-0",
|
|
2062
|
+
isResizingThumbnails && !isAnimatingDragBoundary ? "transition-none" : "transition-[width] duration-200 ease-out motion-reduce:transition-none"
|
|
2063
|
+
),
|
|
2064
|
+
style: { width: isThumbnailRailVisible ? visibleThumbnailWidth : 0 },
|
|
2065
|
+
children: [
|
|
2066
|
+
/* @__PURE__ */ jsx12("div", { className: "h-full w-full overflow-hidden", children: /* @__PURE__ */ jsxs6(
|
|
2067
|
+
"aside",
|
|
2068
|
+
{
|
|
2069
|
+
id: "presentation-thumbnails",
|
|
2070
|
+
"aria-label": "Slide thumbnails",
|
|
2071
|
+
"aria-hidden": !isThumbnailRailVisible,
|
|
2072
|
+
inert: !isThumbnailRailVisible,
|
|
2073
|
+
className: cn(
|
|
2074
|
+
"flex h-screen shrink-0 flex-col border-r border-neutral-800 bg-neutral-950 text-white transition-opacity motion-reduce:transition-none",
|
|
2075
|
+
isThumbnailRailVisible ? "opacity-100 delay-100 duration-100" : "opacity-0 duration-75"
|
|
2076
|
+
),
|
|
2077
|
+
style: { width: visibleThumbnailWidth },
|
|
2078
|
+
children: [
|
|
2079
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between border-b border-neutral-800 px-3 py-2", children: [
|
|
2080
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-sm font-semibold", children: [
|
|
2081
|
+
"Slides (",
|
|
2082
|
+
slides.length,
|
|
2083
|
+
")"
|
|
2084
|
+
] }),
|
|
2085
|
+
/* @__PURE__ */ jsx12(
|
|
2086
|
+
"button",
|
|
2087
|
+
{
|
|
2088
|
+
type: "button",
|
|
2089
|
+
onClick: () => setThumbnailRailOpen(false),
|
|
2090
|
+
className: "rounded p-2 text-neutral-400 hover:bg-neutral-800 hover:text-white",
|
|
2091
|
+
"aria-label": "Hide slide thumbnails",
|
|
2092
|
+
title: "Hide thumbnails (T)",
|
|
2093
|
+
children: /* @__PURE__ */ jsx12(PanelLeftClose, { className: "h-4 w-4" })
|
|
2094
|
+
}
|
|
2095
|
+
)
|
|
2096
|
+
] }),
|
|
2097
|
+
/* @__PURE__ */ jsx12("div", { className: "min-h-0 flex-1 space-y-3 overflow-y-auto p-3", children: slides.map((slideConfig, index) => /* @__PURE__ */ jsxs6(
|
|
2098
|
+
"button",
|
|
2099
|
+
{
|
|
2100
|
+
ref: index === currentSlide ? activeThumbnailRef : void 0,
|
|
2101
|
+
type: "button",
|
|
2102
|
+
onClick: () => goToSlide(index),
|
|
2103
|
+
"aria-label": `Go to slide ${index + 1}${slideConfig.title ? `: ${slideConfig.title}` : ""}`,
|
|
2104
|
+
"aria-current": index === currentSlide ? "page" : void 0,
|
|
2105
|
+
className: cn(
|
|
2106
|
+
"block w-full rounded-lg border p-1.5 text-left transition-colors focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#FF6B35]",
|
|
2107
|
+
index === currentSlide ? "border-[#FF6B35] bg-[#FF6B35]/15 ring-1 ring-[#FF6B35]" : "border-transparent hover:border-neutral-600 hover:bg-neutral-800"
|
|
2108
|
+
),
|
|
2109
|
+
children: [
|
|
2110
|
+
/* @__PURE__ */ jsx12("div", { className: "relative aspect-video w-full overflow-hidden rounded bg-black", children: /* @__PURE__ */ jsx12(
|
|
2111
|
+
GridThumbnail,
|
|
2112
|
+
{
|
|
2113
|
+
slide: slideConfig,
|
|
2114
|
+
index,
|
|
2115
|
+
total: slides.length
|
|
2116
|
+
}
|
|
2117
|
+
) }),
|
|
2118
|
+
/* @__PURE__ */ jsx12("div", { className: "mt-1.5 flex items-baseline gap-2 px-0.5 text-xs", children: slideConfig.title ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2119
|
+
/* @__PURE__ */ jsx12("span", { className: "font-mono text-neutral-400", children: index + 1 }),
|
|
2120
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: slideConfig.title })
|
|
2121
|
+
] }) : /* @__PURE__ */ jsxs6("span", { children: [
|
|
2122
|
+
"Slide ",
|
|
2123
|
+
index + 1
|
|
2124
|
+
] }) })
|
|
2125
|
+
]
|
|
2126
|
+
},
|
|
2127
|
+
slideConfig.id ?? index
|
|
2128
|
+
)) })
|
|
2129
|
+
]
|
|
2130
|
+
}
|
|
2131
|
+
) }),
|
|
2132
|
+
isThumbnailRailVisible && /* @__PURE__ */ jsx12(
|
|
2133
|
+
"div",
|
|
2134
|
+
{
|
|
2135
|
+
role: "separator",
|
|
2136
|
+
"aria-label": "Resize slide thumbnails",
|
|
2137
|
+
"aria-orientation": "vertical",
|
|
2138
|
+
"aria-valuemin": minThumbnailWidth,
|
|
2139
|
+
"aria-valuemax": maxThumbnailWidth,
|
|
2140
|
+
"aria-valuenow": visibleThumbnailWidth,
|
|
2141
|
+
tabIndex: 0,
|
|
2142
|
+
className: cn(
|
|
2143
|
+
"absolute inset-y-0 -right-1 w-2 cursor-col-resize touch-none focus-visible:outline-none after:pointer-events-none after:absolute after:inset-y-0 after:right-1 after:w-0.5 after:bg-transparent after:content-[''] after:transition-colors hover:after:bg-[#FF6B35]/50 focus-visible:after:bg-[#FF6B35]/50",
|
|
2144
|
+
isResizingThumbnails && "after:bg-[#FF6B35]/60"
|
|
2145
|
+
),
|
|
2146
|
+
onPointerDown: (event) => {
|
|
2147
|
+
if (event.button !== 0) return;
|
|
2148
|
+
event.preventDefault();
|
|
2149
|
+
resizeStartRef.current = {
|
|
2150
|
+
pointerId: event.pointerId,
|
|
2151
|
+
pointerX: event.clientX,
|
|
2152
|
+
width: visibleThumbnailWidth
|
|
2153
|
+
};
|
|
2154
|
+
dragCollapsedRef.current = false;
|
|
2155
|
+
setIsAnimatingDragBoundary(false);
|
|
2156
|
+
if (dragAnimationTimerRef.current) clearTimeout(dragAnimationTimerRef.current);
|
|
2157
|
+
setIsResizingThumbnails(true);
|
|
2158
|
+
previousUserSelectRef.current = document.body.style.userSelect;
|
|
2159
|
+
previousCursorRef.current = document.body.style.cursor;
|
|
2160
|
+
document.body.style.userSelect = "none";
|
|
2161
|
+
document.body.style.cursor = "col-resize";
|
|
2162
|
+
},
|
|
2163
|
+
onKeyDown: (event) => {
|
|
2164
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
2165
|
+
event.preventDefault();
|
|
2166
|
+
event.stopPropagation();
|
|
2167
|
+
const amount = event.key === "ArrowRight" ? 16 : -16;
|
|
2168
|
+
setThumbnailWidth(
|
|
2169
|
+
Math.max(
|
|
2170
|
+
minThumbnailWidth,
|
|
2171
|
+
Math.min(maxThumbnailWidth, visibleThumbnailWidth + amount)
|
|
2172
|
+
)
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
)
|
|
2178
|
+
]
|
|
2179
|
+
}
|
|
2180
|
+
),
|
|
2181
|
+
/* @__PURE__ */ jsx12(
|
|
2182
|
+
"div",
|
|
2183
|
+
{
|
|
2184
|
+
"aria-hidden": isThumbnailRailVisible || isResizingThumbnails,
|
|
2185
|
+
inert: isThumbnailRailVisible || isResizingThumbnails,
|
|
2186
|
+
className: cn(
|
|
2187
|
+
"fixed top-4 left-4 z-50 rounded-lg border border-neutral-800 bg-neutral-950/90 backdrop-blur-sm transition-opacity duration-150 motion-reduce:transition-none",
|
|
2188
|
+
isThumbnailRailVisible || isResizingThumbnails ? "pointer-events-none opacity-0" : "opacity-100 delay-200"
|
|
2189
|
+
),
|
|
2190
|
+
children: /* @__PURE__ */ jsx12(
|
|
2191
|
+
"button",
|
|
2192
|
+
{
|
|
2193
|
+
type: "button",
|
|
2194
|
+
onClick: () => setThumbnailRailOpen(true),
|
|
2195
|
+
className: "rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
2196
|
+
"aria-label": "Show slide thumbnails",
|
|
2197
|
+
title: "Show thumbnails (T)",
|
|
2198
|
+
children: /* @__PURE__ */ jsx12(PanelLeftOpen, { className: "h-4 w-4" })
|
|
2199
|
+
}
|
|
2200
|
+
)
|
|
2201
|
+
}
|
|
2202
|
+
)
|
|
2203
|
+
] }),
|
|
1454
2204
|
/* @__PURE__ */ jsxs6(
|
|
1455
2205
|
"div",
|
|
1456
2206
|
{
|
|
@@ -1458,7 +2208,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1458
2208
|
role: "presentation",
|
|
1459
2209
|
tabIndex: isPresentationMode ? 0 : void 0,
|
|
1460
2210
|
className: cn(
|
|
1461
|
-
"flex flex-1 flex-col items-center justify-center overflow-hidden",
|
|
2211
|
+
"flex min-w-0 flex-1 flex-col items-center justify-center overflow-hidden",
|
|
1462
2212
|
isPresentationMode ? "bg-black p-0" : "p-4 md:p-8"
|
|
1463
2213
|
),
|
|
1464
2214
|
onClick: isPresentationMode ? advance : void 0,
|
|
@@ -1491,42 +2241,63 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1491
2241
|
}
|
|
1492
2242
|
)
|
|
1493
2243
|
}
|
|
1494
|
-
) : /* @__PURE__ */
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
2244
|
+
) : /* @__PURE__ */ jsx12(
|
|
2245
|
+
"div",
|
|
2246
|
+
{
|
|
2247
|
+
ref: previewViewportRef,
|
|
2248
|
+
className: "relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl",
|
|
2249
|
+
children: /* @__PURE__ */ jsxs6(
|
|
2250
|
+
"div",
|
|
2251
|
+
{
|
|
2252
|
+
ref: slideContainerRef,
|
|
2253
|
+
className: "absolute top-0 left-0 overflow-hidden",
|
|
2254
|
+
style: {
|
|
2255
|
+
width: SLIDE_DIMENSIONS.width,
|
|
2256
|
+
height: SLIDE_DIMENSIONS.height,
|
|
2257
|
+
transformOrigin: "top left"
|
|
2258
|
+
},
|
|
2259
|
+
children: [
|
|
2260
|
+
/* @__PURE__ */ jsx12(
|
|
2261
|
+
SlideRenderer,
|
|
2262
|
+
{
|
|
2263
|
+
slides,
|
|
2264
|
+
currentSlide,
|
|
2265
|
+
animationStep,
|
|
2266
|
+
totalSteps,
|
|
2267
|
+
direction,
|
|
2268
|
+
showAllAnimations,
|
|
2269
|
+
transition,
|
|
2270
|
+
directionalTransition,
|
|
2271
|
+
onTransitionComplete
|
|
2272
|
+
}
|
|
2273
|
+
),
|
|
2274
|
+
isAnnotationMode && /* @__PURE__ */ jsx12(
|
|
2275
|
+
AnnotationOverlay,
|
|
2276
|
+
{
|
|
2277
|
+
slides,
|
|
2278
|
+
currentSlide,
|
|
2279
|
+
slideContainerRef,
|
|
2280
|
+
selectedId: selectedAnnotationId,
|
|
2281
|
+
hoveredId: hoveredAnnotationId,
|
|
2282
|
+
onSelectId: setSelectedAnnotationId,
|
|
2283
|
+
onShowPanel: () => setShowAnnotationPanel(true),
|
|
2284
|
+
slideAnnotations: getSlideAnnotations(currentSlide),
|
|
2285
|
+
addAnnotation: effectiveAdd ?? (() => {
|
|
2286
|
+
})
|
|
2287
|
+
}
|
|
2288
|
+
)
|
|
2289
|
+
]
|
|
2290
|
+
}
|
|
2291
|
+
)
|
|
2292
|
+
}
|
|
2293
|
+
) }),
|
|
1524
2294
|
!isPresentationMode && /* @__PURE__ */ jsxs6("div", { className: "mt-6 flex items-center gap-4", children: [
|
|
1525
2295
|
/* @__PURE__ */ jsx12(
|
|
1526
2296
|
"button",
|
|
1527
2297
|
{
|
|
1528
2298
|
onClick: goBack,
|
|
1529
2299
|
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",
|
|
2300
|
+
title: "Previous step or slide (\u2190)",
|
|
1530
2301
|
children: /* @__PURE__ */ jsx12(ChevronLeft, { className: "h-5 w-5" })
|
|
1531
2302
|
}
|
|
1532
2303
|
),
|
|
@@ -1540,6 +2311,7 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1540
2311
|
{
|
|
1541
2312
|
onClick: advance,
|
|
1542
2313
|
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",
|
|
2314
|
+
title: "Next step or slide (\u2192 / Space)",
|
|
1543
2315
|
children: /* @__PURE__ */ jsx12(ChevronRight, { className: "h-5 w-5" })
|
|
1544
2316
|
}
|
|
1545
2317
|
)
|
|
@@ -1547,29 +2319,58 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1547
2319
|
]
|
|
1548
2320
|
}
|
|
1549
2321
|
),
|
|
1550
|
-
|
|
1551
|
-
|
|
2322
|
+
!isPresentationMode && /* @__PURE__ */ jsx12(
|
|
2323
|
+
"div",
|
|
1552
2324
|
{
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
2325
|
+
"aria-hidden": !isAnnotationMode || !showAnnotationPanel,
|
|
2326
|
+
inert: !isAnnotationMode || !showAnnotationPanel,
|
|
2327
|
+
className: cn(
|
|
2328
|
+
"h-full shrink-0 overflow-hidden transition-[width,transform] duration-200 ease-out motion-reduce:transition-none",
|
|
2329
|
+
isNarrowViewport && "fixed inset-y-0 right-0 z-50 shadow-xl",
|
|
2330
|
+
isNarrowViewport && (!isAnnotationMode || !showAnnotationPanel) && "translate-x-full"
|
|
2331
|
+
),
|
|
2332
|
+
style: {
|
|
2333
|
+
width: isAnnotationMode && showAnnotationPanel ? isNarrowViewport ? "min(20rem, 90vw)" : "20rem" : 0
|
|
2334
|
+
},
|
|
2335
|
+
children: /* @__PURE__ */ jsx12(
|
|
2336
|
+
"div",
|
|
2337
|
+
{
|
|
2338
|
+
className: cn(
|
|
2339
|
+
"h-full transition-opacity motion-reduce:transition-none",
|
|
2340
|
+
isAnnotationMode && showAnnotationPanel ? "opacity-100 delay-100 duration-100" : "opacity-0 duration-75"
|
|
2341
|
+
),
|
|
2342
|
+
children: /* @__PURE__ */ jsx12(
|
|
2343
|
+
AnnotationPanel,
|
|
2344
|
+
{
|
|
2345
|
+
annotations: getSlideAnnotations(currentSlide),
|
|
2346
|
+
unlinkedAnnotations,
|
|
2347
|
+
selectedId: selectedAnnotationId,
|
|
2348
|
+
onSelect: setSelectedAnnotationId,
|
|
2349
|
+
onHover: setHoveredAnnotationId,
|
|
2350
|
+
onDelete: effectiveDelete ?? (() => {
|
|
2351
|
+
}),
|
|
2352
|
+
onUpdate: effectiveUpdate ?? (() => {
|
|
2353
|
+
}),
|
|
2354
|
+
onClose: () => {
|
|
2355
|
+
setIsAnnotationMode(false);
|
|
2356
|
+
setShowAnnotationPanel(false);
|
|
2357
|
+
setSelectedAnnotationId(null);
|
|
2358
|
+
setHoveredAnnotationId(null);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
)
|
|
2362
|
+
}
|
|
2363
|
+
)
|
|
1562
2364
|
}
|
|
1563
2365
|
)
|
|
1564
2366
|
]
|
|
1565
2367
|
}
|
|
1566
2368
|
),
|
|
1567
2369
|
viewMode === "grid" && /* @__PURE__ */ jsx12("div", { className: "mx-auto max-w-7xl p-8 pt-16 print:hidden", children: /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4", children: slides.map((slideConfig, index) => {
|
|
1568
|
-
const SlideComponent = slideConfig.component;
|
|
1569
2370
|
const prevSection = index > 0 ? slides[index - 1]?.section : void 0;
|
|
1570
2371
|
const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection;
|
|
1571
|
-
return /* @__PURE__ */ jsxs6("div", { className:
|
|
1572
|
-
showSectionHeader && /* @__PURE__ */ jsx12("h3", { className: "mt-4 mb-
|
|
2372
|
+
return /* @__PURE__ */ jsxs6("div", { className: "contents", children: [
|
|
2373
|
+
showSectionHeader && /* @__PURE__ */ jsx12("h3", { className: "col-span-full mt-4 mb-0 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0", children: slideConfig.section }),
|
|
1573
2374
|
/* @__PURE__ */ jsxs6(
|
|
1574
2375
|
"button",
|
|
1575
2376
|
{
|
|
@@ -1577,30 +2378,15 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1577
2378
|
goToSlide(index);
|
|
1578
2379
|
setViewMode("slide");
|
|
1579
2380
|
},
|
|
1580
|
-
className: "group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-
|
|
2381
|
+
className: "group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-[#FF6B35] hover:shadow-lg hover:shadow-[#FF6B35]/10",
|
|
1581
2382
|
children: [
|
|
1582
|
-
/* @__PURE__ */ jsx12(
|
|
1583
|
-
"div",
|
|
1584
|
-
{
|
|
1585
|
-
className: "h-full w-full origin-top-left scale-[0.25]",
|
|
1586
|
-
style: { width: "400%", height: "400%" },
|
|
1587
|
-
children: /* @__PURE__ */ jsx12(
|
|
1588
|
-
AnimationProvider,
|
|
1589
|
-
{
|
|
1590
|
-
currentStep: slideConfig.steps,
|
|
1591
|
-
totalSteps: slideConfig.steps,
|
|
1592
|
-
showAllAnimations: true,
|
|
1593
|
-
children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slideConfig.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: slides.length }) })
|
|
1594
|
-
}
|
|
1595
|
-
)
|
|
1596
|
-
}
|
|
1597
|
-
),
|
|
2383
|
+
/* @__PURE__ */ jsx12(GridThumbnail, { slide: slideConfig, index, total: slides.length }),
|
|
1598
2384
|
/* @__PURE__ */ jsx12("div", { className: "absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20" }),
|
|
1599
2385
|
/* @__PURE__ */ jsx12("div", { className: "absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white", children: slideConfig.title ? `${index + 1}. ${slideConfig.title}` : index + 1 })
|
|
1600
2386
|
]
|
|
1601
2387
|
}
|
|
1602
2388
|
)
|
|
1603
|
-
] }, index);
|
|
2389
|
+
] }, slideConfig.id ?? index);
|
|
1604
2390
|
}) }) }),
|
|
1605
2391
|
/* @__PURE__ */ jsx12(
|
|
1606
2392
|
"div",
|
|
@@ -1610,25 +2396,15 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
|
|
|
1610
2396
|
"print:m-0 print:block print:max-w-none print:p-0",
|
|
1611
2397
|
viewMode === "list" ? "block" : "hidden print:block"
|
|
1612
2398
|
),
|
|
1613
|
-
children: /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-1 gap-8 print:block", children: slides.map((slideConfig, index) =>
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
currentStep: slideConfig.steps,
|
|
1623
|
-
totalSteps: slideConfig.steps,
|
|
1624
|
-
showAllAnimations: true,
|
|
1625
|
-
children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slideConfig.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: slides.length }) })
|
|
1626
|
-
}
|
|
1627
|
-
) })
|
|
1628
|
-
},
|
|
1629
|
-
index
|
|
1630
|
-
);
|
|
1631
|
-
}) })
|
|
2399
|
+
children: /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-1 gap-8 print:block", children: slides.map((slideConfig, index) => /* @__PURE__ */ jsx12(
|
|
2400
|
+
ListSlide,
|
|
2401
|
+
{
|
|
2402
|
+
slide: slideConfig,
|
|
2403
|
+
index,
|
|
2404
|
+
total: slides.length
|
|
2405
|
+
},
|
|
2406
|
+
slideConfig.id ?? index
|
|
2407
|
+
)) })
|
|
1632
2408
|
}
|
|
1633
2409
|
)
|
|
1634
2410
|
] });
|