promptslide 0.3.6 → 0.3.8
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 +17 -0
- package/dist/index.d.ts +80 -2
- package/dist/index.js +700 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/create.mjs +41 -5
- package/src/commands/publish.mjs +74 -6
- package/src/commands/pull.mjs +29 -0
- package/src/core/annotations/adapters/http.ts +43 -0
- package/src/core/annotations/annotation-form.tsx +82 -0
- package/src/core/annotations/annotation-overlay.tsx +198 -0
- package/src/core/annotations/annotation-panel.tsx +150 -0
- package/src/core/annotations/annotation-pin.tsx +42 -0
- package/src/core/annotations/api.ts +23 -0
- package/src/core/annotations/index.ts +5 -0
- package/src/core/annotations/selectors.ts +76 -0
- package/src/core/annotations/types.ts +47 -0
- package/src/core/annotations/use-annotations.ts +50 -0
- package/src/core/index.ts +4 -0
- package/src/core/slide-deck.tsx +168 -83
- package/src/vite/plugin.mjs +54 -0
package/dist/index.js
CHANGED
|
@@ -666,8 +666,505 @@ function SlideEmbed({ slides, transition, directionalTransition }) {
|
|
|
666
666
|
|
|
667
667
|
// src/core/slide-deck.tsx
|
|
668
668
|
import { LayoutGroup } from "framer-motion";
|
|
669
|
-
import { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, Monitor } from "lucide-react";
|
|
670
|
-
import { useCallback as
|
|
669
|
+
import { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, MessageCircle as MessageCircle2, Monitor } from "lucide-react";
|
|
670
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef4, useState as useState6 } from "react";
|
|
671
|
+
|
|
672
|
+
// src/core/annotations/annotation-overlay.tsx
|
|
673
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
|
|
674
|
+
|
|
675
|
+
// src/core/annotations/annotation-form.tsx
|
|
676
|
+
import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
|
|
677
|
+
import { ArrowUp, X } from "lucide-react";
|
|
678
|
+
import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
679
|
+
function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }) {
|
|
680
|
+
const [text, setText] = useState3("");
|
|
681
|
+
const textareaRef = useRef(null);
|
|
682
|
+
useEffect3(() => {
|
|
683
|
+
textareaRef.current?.focus();
|
|
684
|
+
}, []);
|
|
685
|
+
const handleSubmit = () => {
|
|
686
|
+
const trimmed = text.trim();
|
|
687
|
+
if (!trimmed) return;
|
|
688
|
+
onSubmit(trimmed);
|
|
689
|
+
};
|
|
690
|
+
const handleKeyDown = (e) => {
|
|
691
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
692
|
+
e.preventDefault();
|
|
693
|
+
handleSubmit();
|
|
694
|
+
}
|
|
695
|
+
if (e.key === "Escape") {
|
|
696
|
+
onCancel();
|
|
697
|
+
}
|
|
698
|
+
e.stopPropagation();
|
|
699
|
+
};
|
|
700
|
+
const left = xPercent > 70 ? void 0 : `${xPercent}%`;
|
|
701
|
+
const right = xPercent > 70 ? `${100 - xPercent}%` : void 0;
|
|
702
|
+
const top = yPercent > 70 ? void 0 : `${yPercent}%`;
|
|
703
|
+
const bottom = yPercent > 70 ? `${100 - yPercent}%` : void 0;
|
|
704
|
+
return /* @__PURE__ */ jsxs2(
|
|
705
|
+
"div",
|
|
706
|
+
{
|
|
707
|
+
role: "dialog",
|
|
708
|
+
className: "absolute z-40 w-72 overflow-hidden rounded-xl border border-white/[0.08] bg-neutral-900/95 shadow-2xl backdrop-blur-2xl",
|
|
709
|
+
style: { left, right, top, bottom },
|
|
710
|
+
onClick: (e) => e.stopPropagation(),
|
|
711
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
712
|
+
children: [
|
|
713
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-b border-white/[0.06] px-3.5 py-2.5", children: [
|
|
714
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs font-medium tracking-wide text-neutral-400", children: "Add annotation" }),
|
|
715
|
+
/* @__PURE__ */ jsx8(
|
|
716
|
+
"button",
|
|
717
|
+
{
|
|
718
|
+
onClick: onCancel,
|
|
719
|
+
className: "rounded-lg p-1 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300",
|
|
720
|
+
children: /* @__PURE__ */ jsx8(X, { className: "h-3.5 w-3.5" })
|
|
721
|
+
}
|
|
722
|
+
)
|
|
723
|
+
] }),
|
|
724
|
+
/* @__PURE__ */ jsxs2("div", { className: "p-3", children: [
|
|
725
|
+
/* @__PURE__ */ jsx8(
|
|
726
|
+
"textarea",
|
|
727
|
+
{
|
|
728
|
+
ref: textareaRef,
|
|
729
|
+
value: text,
|
|
730
|
+
onChange: (e) => setText(e.target.value),
|
|
731
|
+
onKeyDown: handleKeyDown,
|
|
732
|
+
placeholder: "Describe the change you want...",
|
|
733
|
+
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]",
|
|
734
|
+
rows: 3
|
|
735
|
+
}
|
|
736
|
+
),
|
|
737
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-2.5 flex items-center justify-between", children: [
|
|
738
|
+
/* @__PURE__ */ jsx8("span", { className: "text-[11px] text-neutral-600", children: "Enter to send" }),
|
|
739
|
+
/* @__PURE__ */ jsx8(
|
|
740
|
+
"button",
|
|
741
|
+
{
|
|
742
|
+
onClick: handleSubmit,
|
|
743
|
+
disabled: !text.trim(),
|
|
744
|
+
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]",
|
|
745
|
+
children: /* @__PURE__ */ jsx8(ArrowUp, { className: "h-3.5 w-3.5", strokeWidth: 2.5 })
|
|
746
|
+
}
|
|
747
|
+
)
|
|
748
|
+
] })
|
|
749
|
+
] })
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/core/annotations/annotation-pin.tsx
|
|
756
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
757
|
+
function AnnotationPin({ number, status, xPercent, yPercent, isSelected, onClick }) {
|
|
758
|
+
const isResolved = status === "resolved";
|
|
759
|
+
return /* @__PURE__ */ jsxs3(
|
|
760
|
+
"button",
|
|
761
|
+
{
|
|
762
|
+
onClick: (e) => {
|
|
763
|
+
e.stopPropagation();
|
|
764
|
+
onClick();
|
|
765
|
+
},
|
|
766
|
+
className: "absolute z-30 -translate-x-1/2 -translate-y-1/2 transition-all duration-200 hover:scale-110",
|
|
767
|
+
style: { left: `${xPercent}%`, top: `${yPercent}%` },
|
|
768
|
+
title: `Annotation #${number}`,
|
|
769
|
+
children: [
|
|
770
|
+
isSelected && !isResolved && /* @__PURE__ */ jsx9("div", { className: "absolute inset-[-4px] animate-pulse rounded-full bg-[#FF6B35]/25 blur-sm" }),
|
|
771
|
+
/* @__PURE__ */ jsx9(
|
|
772
|
+
"div",
|
|
773
|
+
{
|
|
774
|
+
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 ${isSelected ? isResolved ? "bg-neutral-500/90 text-white ring-2 ring-neutral-400/50" : "bg-[#FF6B35] text-white ring-2 ring-[#FF6B35]/40 ring-offset-1 ring-offset-black/50" : isResolved ? "bg-neutral-700/80 text-neutral-400" : "bg-[#FF6B35]/90 text-white hover:bg-[#FF6B35]"}`,
|
|
775
|
+
children: number
|
|
776
|
+
}
|
|
777
|
+
)
|
|
778
|
+
]
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// src/core/annotations/selectors.ts
|
|
784
|
+
function buildElementTarget(element, slideRoot) {
|
|
785
|
+
const rect = element.getBoundingClientRect();
|
|
786
|
+
const rootRect = slideRoot.getBoundingClientRect();
|
|
787
|
+
const xPercent = (rect.left + rect.width / 2 - rootRect.left) / rootRect.width * 100;
|
|
788
|
+
const yPercent = (rect.top + rect.height / 2 - rootRect.top) / rootRect.height * 100;
|
|
789
|
+
return {
|
|
790
|
+
dataAnnotate: element.getAttribute("data-annotate") || void 0,
|
|
791
|
+
contentNearPin: getTextFingerprint(element),
|
|
792
|
+
position: { xPercent, yPercent }
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function resolveTarget(target, slideRoot) {
|
|
796
|
+
if (target.dataAnnotate) {
|
|
797
|
+
const el = slideRoot.querySelector(`[data-annotate="${target.dataAnnotate}"]`);
|
|
798
|
+
if (el) return { element: el, method: "dataAnnotate" };
|
|
799
|
+
}
|
|
800
|
+
if (target.contentNearPin) {
|
|
801
|
+
const match = findByTextContent(slideRoot, target.contentNearPin);
|
|
802
|
+
if (match) return { element: match, method: "contentNearPin" };
|
|
803
|
+
}
|
|
804
|
+
return { element: null, method: "position" };
|
|
805
|
+
}
|
|
806
|
+
function getTextFingerprint(element) {
|
|
807
|
+
const text = element.textContent?.trim();
|
|
808
|
+
if (!text) return void 0;
|
|
809
|
+
return text.slice(0, 100);
|
|
810
|
+
}
|
|
811
|
+
function findByTextContent(root, text) {
|
|
812
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
813
|
+
let best = null;
|
|
814
|
+
let bestLength = Infinity;
|
|
815
|
+
while (walker.nextNode()) {
|
|
816
|
+
const el = walker.currentNode;
|
|
817
|
+
const elText = el.textContent?.trim();
|
|
818
|
+
if (!elText) continue;
|
|
819
|
+
const elFingerprint = elText.slice(0, 100);
|
|
820
|
+
if (elFingerprint === text && elText.length < bestLength) {
|
|
821
|
+
best = el;
|
|
822
|
+
bestLength = elText.length;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return best;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// src/core/annotations/annotation-overlay.tsx
|
|
829
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
830
|
+
function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId, onSelectId, onShowPanel, slideAnnotations, addAnnotation }) {
|
|
831
|
+
const [pending, setPending] = useState4(null);
|
|
832
|
+
const [hoveredElement, setHoveredElement] = useState4(null);
|
|
833
|
+
const overlayRef = useRef2(null);
|
|
834
|
+
const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`;
|
|
835
|
+
const resolvedAnnotations = slideAnnotations.map((a, i) => {
|
|
836
|
+
const container = slideContainerRef.current;
|
|
837
|
+
if (!container) {
|
|
838
|
+
return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 };
|
|
839
|
+
}
|
|
840
|
+
const { element } = resolveTarget(a.target, container);
|
|
841
|
+
if (element) {
|
|
842
|
+
const rect = element.getBoundingClientRect();
|
|
843
|
+
const containerRect = container.getBoundingClientRect();
|
|
844
|
+
const xPercent = (rect.left + rect.width / 2 - containerRect.left) / containerRect.width * 100;
|
|
845
|
+
const yPercent = (rect.top + rect.height / 2 - containerRect.top) / containerRect.height * 100;
|
|
846
|
+
return { annotation: a, xPercent, yPercent, number: i + 1 };
|
|
847
|
+
}
|
|
848
|
+
return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 };
|
|
849
|
+
});
|
|
850
|
+
const handleOverlayClick = useCallback3(
|
|
851
|
+
(e) => {
|
|
852
|
+
const container = slideContainerRef.current;
|
|
853
|
+
if (!container) return;
|
|
854
|
+
const overlay = overlayRef.current;
|
|
855
|
+
if (overlay) overlay.style.pointerEvents = "none";
|
|
856
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
857
|
+
if (overlay) overlay.style.pointerEvents = "";
|
|
858
|
+
if (!elementUnder || !container.contains(elementUnder)) {
|
|
859
|
+
setPending(null);
|
|
860
|
+
onSelectId(null);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
let target = elementUnder;
|
|
864
|
+
if (target.tagName === "SPAN" && target.parentElement && container.contains(target.parentElement)) {
|
|
865
|
+
const parent = target.parentElement;
|
|
866
|
+
if (parent.tagName !== "DIV" || parent.children.length <= 3) {
|
|
867
|
+
target = parent;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const annotationTarget = buildElementTarget(target, container);
|
|
871
|
+
const containerRect = container.getBoundingClientRect();
|
|
872
|
+
const xPercent = (e.clientX - containerRect.left) / containerRect.width * 100;
|
|
873
|
+
const yPercent = (e.clientY - containerRect.top) / containerRect.height * 100;
|
|
874
|
+
setPending({ target: annotationTarget, xPercent, yPercent });
|
|
875
|
+
onSelectId(null);
|
|
876
|
+
},
|
|
877
|
+
[slideContainerRef, onSelectId]
|
|
878
|
+
);
|
|
879
|
+
const handleSubmit = useCallback3(
|
|
880
|
+
(text) => {
|
|
881
|
+
if (!pending) return;
|
|
882
|
+
addAnnotation(currentSlide, slideTitle, pending.target, text);
|
|
883
|
+
setPending(null);
|
|
884
|
+
onShowPanel();
|
|
885
|
+
},
|
|
886
|
+
[pending, currentSlide, slideTitle, addAnnotation, onShowPanel]
|
|
887
|
+
);
|
|
888
|
+
const handleMouseMove = useCallback3(
|
|
889
|
+
(e) => {
|
|
890
|
+
if (pending) {
|
|
891
|
+
setHoveredElement(null);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const container = slideContainerRef.current;
|
|
895
|
+
if (!container) return;
|
|
896
|
+
const overlay = overlayRef.current;
|
|
897
|
+
if (overlay) overlay.style.pointerEvents = "none";
|
|
898
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
899
|
+
if (overlay) overlay.style.pointerEvents = "";
|
|
900
|
+
if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {
|
|
901
|
+
const containerRect = container.getBoundingClientRect();
|
|
902
|
+
const elRect = elementUnder.getBoundingClientRect();
|
|
903
|
+
setHoveredElement(
|
|
904
|
+
new DOMRect(
|
|
905
|
+
elRect.left - containerRect.left,
|
|
906
|
+
elRect.top - containerRect.top,
|
|
907
|
+
elRect.width,
|
|
908
|
+
elRect.height
|
|
909
|
+
)
|
|
910
|
+
);
|
|
911
|
+
} else {
|
|
912
|
+
setHoveredElement(null);
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
[slideContainerRef, pending]
|
|
916
|
+
);
|
|
917
|
+
useEffect4(() => {
|
|
918
|
+
setPending(null);
|
|
919
|
+
onSelectId(null);
|
|
920
|
+
setHoveredElement(null);
|
|
921
|
+
}, [currentSlide, onSelectId]);
|
|
922
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
923
|
+
hoveredElement && /* @__PURE__ */ jsx10(
|
|
924
|
+
"div",
|
|
925
|
+
{
|
|
926
|
+
className: "pointer-events-none absolute z-20 rounded-lg border-2 border-dashed border-[#FF6B35]/50 bg-[#FF6B35]/5",
|
|
927
|
+
style: {
|
|
928
|
+
left: hoveredElement.x,
|
|
929
|
+
top: hoveredElement.y,
|
|
930
|
+
width: hoveredElement.width,
|
|
931
|
+
height: hoveredElement.height
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
),
|
|
935
|
+
/* @__PURE__ */ jsx10(
|
|
936
|
+
"div",
|
|
937
|
+
{
|
|
938
|
+
ref: overlayRef,
|
|
939
|
+
role: "button",
|
|
940
|
+
tabIndex: 0,
|
|
941
|
+
className: "absolute inset-0 z-20",
|
|
942
|
+
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` },
|
|
943
|
+
onClick: handleOverlayClick,
|
|
944
|
+
onKeyDown: (e) => {
|
|
945
|
+
if (e.key === "Escape") {
|
|
946
|
+
setPending(null);
|
|
947
|
+
onSelectId(null);
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
onMouseMove: handleMouseMove,
|
|
951
|
+
onMouseLeave: () => setHoveredElement(null)
|
|
952
|
+
}
|
|
953
|
+
),
|
|
954
|
+
resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => /* @__PURE__ */ jsx10(
|
|
955
|
+
AnnotationPin,
|
|
956
|
+
{
|
|
957
|
+
number,
|
|
958
|
+
status: annotation.status,
|
|
959
|
+
xPercent,
|
|
960
|
+
yPercent,
|
|
961
|
+
isSelected: annotation.id === selectedId,
|
|
962
|
+
onClick: () => {
|
|
963
|
+
onSelectId(annotation.id === selectedId ? null : annotation.id);
|
|
964
|
+
onShowPanel();
|
|
965
|
+
setPending(null);
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
annotation.id
|
|
969
|
+
)),
|
|
970
|
+
pending && /* @__PURE__ */ jsx10(
|
|
971
|
+
AnnotationForm,
|
|
972
|
+
{
|
|
973
|
+
xPercent: pending.xPercent,
|
|
974
|
+
yPercent: pending.yPercent,
|
|
975
|
+
onSubmit: handleSubmit,
|
|
976
|
+
onCancel: () => setPending(null)
|
|
977
|
+
}
|
|
978
|
+
)
|
|
979
|
+
] });
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// src/core/annotations/annotation-panel.tsx
|
|
983
|
+
import { MessageCircle, Trash2, X as X2 } from "lucide-react";
|
|
984
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
985
|
+
function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }) {
|
|
986
|
+
const open = annotations.filter((a) => a.status === "open");
|
|
987
|
+
const resolved = annotations.filter((a) => a.status === "resolved");
|
|
988
|
+
return /* @__PURE__ */ jsxs5("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", children: [
|
|
989
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between px-4 py-3.5", children: [
|
|
990
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2.5", children: [
|
|
991
|
+
/* @__PURE__ */ jsx11("div", { className: "flex h-6 w-6 items-center justify-center rounded-lg bg-[#FF6B35]/15", children: /* @__PURE__ */ jsx11(MessageCircle, { className: "h-3.5 w-3.5 text-[#FF6B35]" }) }),
|
|
992
|
+
/* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-white", children: "Annotations" }),
|
|
993
|
+
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 })
|
|
994
|
+
] }),
|
|
995
|
+
/* @__PURE__ */ jsx11(
|
|
996
|
+
"button",
|
|
997
|
+
{
|
|
998
|
+
onClick: onClose,
|
|
999
|
+
className: "rounded-lg p-1.5 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300",
|
|
1000
|
+
children: /* @__PURE__ */ jsx11(X2, { className: "h-4 w-4" })
|
|
1001
|
+
}
|
|
1002
|
+
)
|
|
1003
|
+
] }),
|
|
1004
|
+
/* @__PURE__ */ jsx11("div", { className: "h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" }),
|
|
1005
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex-1 overflow-y-auto p-2", children: [
|
|
1006
|
+
annotations.length === 0 && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center gap-2 px-4 py-8 text-center", children: [
|
|
1007
|
+
/* @__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" }) }),
|
|
1008
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm text-neutral-500", children: "Click on slide elements to add annotations" })
|
|
1009
|
+
] }),
|
|
1010
|
+
open.length > 0 && /* @__PURE__ */ jsxs5("div", { children: [
|
|
1011
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Open" }),
|
|
1012
|
+
open.map((a, i) => /* @__PURE__ */ jsx11(
|
|
1013
|
+
AnnotationItem,
|
|
1014
|
+
{
|
|
1015
|
+
annotation: a,
|
|
1016
|
+
number: i + 1,
|
|
1017
|
+
isSelected: a.id === selectedId,
|
|
1018
|
+
onSelect: () => onSelect(a.id),
|
|
1019
|
+
onDelete: () => onDelete(a.id)
|
|
1020
|
+
},
|
|
1021
|
+
a.id
|
|
1022
|
+
))
|
|
1023
|
+
] }),
|
|
1024
|
+
resolved.length > 0 && /* @__PURE__ */ jsxs5("div", { className: open.length > 0 ? "mt-3" : "", children: [
|
|
1025
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase", children: "Resolved" }),
|
|
1026
|
+
resolved.map((a, i) => /* @__PURE__ */ jsx11(
|
|
1027
|
+
AnnotationItem,
|
|
1028
|
+
{
|
|
1029
|
+
annotation: a,
|
|
1030
|
+
number: open.length + i + 1,
|
|
1031
|
+
isSelected: a.id === selectedId,
|
|
1032
|
+
onSelect: () => onSelect(a.id),
|
|
1033
|
+
onDelete: () => onDelete(a.id)
|
|
1034
|
+
},
|
|
1035
|
+
a.id
|
|
1036
|
+
))
|
|
1037
|
+
] })
|
|
1038
|
+
] })
|
|
1039
|
+
] });
|
|
1040
|
+
}
|
|
1041
|
+
function AnnotationItem({
|
|
1042
|
+
annotation,
|
|
1043
|
+
number,
|
|
1044
|
+
isSelected,
|
|
1045
|
+
onSelect,
|
|
1046
|
+
onDelete
|
|
1047
|
+
}) {
|
|
1048
|
+
return /* @__PURE__ */ jsxs5(
|
|
1049
|
+
"div",
|
|
1050
|
+
{
|
|
1051
|
+
role: "button",
|
|
1052
|
+
tabIndex: 0,
|
|
1053
|
+
onClick: onSelect,
|
|
1054
|
+
onKeyDown: (e) => {
|
|
1055
|
+
if (e.key === "Enter" || e.key === " ") onSelect();
|
|
1056
|
+
},
|
|
1057
|
+
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]"}`,
|
|
1058
|
+
children: [
|
|
1059
|
+
/* @__PURE__ */ jsx11(
|
|
1060
|
+
"button",
|
|
1061
|
+
{
|
|
1062
|
+
onClick: (e) => {
|
|
1063
|
+
e.stopPropagation();
|
|
1064
|
+
onDelete();
|
|
1065
|
+
},
|
|
1066
|
+
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",
|
|
1067
|
+
children: /* @__PURE__ */ jsx11(Trash2, { className: "h-3 w-3" })
|
|
1068
|
+
}
|
|
1069
|
+
),
|
|
1070
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-start gap-2.5", children: [
|
|
1071
|
+
/* @__PURE__ */ jsx11(
|
|
1072
|
+
"div",
|
|
1073
|
+
{
|
|
1074
|
+
className: `mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-[11px] font-semibold ${annotation.status === "open" ? "bg-[#FF6B35] text-white" : "bg-neutral-700 text-neutral-400"}`,
|
|
1075
|
+
children: number
|
|
1076
|
+
}
|
|
1077
|
+
),
|
|
1078
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 pr-4", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[13px] leading-relaxed text-neutral-200", children: annotation.body }),
|
|
1080
|
+
annotation.target.contentNearPin && /* @__PURE__ */ jsx11("p", { className: "mt-1 truncate text-[11px] text-neutral-600", children: annotation.target.contentNearPin }),
|
|
1081
|
+
annotation.resolution && /* @__PURE__ */ jsx11("p", { className: "mt-1.5 text-[11px] text-emerald-400/80 italic", children: annotation.resolution })
|
|
1082
|
+
] })
|
|
1083
|
+
] })
|
|
1084
|
+
]
|
|
1085
|
+
}
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/core/annotations/use-annotations.ts
|
|
1090
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState5 } from "react";
|
|
1091
|
+
|
|
1092
|
+
// src/core/annotations/adapters/http.ts
|
|
1093
|
+
var ENDPOINT = "/__promptslide_annotations";
|
|
1094
|
+
async function loadAll() {
|
|
1095
|
+
try {
|
|
1096
|
+
const res = await fetch(ENDPOINT);
|
|
1097
|
+
if (!res.ok) return [];
|
|
1098
|
+
const data = await res.json();
|
|
1099
|
+
return data.annotations || [];
|
|
1100
|
+
} catch {
|
|
1101
|
+
return [];
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
async function saveAll(annotations) {
|
|
1105
|
+
const data = { version: 1, annotations };
|
|
1106
|
+
await fetch(ENDPOINT, {
|
|
1107
|
+
method: "POST",
|
|
1108
|
+
headers: { "Content-Type": "application/json" },
|
|
1109
|
+
body: JSON.stringify(data)
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
function createHttpAdapter() {
|
|
1113
|
+
let cache = [];
|
|
1114
|
+
return {
|
|
1115
|
+
async load() {
|
|
1116
|
+
cache = await loadAll();
|
|
1117
|
+
return cache;
|
|
1118
|
+
},
|
|
1119
|
+
async add(annotation) {
|
|
1120
|
+
cache = [...cache, annotation];
|
|
1121
|
+
await saveAll(cache);
|
|
1122
|
+
},
|
|
1123
|
+
async remove(id) {
|
|
1124
|
+
cache = cache.filter((a) => a.id !== id);
|
|
1125
|
+
await saveAll(cache);
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// src/core/annotations/use-annotations.ts
|
|
1131
|
+
function useAnnotations(adapter) {
|
|
1132
|
+
const adapterRef = useRef3(adapter ?? createHttpAdapter());
|
|
1133
|
+
const [annotations, setAnnotations] = useState5([]);
|
|
1134
|
+
useEffect5(() => {
|
|
1135
|
+
adapterRef.current.load().then(setAnnotations);
|
|
1136
|
+
return adapterRef.current.subscribe?.(setAnnotations);
|
|
1137
|
+
}, []);
|
|
1138
|
+
const addAnnotation = useCallback4(
|
|
1139
|
+
(slideIndex, slideTitle, target, body) => {
|
|
1140
|
+
const annotation = {
|
|
1141
|
+
id: crypto.randomUUID(),
|
|
1142
|
+
slideIndex,
|
|
1143
|
+
slideTitle,
|
|
1144
|
+
target,
|
|
1145
|
+
body,
|
|
1146
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1147
|
+
status: "open"
|
|
1148
|
+
};
|
|
1149
|
+
setAnnotations((prev) => [...prev, annotation]);
|
|
1150
|
+
adapterRef.current.add(annotation);
|
|
1151
|
+
},
|
|
1152
|
+
[]
|
|
1153
|
+
);
|
|
1154
|
+
const deleteAnnotation = useCallback4((id) => {
|
|
1155
|
+
setAnnotations((prev) => prev.filter((a) => a.id !== id));
|
|
1156
|
+
adapterRef.current.remove(id);
|
|
1157
|
+
}, []);
|
|
1158
|
+
const getSlideAnnotations = useCallback4(
|
|
1159
|
+
(slideIndex) => annotations.filter((a) => a.slideIndex === slideIndex),
|
|
1160
|
+
[annotations]
|
|
1161
|
+
);
|
|
1162
|
+
const openCount = useMemo3(() => annotations.filter((a) => a.status === "open").length, [annotations]);
|
|
1163
|
+
const updateAnnotations = useCallback4((updated) => {
|
|
1164
|
+
setAnnotations(updated);
|
|
1165
|
+
}, []);
|
|
1166
|
+
return { annotations, addAnnotation, deleteAnnotation, getSlideAnnotations, openCount, updateAnnotations };
|
|
1167
|
+
}
|
|
671
1168
|
|
|
672
1169
|
// src/core/utils.ts
|
|
673
1170
|
import { clsx } from "clsx";
|
|
@@ -677,16 +1174,16 @@ function cn(...inputs) {
|
|
|
677
1174
|
}
|
|
678
1175
|
|
|
679
1176
|
// src/core/slide-deck.tsx
|
|
680
|
-
import { jsx as
|
|
1177
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
681
1178
|
function SlideExportView({ slides, slideIndex }) {
|
|
682
|
-
const [ready, setReady] =
|
|
1179
|
+
const [ready, setReady] = useState6(false);
|
|
683
1180
|
const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1));
|
|
684
1181
|
const slideConfig = slides[clampedIndex];
|
|
685
1182
|
const SlideComponent = slideConfig.component;
|
|
686
|
-
|
|
1183
|
+
useEffect6(() => {
|
|
687
1184
|
setReady(true);
|
|
688
1185
|
}, []);
|
|
689
|
-
return /* @__PURE__ */
|
|
1186
|
+
return /* @__PURE__ */ jsx12(
|
|
690
1187
|
"div",
|
|
691
1188
|
{
|
|
692
1189
|
"data-export-ready": ready ? "true" : void 0,
|
|
@@ -697,32 +1194,46 @@ function SlideExportView({ slides, slideIndex }) {
|
|
|
697
1194
|
position: "relative",
|
|
698
1195
|
background: "black"
|
|
699
1196
|
},
|
|
700
|
-
children: /* @__PURE__ */
|
|
1197
|
+
children: /* @__PURE__ */ jsx12(
|
|
701
1198
|
AnimationProvider,
|
|
702
1199
|
{
|
|
703
1200
|
currentStep: slideConfig.steps,
|
|
704
1201
|
totalSteps: slideConfig.steps,
|
|
705
1202
|
showAllAnimations: true,
|
|
706
|
-
children: /* @__PURE__ */
|
|
1203
|
+
children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: clampedIndex, slideTitle: slideConfig.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: clampedIndex + 1, totalSlides: slides.length }) })
|
|
707
1204
|
}
|
|
708
1205
|
)
|
|
709
1206
|
}
|
|
710
1207
|
);
|
|
711
1208
|
}
|
|
712
|
-
function SlideDeck({ slides, transition, directionalTransition }) {
|
|
713
|
-
const [exportParams] =
|
|
1209
|
+
function SlideDeck({ slides, transition, directionalTransition, annotations, onAnnotationAdd, onAnnotationDelete }) {
|
|
1210
|
+
const [exportParams] = useState6(() => {
|
|
714
1211
|
if (typeof window === "undefined") return null;
|
|
715
1212
|
const params = new URLSearchParams(window.location.search);
|
|
716
1213
|
if (params.get("export") !== "true") return null;
|
|
717
1214
|
return { slideIndex: parseInt(params.get("slide") || "0", 10) };
|
|
718
1215
|
});
|
|
719
1216
|
if (exportParams) {
|
|
720
|
-
return /* @__PURE__ */
|
|
1217
|
+
return /* @__PURE__ */ jsx12(SlideExportView, { slides, slideIndex: exportParams.slideIndex });
|
|
721
1218
|
}
|
|
722
|
-
const
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
const
|
|
1219
|
+
const internal = useAnnotations();
|
|
1220
|
+
const isExternallyManaged = annotations !== void 0;
|
|
1221
|
+
const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations;
|
|
1222
|
+
const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation;
|
|
1223
|
+
const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation;
|
|
1224
|
+
const openCount = useMemo4(() => effectiveAnnotations.filter((a) => a.status === "open").length, [effectiveAnnotations]);
|
|
1225
|
+
const getSlideAnnotations = useCallback5(
|
|
1226
|
+
(slideIndex) => effectiveAnnotations.filter((a) => a.slideIndex === slideIndex),
|
|
1227
|
+
[effectiveAnnotations]
|
|
1228
|
+
);
|
|
1229
|
+
const [viewMode, setViewMode] = useState6("slide");
|
|
1230
|
+
const [isPresentationMode, setIsPresentationMode] = useState6(false);
|
|
1231
|
+
const [isAnnotationMode, setIsAnnotationMode] = useState6(false);
|
|
1232
|
+
const [showAnnotationPanel, setShowAnnotationPanel] = useState6(false);
|
|
1233
|
+
const [selectedAnnotationId, setSelectedAnnotationId] = useState6(null);
|
|
1234
|
+
const [scale, setScale] = useState6(1);
|
|
1235
|
+
const containerRef = useRef4(null);
|
|
1236
|
+
const slideContainerRef = useRef4(null);
|
|
726
1237
|
const {
|
|
727
1238
|
currentSlide,
|
|
728
1239
|
animationStep,
|
|
@@ -736,21 +1247,21 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
736
1247
|
} = useSlideNavigation({
|
|
737
1248
|
slides
|
|
738
1249
|
});
|
|
739
|
-
const togglePresentationMode =
|
|
1250
|
+
const togglePresentationMode = useCallback5(async () => {
|
|
740
1251
|
if (!document.fullscreenElement) {
|
|
741
1252
|
await document.documentElement.requestFullscreen();
|
|
742
1253
|
} else {
|
|
743
1254
|
await document.exitFullscreen();
|
|
744
1255
|
}
|
|
745
1256
|
}, []);
|
|
746
|
-
|
|
1257
|
+
useEffect6(() => {
|
|
747
1258
|
const handleFullscreenChange = () => {
|
|
748
1259
|
setIsPresentationMode(!!document.fullscreenElement);
|
|
749
1260
|
};
|
|
750
1261
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
751
1262
|
return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
752
1263
|
}, []);
|
|
753
|
-
|
|
1264
|
+
useEffect6(() => {
|
|
754
1265
|
const calculateScale = () => {
|
|
755
1266
|
if (!isPresentationMode) {
|
|
756
1267
|
setScale(1);
|
|
@@ -778,7 +1289,7 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
778
1289
|
window.print();
|
|
779
1290
|
}, 100);
|
|
780
1291
|
};
|
|
781
|
-
|
|
1292
|
+
useEffect6(() => {
|
|
782
1293
|
const handleKeyDown = (e) => {
|
|
783
1294
|
if (e.key === "f" || e.key === "F") {
|
|
784
1295
|
togglePresentationMode();
|
|
@@ -788,7 +1299,7 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
788
1299
|
setViewMode((prev) => prev === "grid" ? "slide" : "grid");
|
|
789
1300
|
return;
|
|
790
1301
|
}
|
|
791
|
-
if (viewMode !== "slide") return;
|
|
1302
|
+
if (viewMode !== "slide" || isAnnotationMode) return;
|
|
792
1303
|
if (e.key === "ArrowRight" || e.key === " ") {
|
|
793
1304
|
e.preventDefault();
|
|
794
1305
|
advance();
|
|
@@ -799,9 +1310,9 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
799
1310
|
};
|
|
800
1311
|
window.addEventListener("keydown", handleKeyDown);
|
|
801
1312
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
802
|
-
}, [advance, goBack, viewMode, togglePresentationMode]);
|
|
803
|
-
return /* @__PURE__ */
|
|
804
|
-
/* @__PURE__ */
|
|
1313
|
+
}, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode]);
|
|
1314
|
+
return /* @__PURE__ */ jsxs6("div", { className: "min-h-screen w-full bg-neutral-950 text-foreground", children: [
|
|
1315
|
+
/* @__PURE__ */ jsx12("style", { children: `
|
|
805
1316
|
@media print {
|
|
806
1317
|
@page {
|
|
807
1318
|
size: 1920px 1080px;
|
|
@@ -822,15 +1333,16 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
822
1333
|
}
|
|
823
1334
|
}
|
|
824
1335
|
` }),
|
|
825
|
-
/* @__PURE__ */
|
|
1336
|
+
/* @__PURE__ */ jsxs6(
|
|
826
1337
|
"div",
|
|
827
1338
|
{
|
|
828
1339
|
className: cn(
|
|
829
|
-
"fixed top-4
|
|
830
|
-
isPresentationMode && "hidden"
|
|
1340
|
+
"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",
|
|
1341
|
+
isPresentationMode && "hidden",
|
|
1342
|
+
isAnnotationMode && showAnnotationPanel ? "right-[19.5rem]" : "right-4"
|
|
831
1343
|
),
|
|
832
1344
|
children: [
|
|
833
|
-
/* @__PURE__ */
|
|
1345
|
+
/* @__PURE__ */ jsx12(
|
|
834
1346
|
"button",
|
|
835
1347
|
{
|
|
836
1348
|
onClick: () => setViewMode("slide"),
|
|
@@ -839,10 +1351,10 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
839
1351
|
viewMode === "slide" && "bg-neutral-800 text-white"
|
|
840
1352
|
),
|
|
841
1353
|
title: "Presentation View",
|
|
842
|
-
children: /* @__PURE__ */
|
|
1354
|
+
children: /* @__PURE__ */ jsx12(Monitor, { className: "h-4 w-4" })
|
|
843
1355
|
}
|
|
844
1356
|
),
|
|
845
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx12(
|
|
846
1358
|
"button",
|
|
847
1359
|
{
|
|
848
1360
|
onClick: () => setViewMode("list"),
|
|
@@ -851,10 +1363,10 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
851
1363
|
viewMode === "list" && "bg-neutral-800 text-white"
|
|
852
1364
|
),
|
|
853
1365
|
title: "List View",
|
|
854
|
-
children: /* @__PURE__ */
|
|
1366
|
+
children: /* @__PURE__ */ jsx12(List, { className: "h-4 w-4" })
|
|
855
1367
|
}
|
|
856
1368
|
),
|
|
857
|
-
/* @__PURE__ */
|
|
1369
|
+
/* @__PURE__ */ jsx12(
|
|
858
1370
|
"button",
|
|
859
1371
|
{
|
|
860
1372
|
onClick: () => setViewMode("grid"),
|
|
@@ -863,121 +1375,185 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
863
1375
|
viewMode === "grid" && "bg-neutral-800 text-white"
|
|
864
1376
|
),
|
|
865
1377
|
title: "Grid View",
|
|
866
|
-
children: /* @__PURE__ */
|
|
1378
|
+
children: /* @__PURE__ */ jsx12(Grid3X3, { className: "h-4 w-4" })
|
|
867
1379
|
}
|
|
868
1380
|
),
|
|
869
|
-
/* @__PURE__ */
|
|
870
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsx12("div", { className: "mx-1 w-px bg-neutral-800" }),
|
|
1382
|
+
/* @__PURE__ */ jsxs6(
|
|
1383
|
+
"button",
|
|
1384
|
+
{
|
|
1385
|
+
onClick: () => {
|
|
1386
|
+
setIsAnnotationMode((prev) => {
|
|
1387
|
+
const next = !prev;
|
|
1388
|
+
setShowAnnotationPanel(next);
|
|
1389
|
+
if (!next) setSelectedAnnotationId(null);
|
|
1390
|
+
return next;
|
|
1391
|
+
});
|
|
1392
|
+
},
|
|
1393
|
+
className: cn(
|
|
1394
|
+
"relative rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
1395
|
+
isAnnotationMode && "bg-[#FF6B35] text-white hover:bg-[#FF7A4A]"
|
|
1396
|
+
),
|
|
1397
|
+
title: "Annotate slides",
|
|
1398
|
+
children: [
|
|
1399
|
+
/* @__PURE__ */ jsx12(MessageCircle2, { className: "h-4 w-4" }),
|
|
1400
|
+
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 })
|
|
1401
|
+
]
|
|
1402
|
+
}
|
|
1403
|
+
),
|
|
1404
|
+
/* @__PURE__ */ jsx12("div", { className: "mx-1 w-px bg-neutral-800" }),
|
|
1405
|
+
/* @__PURE__ */ jsx12(
|
|
871
1406
|
"button",
|
|
872
1407
|
{
|
|
873
1408
|
onClick: handleExportPdf,
|
|
874
1409
|
className: "rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
875
1410
|
title: "Download PDF",
|
|
876
|
-
children: /* @__PURE__ */
|
|
1411
|
+
children: /* @__PURE__ */ jsx12(Download, { className: "h-4 w-4" })
|
|
877
1412
|
}
|
|
878
1413
|
),
|
|
879
|
-
/* @__PURE__ */
|
|
1414
|
+
/* @__PURE__ */ jsx12(
|
|
880
1415
|
"button",
|
|
881
1416
|
{
|
|
882
1417
|
onClick: togglePresentationMode,
|
|
883
1418
|
className: "rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white",
|
|
884
1419
|
title: "Present (F)",
|
|
885
|
-
children: /* @__PURE__ */
|
|
1420
|
+
children: /* @__PURE__ */ jsx12(Maximize, { className: "h-4 w-4" })
|
|
886
1421
|
}
|
|
887
1422
|
)
|
|
888
1423
|
]
|
|
889
1424
|
}
|
|
890
1425
|
),
|
|
891
|
-
viewMode === "slide" && /* @__PURE__ */
|
|
1426
|
+
viewMode === "slide" && /* @__PURE__ */ jsxs6(
|
|
892
1427
|
"div",
|
|
893
1428
|
{
|
|
894
|
-
ref: containerRef,
|
|
895
|
-
role: "presentation",
|
|
896
|
-
tabIndex: isPresentationMode ? 0 : void 0,
|
|
897
1429
|
className: cn(
|
|
898
|
-
"flex h-screen w-full
|
|
899
|
-
isPresentationMode ? "bg-black
|
|
1430
|
+
"flex h-screen w-full print:hidden",
|
|
1431
|
+
isPresentationMode ? "bg-black" : ""
|
|
900
1432
|
),
|
|
901
|
-
onClick: isPresentationMode ? advance : void 0,
|
|
902
|
-
onKeyDown: isPresentationMode ? (e) => {
|
|
903
|
-
if (e.key === "Enter" || e.key === " ") advance();
|
|
904
|
-
} : void 0,
|
|
905
1433
|
children: [
|
|
906
|
-
/* @__PURE__ */
|
|
1434
|
+
/* @__PURE__ */ jsxs6(
|
|
907
1435
|
"div",
|
|
908
1436
|
{
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1437
|
+
ref: containerRef,
|
|
1438
|
+
role: "presentation",
|
|
1439
|
+
tabIndex: isPresentationMode ? 0 : void 0,
|
|
1440
|
+
className: cn(
|
|
1441
|
+
"flex flex-1 flex-col items-center justify-center overflow-hidden",
|
|
1442
|
+
isPresentationMode ? "bg-black p-0" : "p-4 md:p-8"
|
|
1443
|
+
),
|
|
1444
|
+
onClick: isPresentationMode ? advance : void 0,
|
|
1445
|
+
onKeyDown: isPresentationMode ? (e) => {
|
|
1446
|
+
if (e.key === "Enter" || e.key === " ") advance();
|
|
1447
|
+
} : void 0,
|
|
1448
|
+
children: [
|
|
1449
|
+
/* @__PURE__ */ jsx12(LayoutGroup, { id: "slide-deck", children: isPresentationMode ? /* @__PURE__ */ jsx12(
|
|
1450
|
+
"div",
|
|
1451
|
+
{
|
|
1452
|
+
className: "pointer-events-none relative overflow-hidden bg-black",
|
|
1453
|
+
style: {
|
|
1454
|
+
width: SLIDE_DIMENSIONS.width,
|
|
1455
|
+
height: SLIDE_DIMENSIONS.height,
|
|
1456
|
+
transform: `scale(${scale})`,
|
|
1457
|
+
transformOrigin: "center center"
|
|
1458
|
+
},
|
|
1459
|
+
children: /* @__PURE__ */ jsx12(
|
|
1460
|
+
SlideRenderer,
|
|
1461
|
+
{
|
|
1462
|
+
slides,
|
|
1463
|
+
currentSlide,
|
|
1464
|
+
animationStep,
|
|
1465
|
+
totalSteps,
|
|
1466
|
+
direction,
|
|
1467
|
+
showAllAnimations,
|
|
1468
|
+
transition,
|
|
1469
|
+
directionalTransition,
|
|
1470
|
+
onTransitionComplete
|
|
1471
|
+
}
|
|
1472
|
+
)
|
|
1473
|
+
}
|
|
1474
|
+
) : /* @__PURE__ */ jsxs6("div", { ref: slideContainerRef, className: "relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl", children: [
|
|
1475
|
+
/* @__PURE__ */ jsx12(
|
|
1476
|
+
SlideRenderer,
|
|
1477
|
+
{
|
|
1478
|
+
slides,
|
|
1479
|
+
currentSlide,
|
|
1480
|
+
animationStep,
|
|
1481
|
+
totalSteps,
|
|
1482
|
+
direction,
|
|
1483
|
+
showAllAnimations,
|
|
1484
|
+
transition,
|
|
1485
|
+
directionalTransition,
|
|
1486
|
+
onTransitionComplete
|
|
1487
|
+
}
|
|
1488
|
+
),
|
|
1489
|
+
isAnnotationMode && /* @__PURE__ */ jsx12(
|
|
1490
|
+
AnnotationOverlay,
|
|
1491
|
+
{
|
|
1492
|
+
slides,
|
|
1493
|
+
currentSlide,
|
|
1494
|
+
slideContainerRef,
|
|
1495
|
+
selectedId: selectedAnnotationId,
|
|
1496
|
+
onSelectId: setSelectedAnnotationId,
|
|
1497
|
+
onShowPanel: () => setShowAnnotationPanel(true),
|
|
1498
|
+
slideAnnotations: getSlideAnnotations(currentSlide),
|
|
1499
|
+
addAnnotation: effectiveAdd ?? (() => {
|
|
1500
|
+
})
|
|
1501
|
+
}
|
|
1502
|
+
)
|
|
1503
|
+
] }) }),
|
|
1504
|
+
!isPresentationMode && /* @__PURE__ */ jsxs6("div", { className: "mt-6 flex items-center gap-4", children: [
|
|
1505
|
+
/* @__PURE__ */ jsx12(
|
|
1506
|
+
"button",
|
|
1507
|
+
{
|
|
1508
|
+
onClick: goBack,
|
|
1509
|
+
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",
|
|
1510
|
+
children: /* @__PURE__ */ jsx12(ChevronLeft, { className: "h-5 w-5" })
|
|
1511
|
+
}
|
|
1512
|
+
),
|
|
1513
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex min-w-[4rem] flex-col items-center", children: [
|
|
1514
|
+
/* @__PURE__ */ jsxs6("span", { className: "font-mono text-sm text-neutral-500", children: [
|
|
1515
|
+
currentSlide + 1,
|
|
1516
|
+
" / ",
|
|
1517
|
+
slides.length
|
|
1518
|
+
] }),
|
|
1519
|
+
slides[currentSlide]?.title && /* @__PURE__ */ jsx12("span", { className: "mt-0.5 text-xs text-neutral-600", children: slides[currentSlide].title })
|
|
1520
|
+
] }),
|
|
1521
|
+
/* @__PURE__ */ jsx12(
|
|
1522
|
+
"button",
|
|
1523
|
+
{
|
|
1524
|
+
onClick: advance,
|
|
1525
|
+
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",
|
|
1526
|
+
children: /* @__PURE__ */ jsx12(ChevronRight, { className: "h-5 w-5" })
|
|
1527
|
+
}
|
|
1528
|
+
)
|
|
1529
|
+
] })
|
|
1530
|
+
]
|
|
930
1531
|
}
|
|
931
|
-
)
|
|
932
|
-
|
|
1532
|
+
),
|
|
1533
|
+
isAnnotationMode && showAnnotationPanel && !isPresentationMode && /* @__PURE__ */ jsx12(
|
|
1534
|
+
AnnotationPanel,
|
|
933
1535
|
{
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
onTransitionComplete
|
|
943
|
-
}
|
|
944
|
-
) }) }),
|
|
945
|
-
!isPresentationMode && /* @__PURE__ */ jsxs2("div", { className: "mt-6 flex items-center gap-4", children: [
|
|
946
|
-
/* @__PURE__ */ jsx8(
|
|
947
|
-
"button",
|
|
948
|
-
{
|
|
949
|
-
onClick: goBack,
|
|
950
|
-
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",
|
|
951
|
-
children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "h-5 w-5" })
|
|
1536
|
+
annotations: getSlideAnnotations(currentSlide),
|
|
1537
|
+
selectedId: selectedAnnotationId,
|
|
1538
|
+
onSelect: setSelectedAnnotationId,
|
|
1539
|
+
onDelete: effectiveDelete ?? (() => {
|
|
1540
|
+
}),
|
|
1541
|
+
onClose: () => {
|
|
1542
|
+
setShowAnnotationPanel(false);
|
|
1543
|
+
setSelectedAnnotationId(null);
|
|
952
1544
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
/* @__PURE__ */ jsxs2("span", { className: "font-mono text-sm text-neutral-500", children: [
|
|
956
|
-
currentSlide + 1,
|
|
957
|
-
" / ",
|
|
958
|
-
slides.length
|
|
959
|
-
] }),
|
|
960
|
-
slides[currentSlide]?.title && /* @__PURE__ */ jsx8("span", { className: "mt-0.5 text-xs text-neutral-600", children: slides[currentSlide].title })
|
|
961
|
-
] }),
|
|
962
|
-
/* @__PURE__ */ jsx8(
|
|
963
|
-
"button",
|
|
964
|
-
{
|
|
965
|
-
onClick: advance,
|
|
966
|
-
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",
|
|
967
|
-
children: /* @__PURE__ */ jsx8(ChevronRight, { className: "h-5 w-5" })
|
|
968
|
-
}
|
|
969
|
-
)
|
|
970
|
-
] })
|
|
1545
|
+
}
|
|
1546
|
+
)
|
|
971
1547
|
]
|
|
972
1548
|
}
|
|
973
1549
|
),
|
|
974
|
-
viewMode === "grid" && /* @__PURE__ */
|
|
1550
|
+
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) => {
|
|
975
1551
|
const SlideComponent = slideConfig.component;
|
|
976
1552
|
const prevSection = index > 0 ? slides[index - 1]?.section : void 0;
|
|
977
1553
|
const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection;
|
|
978
|
-
return /* @__PURE__ */
|
|
979
|
-
showSectionHeader && /* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
1554
|
+
return /* @__PURE__ */ jsxs6("div", { className: showSectionHeader ? "col-span-full" : void 0, children: [
|
|
1555
|
+
showSectionHeader && /* @__PURE__ */ jsx12("h3", { className: "mt-4 mb-3 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0", children: slideConfig.section }),
|
|
1556
|
+
/* @__PURE__ */ jsxs6(
|
|
981
1557
|
"button",
|
|
982
1558
|
{
|
|
983
1559
|
onClick: () => {
|
|
@@ -986,30 +1562,30 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
986
1562
|
},
|
|
987
1563
|
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",
|
|
988
1564
|
children: [
|
|
989
|
-
/* @__PURE__ */
|
|
1565
|
+
/* @__PURE__ */ jsx12(
|
|
990
1566
|
"div",
|
|
991
1567
|
{
|
|
992
1568
|
className: "h-full w-full origin-top-left scale-[0.25]",
|
|
993
1569
|
style: { width: "400%", height: "400%" },
|
|
994
|
-
children: /* @__PURE__ */
|
|
1570
|
+
children: /* @__PURE__ */ jsx12(
|
|
995
1571
|
AnimationProvider,
|
|
996
1572
|
{
|
|
997
1573
|
currentStep: slideConfig.steps,
|
|
998
1574
|
totalSteps: slideConfig.steps,
|
|
999
1575
|
showAllAnimations: true,
|
|
1000
|
-
children: /* @__PURE__ */
|
|
1576
|
+
children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slideConfig.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: slides.length }) })
|
|
1001
1577
|
}
|
|
1002
1578
|
)
|
|
1003
1579
|
}
|
|
1004
1580
|
),
|
|
1005
|
-
/* @__PURE__ */
|
|
1006
|
-
/* @__PURE__ */
|
|
1581
|
+
/* @__PURE__ */ jsx12("div", { className: "absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20" }),
|
|
1582
|
+
/* @__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 })
|
|
1007
1583
|
]
|
|
1008
1584
|
}
|
|
1009
1585
|
)
|
|
1010
1586
|
] }, index);
|
|
1011
1587
|
}) }) }),
|
|
1012
|
-
/* @__PURE__ */
|
|
1588
|
+
/* @__PURE__ */ jsx12(
|
|
1013
1589
|
"div",
|
|
1014
1590
|
{
|
|
1015
1591
|
className: cn(
|
|
@@ -1017,19 +1593,19 @@ function SlideDeck({ slides, transition, directionalTransition }) {
|
|
|
1017
1593
|
"print:m-0 print:block print:max-w-none print:p-0",
|
|
1018
1594
|
viewMode === "list" ? "block" : "hidden print:block"
|
|
1019
1595
|
),
|
|
1020
|
-
children: /* @__PURE__ */
|
|
1596
|
+
children: /* @__PURE__ */ jsx12("div", { className: "grid grid-cols-1 gap-8 print:block", children: slides.map((slideConfig, index) => {
|
|
1021
1597
|
const SlideComponent = slideConfig.component;
|
|
1022
|
-
return /* @__PURE__ */
|
|
1598
|
+
return /* @__PURE__ */ jsx12(
|
|
1023
1599
|
"div",
|
|
1024
1600
|
{
|
|
1025
1601
|
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",
|
|
1026
|
-
children: /* @__PURE__ */
|
|
1602
|
+
children: /* @__PURE__ */ jsx12("div", { className: "h-full w-full print:h-[720px] print:w-[1280px] print:origin-top-left print:scale-[1.5]", children: /* @__PURE__ */ jsx12(
|
|
1027
1603
|
AnimationProvider,
|
|
1028
1604
|
{
|
|
1029
1605
|
currentStep: slideConfig.steps,
|
|
1030
1606
|
totalSteps: slideConfig.steps,
|
|
1031
1607
|
showAllAnimations: true,
|
|
1032
|
-
children: /* @__PURE__ */
|
|
1608
|
+
children: /* @__PURE__ */ jsx12(SlideErrorBoundary, { slideIndex: index, slideTitle: slideConfig.title, children: /* @__PURE__ */ jsx12(SlideComponent, { slideNumber: index + 1, totalSlides: slides.length }) })
|
|
1033
1609
|
}
|
|
1034
1610
|
) })
|
|
1035
1611
|
},
|
|
@@ -1044,6 +1620,7 @@ export {
|
|
|
1044
1620
|
Animated,
|
|
1045
1621
|
AnimatedGroup,
|
|
1046
1622
|
AnimationProvider,
|
|
1623
|
+
AnnotationOverlay,
|
|
1047
1624
|
DEFAULT_SLIDE_TRANSITION,
|
|
1048
1625
|
EASE_DEFAULT,
|
|
1049
1626
|
EASE_IN,
|
|
@@ -1074,11 +1651,13 @@ export {
|
|
|
1074
1651
|
SlideThemeProvider,
|
|
1075
1652
|
cn,
|
|
1076
1653
|
createDirectionalVariants,
|
|
1654
|
+
createHttpAdapter,
|
|
1077
1655
|
directionalSlideX,
|
|
1078
1656
|
directionalSlideY,
|
|
1079
1657
|
getSlideTransition,
|
|
1080
1658
|
getSlideVariants,
|
|
1081
1659
|
useAnimationContext,
|
|
1660
|
+
useAnnotations,
|
|
1082
1661
|
useSlideNavigation,
|
|
1083
1662
|
useTheme
|
|
1084
1663
|
};
|