apostil 0.1.5 → 0.1.7
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/README.md +7 -1
- package/dist/index.cjs +117 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +117 -48
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -93,6 +93,7 @@ var ApostilContext = createContext(null);
|
|
|
93
93
|
function ApostilProvider({
|
|
94
94
|
pageId,
|
|
95
95
|
storage,
|
|
96
|
+
brandColor = "#171717",
|
|
96
97
|
children
|
|
97
98
|
}) {
|
|
98
99
|
const adapter = storage ?? defaultAdapter;
|
|
@@ -110,6 +111,7 @@ function ApostilProvider({
|
|
|
110
111
|
useEffect(() => {
|
|
111
112
|
pageIdRef.current = pageId;
|
|
112
113
|
setLoaded(false);
|
|
114
|
+
hadThreadsRef.current = false;
|
|
113
115
|
debug.log("loading threads for pageId:", pageId);
|
|
114
116
|
adapter.load(pageId).then((t) => {
|
|
115
117
|
if (pageIdRef.current === pageId) {
|
|
@@ -119,10 +121,17 @@ function ApostilProvider({
|
|
|
119
121
|
}
|
|
120
122
|
});
|
|
121
123
|
}, [pageId]);
|
|
124
|
+
const hadThreadsRef = useRef(false);
|
|
122
125
|
useEffect(() => {
|
|
123
|
-
if (loaded
|
|
126
|
+
if (!loaded || pageIdRef.current !== pageId) return;
|
|
127
|
+
if (threads.length > 0) {
|
|
128
|
+
hadThreadsRef.current = true;
|
|
124
129
|
debug.log("saving", threads.length, "threads for pageId:", pageId);
|
|
125
130
|
adapter.save(pageId, threads);
|
|
131
|
+
} else if (hadThreadsRef.current) {
|
|
132
|
+
debug.log("saving empty threads for pageId:", pageId);
|
|
133
|
+
adapter.save(pageId, threads);
|
|
134
|
+
hadThreadsRef.current = false;
|
|
126
135
|
}
|
|
127
136
|
}, [threads, pageId, loaded]);
|
|
128
137
|
const setUser = useCallback((name) => {
|
|
@@ -184,6 +193,7 @@ function ApostilProvider({
|
|
|
184
193
|
commentMode,
|
|
185
194
|
activeThreadId,
|
|
186
195
|
sidebarOpen,
|
|
196
|
+
brandColor,
|
|
187
197
|
setCommentMode,
|
|
188
198
|
setActiveThreadId,
|
|
189
199
|
setSidebarOpen,
|
|
@@ -417,7 +427,11 @@ function OverlayPin({
|
|
|
417
427
|
useEffect2(() => {
|
|
418
428
|
updatePos();
|
|
419
429
|
window.addEventListener("resize", updatePos);
|
|
420
|
-
|
|
430
|
+
document.addEventListener("scroll", updatePos, true);
|
|
431
|
+
return () => {
|
|
432
|
+
window.removeEventListener("resize", updatePos);
|
|
433
|
+
document.removeEventListener("scroll", updatePos, true);
|
|
434
|
+
};
|
|
421
435
|
}, [updatePos]);
|
|
422
436
|
if (!pos) return null;
|
|
423
437
|
return /* @__PURE__ */ jsx2(
|
|
@@ -450,6 +464,7 @@ function CommentPin({
|
|
|
450
464
|
index,
|
|
451
465
|
overlayRef
|
|
452
466
|
}) {
|
|
467
|
+
if (thread.resolved) return null;
|
|
453
468
|
if (thread.targetId) {
|
|
454
469
|
return /* @__PURE__ */ jsx2(TargetedPin, { thread, index }, `targeted-${thread.id}`);
|
|
455
470
|
}
|
|
@@ -529,11 +544,15 @@ function CommentComposer({
|
|
|
529
544
|
placeholder = "Add a comment...",
|
|
530
545
|
autoFocus = false
|
|
531
546
|
}) {
|
|
547
|
+
const { brandColor } = useApostil();
|
|
532
548
|
const [value, setValue] = useState3("");
|
|
533
549
|
const inputRef = useRef3(null);
|
|
534
550
|
useEffect3(() => {
|
|
535
551
|
if (autoFocus) {
|
|
536
|
-
|
|
552
|
+
const timer = setTimeout(() => {
|
|
553
|
+
inputRef.current?.focus();
|
|
554
|
+
}, 50);
|
|
555
|
+
return () => clearTimeout(timer);
|
|
537
556
|
}
|
|
538
557
|
}, [autoFocus]);
|
|
539
558
|
const handleSubmit = () => {
|
|
@@ -557,7 +576,7 @@ function CommentComposer({
|
|
|
557
576
|
},
|
|
558
577
|
placeholder,
|
|
559
578
|
rows: 1,
|
|
560
|
-
className: "flex-1 resize-none rounded-lg border border-neutral-200 bg-white px-3 py-
|
|
579
|
+
className: "flex-1 resize-none rounded-lg border border-neutral-200 bg-white px-3 py-1.5 text-sm\n placeholder:text-neutral-400 focus:outline-none focus:ring-2 focus:ring-neutral-300\n min-h-[36px] max-h-[120px]"
|
|
561
580
|
}
|
|
562
581
|
),
|
|
563
582
|
/* @__PURE__ */ jsx4(
|
|
@@ -565,7 +584,8 @@ function CommentComposer({
|
|
|
565
584
|
{
|
|
566
585
|
onClick: handleSubmit,
|
|
567
586
|
disabled: !value.trim(),
|
|
568
|
-
className: "flex items-center justify-center w-
|
|
587
|
+
className: "flex items-center justify-center w-9 h-9 rounded-lg\n text-white disabled:opacity-30\n transition-colors shrink-0",
|
|
588
|
+
style: { backgroundColor: brandColor },
|
|
569
589
|
children: /* @__PURE__ */ jsx4(Send, { className: "w-3.5 h-3.5" })
|
|
570
590
|
}
|
|
571
591
|
)
|
|
@@ -592,9 +612,17 @@ function ApostilThreadPopover({
|
|
|
592
612
|
const ref = useRef4(null);
|
|
593
613
|
const isOpen = activeThreadId === thread.id;
|
|
594
614
|
const [pos, setPos] = useState4(null);
|
|
615
|
+
const [flip, setFlip] = useState4({ x: false, y: false });
|
|
595
616
|
const updatePos = useCallback3(() => {
|
|
596
617
|
setPos(resolvePosition(thread, overlayRef.current));
|
|
597
618
|
}, [thread, overlayRef]);
|
|
619
|
+
useEffect4(() => {
|
|
620
|
+
if (!isOpen || !pos || !ref.current) return;
|
|
621
|
+
const rect = ref.current.getBoundingClientRect();
|
|
622
|
+
const flipX = rect.right > window.innerWidth;
|
|
623
|
+
const flipY = rect.bottom > window.innerHeight;
|
|
624
|
+
setFlip({ x: flipX, y: flipY });
|
|
625
|
+
}, [isOpen, pos]);
|
|
598
626
|
useEffect4(() => {
|
|
599
627
|
if (!isOpen) return;
|
|
600
628
|
updatePos();
|
|
@@ -623,8 +651,14 @@ function ApostilThreadPopover({
|
|
|
623
651
|
"div",
|
|
624
652
|
{
|
|
625
653
|
ref,
|
|
626
|
-
className: "absolute z-[70]
|
|
627
|
-
style: {
|
|
654
|
+
className: "absolute z-[70]",
|
|
655
|
+
style: {
|
|
656
|
+
left: pos.left,
|
|
657
|
+
top: pos.top,
|
|
658
|
+
marginLeft: flip.x ? -340 : 20,
|
|
659
|
+
marginTop: flip.y ? -12 : -12,
|
|
660
|
+
...flip.y ? { transform: "translateY(-100%)" } : {}
|
|
661
|
+
},
|
|
628
662
|
onClick: (e) => e.stopPropagation(),
|
|
629
663
|
children: /* @__PURE__ */ jsxs4("div", { className: "w-80 bg-white rounded-xl shadow-2xl border border-neutral-200 overflow-hidden", children: [
|
|
630
664
|
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between px-4 py-2.5 border-b border-neutral-100 bg-neutral-50", children: [
|
|
@@ -690,7 +724,7 @@ function ApostilThreadPopover({
|
|
|
690
724
|
import { useState as useState5 } from "react";
|
|
691
725
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
692
726
|
function UserPrompt() {
|
|
693
|
-
const { user, setUser, commentMode } = useApostil();
|
|
727
|
+
const { user, setUser, commentMode, brandColor } = useApostil();
|
|
694
728
|
const [name, setName] = useState5("");
|
|
695
729
|
if (user || !commentMode) return null;
|
|
696
730
|
const handleSubmit = () => {
|
|
@@ -726,7 +760,8 @@ function UserPrompt() {
|
|
|
726
760
|
{
|
|
727
761
|
onClick: handleSubmit,
|
|
728
762
|
disabled: !name.trim(),
|
|
729
|
-
className: "w-full py-2 rounded-lg
|
|
763
|
+
className: "w-full py-2 rounded-lg text-white text-sm font-medium\n disabled:opacity-30 transition-colors",
|
|
764
|
+
style: { backgroundColor: brandColor },
|
|
730
765
|
children: "Continue"
|
|
731
766
|
}
|
|
732
767
|
)
|
|
@@ -877,18 +912,26 @@ function findCommentTarget(el, boundary) {
|
|
|
877
912
|
};
|
|
878
913
|
}
|
|
879
914
|
function CommentOverlay() {
|
|
880
|
-
const { threads, commentMode, setCommentMode, user, addThread, setActiveThreadId } = useApostil();
|
|
915
|
+
const { threads, commentMode, setCommentMode, user, addThread, activeThreadId, setActiveThreadId, brandColor } = useApostil();
|
|
881
916
|
const overlayRef = useRef5(null);
|
|
882
917
|
const [pendingPin, setPendingPin] = useState6(null);
|
|
883
918
|
const [pendingPixel, setPendingPixel] = useState6(null);
|
|
919
|
+
const pendingRef = useRef5(null);
|
|
920
|
+
const [pendingFlip, setPendingFlip] = useState6({ x: false, y: false });
|
|
884
921
|
const handleClick = useCallback4(
|
|
885
922
|
(e) => {
|
|
886
923
|
if (!commentMode || !overlayRef.current) return;
|
|
887
924
|
const overlayRect = overlayRef.current.getBoundingClientRect();
|
|
888
925
|
const overlay = overlayRef.current;
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
926
|
+
const elements = document.elementsFromPoint(e.clientX, e.clientY);
|
|
927
|
+
let elementBelow = null;
|
|
928
|
+
for (const el of elements) {
|
|
929
|
+
if (el === overlay || overlay.contains(el)) continue;
|
|
930
|
+
if (el instanceof HTMLElement) {
|
|
931
|
+
elementBelow = el;
|
|
932
|
+
break;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
892
935
|
debug.log(" click at", { clientX: e.clientX, clientY: e.clientY });
|
|
893
936
|
debug.log(" element below overlay:", elementBelow);
|
|
894
937
|
if (elementBelow) {
|
|
@@ -957,6 +1000,16 @@ function CommentOverlay() {
|
|
|
957
1000
|
setPendingPixel(null);
|
|
958
1001
|
setCommentMode(false);
|
|
959
1002
|
}, [setCommentMode]);
|
|
1003
|
+
useEffect5(() => {
|
|
1004
|
+
if (!pendingPixel || !overlayRef.current) return;
|
|
1005
|
+
const overlayRect = overlayRef.current.getBoundingClientRect();
|
|
1006
|
+
const clickX = overlayRect.left + pendingPixel.left;
|
|
1007
|
+
const clickY = overlayRect.top + pendingPixel.top;
|
|
1008
|
+
setPendingFlip({
|
|
1009
|
+
x: clickX + 308 > window.innerWidth,
|
|
1010
|
+
y: clickY + 200 > window.innerHeight
|
|
1011
|
+
});
|
|
1012
|
+
}, [pendingPixel, overlayRef]);
|
|
960
1013
|
useEffect5(() => {
|
|
961
1014
|
const hash = window.location.hash;
|
|
962
1015
|
console.log("[apostil] hash check:", hash, "threads:", threads.length, threads.map((t) => t.id));
|
|
@@ -982,6 +1035,8 @@ function CommentOverlay() {
|
|
|
982
1035
|
setPendingPin(null);
|
|
983
1036
|
setPendingPixel(null);
|
|
984
1037
|
setCommentMode(false);
|
|
1038
|
+
} else if (activeThreadId) {
|
|
1039
|
+
setActiveThreadId(null);
|
|
985
1040
|
} else if (commentMode) {
|
|
986
1041
|
setCommentMode(false);
|
|
987
1042
|
}
|
|
@@ -1003,11 +1058,8 @@ function CommentOverlay() {
|
|
|
1003
1058
|
};
|
|
1004
1059
|
document.addEventListener("keydown", handler);
|
|
1005
1060
|
return () => document.removeEventListener("keydown", handler);
|
|
1006
|
-
}, [commentMode, pendingPin, setCommentMode, setActiveThreadId]);
|
|
1007
|
-
const
|
|
1008
|
-
if (a.resolved !== b.resolved) return a.resolved ? 1 : -1;
|
|
1009
|
-
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
1010
|
-
});
|
|
1061
|
+
}, [commentMode, pendingPin, activeThreadId, setCommentMode, setActiveThreadId]);
|
|
1062
|
+
const visibleThreads = threads.filter((t) => !t.resolved).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
1011
1063
|
const showingUserPrompt = commentMode && !user;
|
|
1012
1064
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1013
1065
|
/* @__PURE__ */ jsxs6(
|
|
@@ -1018,7 +1070,7 @@ function CommentOverlay() {
|
|
|
1018
1070
|
style: { zIndex: commentMode ? getHighestZIndex() + 10 : 55 },
|
|
1019
1071
|
onMouseDown: handleClick,
|
|
1020
1072
|
children: [
|
|
1021
|
-
|
|
1073
|
+
visibleThreads.map((thread, i) => /* @__PURE__ */ jsxs6("div", { className: "pointer-events-auto", children: [
|
|
1022
1074
|
/* @__PURE__ */ jsx7(CommentPin, { thread, index: i, overlayRef }),
|
|
1023
1075
|
/* @__PURE__ */ jsx7(ApostilThreadPopover, { thread, overlayRef })
|
|
1024
1076
|
] }, thread.id)),
|
|
@@ -1041,35 +1093,46 @@ function CommentOverlay() {
|
|
|
1041
1093
|
children: "+"
|
|
1042
1094
|
}
|
|
1043
1095
|
),
|
|
1044
|
-
/* @__PURE__ */ jsx7(
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1096
|
+
/* @__PURE__ */ jsx7(
|
|
1097
|
+
"div",
|
|
1098
|
+
{
|
|
1099
|
+
ref: pendingRef,
|
|
1100
|
+
className: "absolute w-72",
|
|
1101
|
+
style: {
|
|
1102
|
+
marginLeft: pendingFlip.x ? -308 : 20,
|
|
1103
|
+
marginTop: pendingFlip.y ? void 0 : -12,
|
|
1104
|
+
...pendingFlip.y ? { bottom: 0 } : {}
|
|
1105
|
+
},
|
|
1106
|
+
children: /* @__PURE__ */ jsxs6("div", { className: "bg-white rounded-xl shadow-2xl border border-neutral-200 p-3", children: [
|
|
1107
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
1108
|
+
/* @__PURE__ */ jsx7("p", { className: "text-xs text-neutral-500", children: "New comment" }),
|
|
1109
|
+
pendingPin.targetLabel && /* @__PURE__ */ jsx7("span", { className: "text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium", children: pendingPin.targetLabel })
|
|
1110
|
+
] }),
|
|
1111
|
+
/* @__PURE__ */ jsx7(
|
|
1112
|
+
CommentComposer,
|
|
1113
|
+
{
|
|
1114
|
+
onSubmit: handleNewComment,
|
|
1115
|
+
placeholder: "What's on your mind?",
|
|
1116
|
+
autoFocus: true
|
|
1117
|
+
}
|
|
1118
|
+
)
|
|
1119
|
+
] })
|
|
1120
|
+
}
|
|
1121
|
+
)
|
|
1066
1122
|
]
|
|
1067
1123
|
}
|
|
1068
1124
|
)
|
|
1069
1125
|
]
|
|
1070
1126
|
}
|
|
1071
1127
|
),
|
|
1072
|
-
commentMode && !pendingPin && !showingUserPrompt && /* @__PURE__ */ jsx7("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 z-[60] pointer-events-none", children: /* @__PURE__ */ jsx7(
|
|
1128
|
+
commentMode && !pendingPin && !showingUserPrompt && /* @__PURE__ */ jsx7("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 z-[60] pointer-events-none", children: /* @__PURE__ */ jsx7(
|
|
1129
|
+
"div",
|
|
1130
|
+
{
|
|
1131
|
+
className: "text-white text-sm px-4 py-2 rounded-full backdrop-blur-sm",
|
|
1132
|
+
style: { backgroundColor: `color-mix(in oklab, ${brandColor} 80%, transparent)` },
|
|
1133
|
+
children: "Click anywhere to add a comment"
|
|
1134
|
+
}
|
|
1135
|
+
) }),
|
|
1073
1136
|
/* @__PURE__ */ jsx7(UserPrompt, {})
|
|
1074
1137
|
] });
|
|
1075
1138
|
}
|
|
@@ -1083,7 +1146,8 @@ function CommentToggle() {
|
|
|
1083
1146
|
sidebarOpen,
|
|
1084
1147
|
setSidebarOpen,
|
|
1085
1148
|
unresolvedCount,
|
|
1086
|
-
setActiveThreadId
|
|
1149
|
+
setActiveThreadId,
|
|
1150
|
+
brandColor
|
|
1087
1151
|
} = useApostil();
|
|
1088
1152
|
return /* @__PURE__ */ jsxs7("div", { className: "absolute bottom-5 right-5 z-[65] flex flex-col gap-2 items-end", children: [
|
|
1089
1153
|
/* @__PURE__ */ jsx8(
|
|
@@ -1092,7 +1156,8 @@ function CommentToggle() {
|
|
|
1092
1156
|
onClick: () => setSidebarOpen(!sidebarOpen),
|
|
1093
1157
|
className: `flex items-center justify-center w-10 h-10 rounded-full shadow-lg
|
|
1094
1158
|
transition-all duration-200 hover:scale-105
|
|
1095
|
-
${sidebarOpen ? "
|
|
1159
|
+
${sidebarOpen ? "text-white" : "bg-white text-neutral-600 hover:bg-neutral-50 border border-neutral-200"}`,
|
|
1160
|
+
style: sidebarOpen ? { backgroundColor: brandColor } : void 0,
|
|
1096
1161
|
title: "Toggle comment list",
|
|
1097
1162
|
children: /* @__PURE__ */ jsx8(List, { className: "w-4 h-4" })
|
|
1098
1163
|
}
|
|
@@ -1110,7 +1175,8 @@ function CommentToggle() {
|
|
|
1110
1175
|
},
|
|
1111
1176
|
className: `relative flex items-center justify-center w-12 h-12 rounded-full shadow-lg
|
|
1112
1177
|
transition-all duration-200 hover:scale-105
|
|
1113
|
-
${commentMode ? "
|
|
1178
|
+
${commentMode ? "text-white ring-2 ring-neutral-400" : "bg-white text-neutral-700 hover:bg-neutral-50 border border-neutral-200"}`,
|
|
1179
|
+
style: commentMode ? { backgroundColor: brandColor } : void 0,
|
|
1114
1180
|
title: commentMode ? "Exit comment mode" : "Add comment",
|
|
1115
1181
|
children: [
|
|
1116
1182
|
commentMode ? /* @__PURE__ */ jsx8(X, { className: "w-5 h-5" }) : /* @__PURE__ */ jsx8(MessageSquare, { className: "w-5 h-5" }),
|
|
@@ -1143,7 +1209,8 @@ function CommentSidebar() {
|
|
|
1143
1209
|
sidebarOpen,
|
|
1144
1210
|
setSidebarOpen,
|
|
1145
1211
|
setActiveThreadId,
|
|
1146
|
-
resolveThread
|
|
1212
|
+
resolveThread,
|
|
1213
|
+
brandColor
|
|
1147
1214
|
} = useApostil();
|
|
1148
1215
|
const [tab, setTab] = useState7("page");
|
|
1149
1216
|
const [allPages, setAllPages] = useState7([]);
|
|
@@ -1187,7 +1254,8 @@ function CommentSidebar() {
|
|
|
1187
1254
|
"button",
|
|
1188
1255
|
{
|
|
1189
1256
|
onClick: () => setTab("page"),
|
|
1190
|
-
className: `flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${tab === "page" ? "
|
|
1257
|
+
className: `flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${tab === "page" ? "border-b-2" : "text-neutral-400 hover:text-neutral-600"}`,
|
|
1258
|
+
style: tab === "page" ? { color: brandColor, borderColor: brandColor } : void 0,
|
|
1191
1259
|
children: [
|
|
1192
1260
|
/* @__PURE__ */ jsx9(FileText, { className: "w-3 h-3" }),
|
|
1193
1261
|
"This Page",
|
|
@@ -1199,7 +1267,8 @@ function CommentSidebar() {
|
|
|
1199
1267
|
"button",
|
|
1200
1268
|
{
|
|
1201
1269
|
onClick: () => setTab("all"),
|
|
1202
|
-
className: `flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${tab === "all" ? "
|
|
1270
|
+
className: `flex-1 flex items-center justify-center gap-1.5 py-2 text-xs font-medium transition-colors ${tab === "all" ? "border-b-2" : "text-neutral-400 hover:text-neutral-600"}`,
|
|
1271
|
+
style: tab === "all" ? { color: brandColor, borderColor: brandColor } : void 0,
|
|
1203
1272
|
children: [
|
|
1204
1273
|
/* @__PURE__ */ jsx9(Globe, { className: "w-3 h-3" }),
|
|
1205
1274
|
"All Pages"
|