@agent-native/core 0.77.8 → 0.77.11
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +35 -0
- package/corpus/core/package.json +3 -3
- package/corpus/core/src/a2a/task-store.ts +25 -3
- package/corpus/core/src/agent/harness/store.ts +89 -55
- package/corpus/core/src/agent/observational-memory/store.ts +40 -14
- package/corpus/core/src/agent/run-store.ts +116 -59
- package/corpus/core/src/application-state/store.ts +36 -2
- package/corpus/core/src/audit/store.ts +37 -6
- package/corpus/core/src/browser-sessions/store.ts +31 -12
- package/corpus/core/src/chat-threads/store.ts +67 -3
- package/corpus/core/src/checkpoints/store.ts +14 -3
- package/corpus/core/src/cli/create.ts +9 -0
- package/corpus/core/src/collab/awareness.ts +12 -9
- package/corpus/core/src/collab/client.ts +4 -2
- package/corpus/core/src/collab/storage.ts +20 -2
- package/corpus/core/src/db/ddl-guard.ts +112 -0
- package/corpus/core/src/extensions/store.ts +76 -3
- package/corpus/core/src/integrations/a2a-continuations-store.ts +69 -26
- package/corpus/core/src/integrations/config-store.ts +19 -18
- package/corpus/core/src/integrations/pending-tasks-store.ts +40 -16
- package/corpus/core/src/integrations/remote-commands-store.ts +37 -23
- package/corpus/core/src/integrations/remote-devices-store.ts +64 -20
- package/corpus/core/src/integrations/remote-push-store.ts +75 -35
- package/corpus/core/src/integrations/remote-run-events-store.ts +38 -12
- package/corpus/core/src/integrations/thread-mapping-store.ts +11 -2
- package/corpus/core/src/mcp/connect-store.ts +53 -16
- package/corpus/core/src/mcp/oauth-store.ts +32 -7
- package/corpus/core/src/notifications/store.ts +20 -4
- package/corpus/core/src/oauth-tokens/store.ts +45 -2
- package/corpus/core/src/observability/store.ts +154 -47
- package/corpus/core/src/progress/store.ts +36 -21
- package/corpus/core/src/provider-api/corpus-jobs-store.ts +25 -5
- package/corpus/core/src/provider-api/custom-registry.ts +15 -4
- package/corpus/core/src/provider-api/quota-governor.ts +15 -3
- package/corpus/core/src/provider-api/staged-datasets-store.ts +22 -5
- package/corpus/core/src/resources/store.ts +66 -31
- package/corpus/core/src/secrets/storage.ts +18 -22
- package/corpus/core/src/server/agent-teams-run-queue.ts +22 -10
- package/corpus/core/src/server/auth.ts +22 -4
- package/corpus/core/src/server/better-auth-instance.ts +49 -15
- package/corpus/core/src/server/embed-session.ts +19 -4
- package/corpus/core/src/server/identity-sso-store.ts +40 -11
- package/corpus/core/src/server/recap-image-store.ts +28 -9
- package/corpus/core/src/settings/store.ts +14 -19
- package/corpus/core/src/usage/store.ts +51 -16
- package/corpus/core/src/workspace-connections/store.ts +200 -22
- package/corpus/templates/calendar/changelog/2026-06-25-booking-link-previews-now-show-the-content-directly-on-the-g.md +6 -0
- package/corpus/templates/calendar/server/lib/booking-og-image.ts +0 -2
- package/corpus/templates/clips/chrome-extension/package.json +1 -1
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +36 -2
- package/corpus/templates/content/actions/move-document.ts +79 -33
- package/corpus/templates/content/actions/restore-content-database.ts +40 -3
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +84 -2
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +177 -49
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +393 -219
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +632 -467
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +114 -14
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +56 -15
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +6 -5
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +12 -4
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +36 -7
- package/corpus/templates/content/app/i18n-data.ts +140 -10
- package/corpus/templates/content/changelog/2026-06-25-comment-cards-now-track-their-highlighted-text-while-scrolli.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-comments-now-scroll-with-the-document-and-stay-below-the-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-creating-a-database-from-the-slash-menu-no-longer-leaves-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-hover-controls-no-longer-leave-a-lingering-fade-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-page-actions-are-now-easier-to-see-when-hovering-ina.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-text-selections-in-the-editor-no-longer-show-white-gaps-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-has-a-one-click-page-link-copy-button.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-shows-page-breadcrumbs-and-the-latest.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-page-command-now-leaves-a-notion-style-page-reference-an.md +6 -0
- package/corpus/templates/content/shared/nfm.ts +16 -0
- package/corpus/templates/forms/changelog/2026-06-25-form-previews-now-show-the-content-directly-on-the-grid-back.md +6 -0
- package/corpus/templates/forms/server/lib/form-og-image.ts +0 -2
- package/corpus/templates/plan/app/global.css +6 -11
- package/corpus/templates/plan/app/pages/PlansPage.tsx +12 -10
- package/corpus/templates/plan/changelog/2026-06-25-plan-access-request-pages-stay-visible-during-background-ref.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-25-plan-documents-keep-their-full-reading-width-below-1200px-an.md +6 -0
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +19 -3
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/agent/harness/store.d.ts.map +1 -1
- package/dist/agent/harness/store.js +63 -34
- package/dist/agent/harness/store.js.map +1 -1
- package/dist/agent/observational-memory/store.d.ts.map +1 -1
- package/dist/agent/observational-memory/store.js +30 -8
- package/dist/agent/observational-memory/store.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +103 -46
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +28 -2
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +18 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/browser-sessions/store.d.ts.map +1 -1
- package/dist/browser-sessions/store.js +22 -7
- package/dist/browser-sessions/store.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +51 -3
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +12 -3
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +9 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/collab/awareness.d.ts +1 -1
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +12 -8
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +4 -3
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +14 -2
- package/dist/collab/storage.js.map +1 -1
- package/dist/db/ddl-guard.d.ts +63 -0
- package/dist/db/ddl-guard.d.ts.map +1 -1
- package/dist/db/ddl-guard.js +85 -0
- package/dist/db/ddl-guard.js.map +1 -1
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +32 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +44 -24
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +17 -11
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +24 -16
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts.map +1 -1
- package/dist/integrations/remote-commands-store.js +29 -21
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.d.ts.map +1 -1
- package/dist/integrations/remote-devices-store.js +33 -18
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.d.ts.map +1 -1
- package/dist/integrations/remote-push-store.js +53 -31
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
- package/dist/integrations/remote-run-events-store.js +26 -10
- package/dist/integrations/remote-run-events-store.js.map +1 -1
- package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
- package/dist/integrations/thread-mapping-store.js +10 -2
- package/dist/integrations/thread-mapping-store.js.map +1 -1
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +34 -16
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp/oauth-store.d.ts.map +1 -1
- package/dist/mcp/oauth-store.js +22 -7
- package/dist/mcp/oauth-store.js.map +1 -1
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/notifications/store.js +15 -3
- package/dist/notifications/store.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +33 -2
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +72 -29
- package/dist/observability/store.js.map +1 -1
- package/dist/progress/store.d.ts.map +1 -1
- package/dist/progress/store.js +31 -16
- package/dist/progress/store.js.map +1 -1
- package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -1
- package/dist/provider-api/corpus-jobs-store.js +16 -5
- package/dist/provider-api/corpus-jobs-store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +14 -4
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/quota-governor.d.ts.map +1 -1
- package/dist/provider-api/quota-governor.js +12 -3
- package/dist/provider-api/quota-governor.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +16 -5
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +56 -25
- package/dist/resources/store.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +10 -14
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
- package/dist/server/agent-teams-run-queue.js +15 -4
- package/dist/server/agent-teams-run-queue.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +16 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +55 -21
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +15 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/identity-sso-store.d.ts.map +1 -1
- package/dist/server/identity-sso-store.js +30 -11
- package/dist/server/identity-sso-store.js.map +1 -1
- package/dist/server/recap-image-store.d.ts.map +1 -1
- package/dist/server/recap-image-store.js +21 -7
- package/dist/server/recap-image-store.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +11 -13
- package/dist/settings/store.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +36 -11
- package/dist/usage/store.js.map +1 -1
- package/dist/workspace-connections/store.d.ts.map +1 -1
- package/dist/workspace-connections/store.js +57 -7
- package/dist/workspace-connections/store.js.map +1 -1
- package/package.json +3 -3
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
TooltipTrigger,
|
|
22
22
|
} from "@/components/ui/tooltip";
|
|
23
23
|
import {
|
|
24
|
-
useComments,
|
|
25
24
|
useCreateComment,
|
|
26
25
|
useResolveComment,
|
|
27
26
|
type CommentThread,
|
|
@@ -88,26 +87,35 @@ function formatDate(dateStr: string) {
|
|
|
88
87
|
return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
function cssEscape(value: string) {
|
|
91
|
+
return globalThis.CSS?.escape
|
|
92
|
+
? globalThis.CSS.escape(value)
|
|
93
|
+
: value.replace(/["\\]/g, "\\$&");
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
/**
|
|
92
|
-
* Y offset (relative to the scroll container's
|
|
97
|
+
* Y offset (relative to the scroll container's visible viewport) of a thread's inline
|
|
93
98
|
* highlight. Prefers the real decoration element rendered by the
|
|
94
99
|
* CommentHighlight plugin (`[data-comment-thread]`); falls back to a text search
|
|
95
100
|
* by quoted text for legacy/unresolved anchors so older comments still line up.
|
|
96
101
|
*/
|
|
97
|
-
function findThreadOffset(
|
|
102
|
+
export function findThreadOffset(
|
|
98
103
|
threadId: string,
|
|
99
104
|
quotedText: string | null,
|
|
100
105
|
scrollContainer: HTMLElement | null,
|
|
106
|
+
positionContainer: HTMLElement | null = scrollContainer,
|
|
101
107
|
): number | null {
|
|
102
108
|
if (!scrollContainer) return null;
|
|
103
|
-
const containerRect =
|
|
109
|
+
const containerRect = (
|
|
110
|
+
positionContainer ?? scrollContainer
|
|
111
|
+
).getBoundingClientRect();
|
|
104
112
|
|
|
105
113
|
const marked = scrollContainer.querySelector(
|
|
106
|
-
`[data-comment-thread="${
|
|
114
|
+
`[data-comment-thread="${cssEscape(threadId)}"]`,
|
|
107
115
|
) as HTMLElement | null;
|
|
108
116
|
if (marked) {
|
|
109
117
|
const rect = marked.getBoundingClientRect();
|
|
110
|
-
return rect.top - containerRect.top
|
|
118
|
+
return rect.top - containerRect.top;
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
if (!quotedText) return null;
|
|
@@ -125,14 +133,46 @@ function findThreadOffset(
|
|
|
125
133
|
const range = window.document.createRange();
|
|
126
134
|
range.selectNode(node);
|
|
127
135
|
const rect = range.getBoundingClientRect();
|
|
128
|
-
return rect.top - containerRect.top
|
|
136
|
+
return rect.top - containerRect.top;
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
return null;
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
export function findPendingCommentOffset(
|
|
143
|
+
scrollContainer: HTMLElement | null,
|
|
144
|
+
positionContainer: HTMLElement | null = scrollContainer,
|
|
145
|
+
): number | null {
|
|
146
|
+
if (!scrollContainer) return null;
|
|
147
|
+
const pending = scrollContainer.querySelector(
|
|
148
|
+
".comment-highlight--pending",
|
|
149
|
+
) as HTMLElement | null;
|
|
150
|
+
if (!pending) return null;
|
|
151
|
+
const containerRect = (
|
|
152
|
+
positionContainer ?? scrollContainer
|
|
153
|
+
).getBoundingClientRect();
|
|
154
|
+
const rect = pending.getBoundingClientRect();
|
|
155
|
+
return rect.top - containerRect.top;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function shouldClearSelectedThreadOnScroll(
|
|
159
|
+
offset: number | null,
|
|
160
|
+
containerHeight: number,
|
|
161
|
+
margin = 40,
|
|
162
|
+
) {
|
|
163
|
+
return (
|
|
164
|
+
offset == null || offset < -margin || offset > containerHeight + margin
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function estimateThreadCardHeight(thread: CommentThread) {
|
|
169
|
+
return 80 + Math.max(0, thread.comments.length - 1) * 44;
|
|
170
|
+
}
|
|
171
|
+
|
|
134
172
|
interface CommentsSidebarProps {
|
|
135
173
|
documentId: string;
|
|
174
|
+
threads?: CommentThread[];
|
|
175
|
+
isLoading?: boolean;
|
|
136
176
|
pendingComment?: {
|
|
137
177
|
quotedText: string;
|
|
138
178
|
offsetTop: number;
|
|
@@ -142,30 +182,36 @@ interface CommentsSidebarProps {
|
|
|
142
182
|
onPendingDone?: () => void;
|
|
143
183
|
scrollContainerRef?: RefObject<HTMLDivElement | null>;
|
|
144
184
|
activeThreadId?: string | null;
|
|
145
|
-
|
|
185
|
+
selectedThreadId?: string | null;
|
|
186
|
+
onSelectedThreadChange?: (id: string | null) => void;
|
|
187
|
+
onHoveredThreadChange?: (id: string | null) => void;
|
|
146
188
|
currentUserEmail?: string;
|
|
147
189
|
}
|
|
148
190
|
|
|
149
191
|
export function CommentsSidebar({
|
|
150
192
|
documentId,
|
|
193
|
+
threads = [],
|
|
194
|
+
isLoading = false,
|
|
151
195
|
pendingComment,
|
|
152
196
|
onPendingDone,
|
|
153
197
|
scrollContainerRef,
|
|
154
198
|
activeThreadId,
|
|
155
|
-
|
|
199
|
+
selectedThreadId,
|
|
200
|
+
onSelectedThreadChange,
|
|
201
|
+
onHoveredThreadChange,
|
|
156
202
|
currentUserEmail,
|
|
157
203
|
}: CommentsSidebarProps) {
|
|
158
204
|
const t = useT();
|
|
159
|
-
const { data: threads, isLoading } = useComments(documentId);
|
|
160
205
|
const { data: members = [] } = useMentionMembers();
|
|
161
206
|
const createComment = useCreateComment();
|
|
162
207
|
const resolveComment = useResolveComment();
|
|
163
|
-
const [
|
|
208
|
+
const [replyingThreadId, setReplyingThreadId] = useState<string | null>(null);
|
|
164
209
|
const [replyText, setReplyText] = useState("");
|
|
165
210
|
const [replyMentions, setReplyMentions] = useState<MentionEntry[]>([]);
|
|
166
211
|
const [pendingText, setPendingText] = useState("");
|
|
167
212
|
const [pendingMentions, setPendingMentions] = useState<MentionEntry[]>([]);
|
|
168
213
|
const [showResolved, setShowResolved] = useState(false);
|
|
214
|
+
const sidebarRef = useRef<HTMLDivElement>(null);
|
|
169
215
|
const pendingInputRef = useRef<HTMLTextAreaElement>(null);
|
|
170
216
|
|
|
171
217
|
const openThreads = useMemo(
|
|
@@ -225,7 +271,7 @@ export function CommentsSidebar({
|
|
|
225
271
|
});
|
|
226
272
|
setReplyText("");
|
|
227
273
|
setReplyMentions([]);
|
|
228
|
-
|
|
274
|
+
setReplyingThreadId(null);
|
|
229
275
|
};
|
|
230
276
|
|
|
231
277
|
const handleSendToAI = (thread: CommentThread) => {
|
|
@@ -246,25 +292,52 @@ export function CommentsSidebar({
|
|
|
246
292
|
const [threadOffsets, setThreadOffsets] = useState<Map<string, number>>(
|
|
247
293
|
new Map(),
|
|
248
294
|
);
|
|
295
|
+
const [threadCardHeights, setThreadCardHeights] = useState<
|
|
296
|
+
Map<string, number>
|
|
297
|
+
>(new Map());
|
|
298
|
+
const [pendingOffset, setPendingOffset] = useState<number | null>(null);
|
|
249
299
|
const openThreadKey = openThreads
|
|
250
300
|
.map((t) => `${t.threadId}:${t.quotedText ?? ""}`)
|
|
251
301
|
.join(",");
|
|
252
302
|
|
|
303
|
+
const handleThreadCardHeightChange = useCallback(
|
|
304
|
+
(threadId: string, height: number) => {
|
|
305
|
+
setThreadCardHeights((prev) => {
|
|
306
|
+
if (prev.get(threadId) === height) return prev;
|
|
307
|
+
const next = new Map(prev);
|
|
308
|
+
next.set(threadId, height);
|
|
309
|
+
return next;
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
[],
|
|
313
|
+
);
|
|
314
|
+
|
|
253
315
|
const recomputeOffsets = useCallback(() => {
|
|
254
316
|
const container = scrollContainerRef?.current ?? null;
|
|
255
317
|
if (!container || openThreads.length === 0) {
|
|
256
318
|
setThreadOffsets((prev) => (prev.size === 0 ? prev : new Map()));
|
|
319
|
+
setPendingOffset((prev) => {
|
|
320
|
+
const next = pendingComment
|
|
321
|
+
? findPendingCommentOffset(container, sidebarRef.current ?? container)
|
|
322
|
+
: null;
|
|
323
|
+
return prev === next ? prev : next;
|
|
324
|
+
});
|
|
257
325
|
return;
|
|
258
326
|
}
|
|
327
|
+
const positionContainer = sidebarRef.current ?? container;
|
|
259
328
|
const offsets = new Map<string, number>();
|
|
260
329
|
for (const thread of openThreads) {
|
|
261
330
|
const offset = findThreadOffset(
|
|
262
331
|
thread.threadId,
|
|
263
332
|
thread.quotedText,
|
|
264
333
|
container,
|
|
334
|
+
positionContainer,
|
|
265
335
|
);
|
|
266
336
|
if (offset != null) offsets.set(thread.threadId, offset);
|
|
267
337
|
}
|
|
338
|
+
const nextPendingOffset = pendingComment
|
|
339
|
+
? findPendingCommentOffset(container, positionContainer)
|
|
340
|
+
: null;
|
|
268
341
|
setThreadOffsets((prev) => {
|
|
269
342
|
if (
|
|
270
343
|
prev.size === offsets.size &&
|
|
@@ -274,7 +347,28 @@ export function CommentsSidebar({
|
|
|
274
347
|
}
|
|
275
348
|
return offsets;
|
|
276
349
|
});
|
|
277
|
-
|
|
350
|
+
setPendingOffset((prev) =>
|
|
351
|
+
prev === nextPendingOffset ? prev : nextPendingOffset,
|
|
352
|
+
);
|
|
353
|
+
if (
|
|
354
|
+
selectedThreadId &&
|
|
355
|
+
shouldClearSelectedThreadOnScroll(
|
|
356
|
+
offsets.get(selectedThreadId) ?? null,
|
|
357
|
+
container.clientHeight,
|
|
358
|
+
)
|
|
359
|
+
) {
|
|
360
|
+
onSelectedThreadChange?.(null);
|
|
361
|
+
setReplyingThreadId(null);
|
|
362
|
+
setReplyText("");
|
|
363
|
+
setReplyMentions([]);
|
|
364
|
+
}
|
|
365
|
+
}, [
|
|
366
|
+
openThreads,
|
|
367
|
+
onSelectedThreadChange,
|
|
368
|
+
pendingComment,
|
|
369
|
+
scrollContainerRef,
|
|
370
|
+
selectedThreadId,
|
|
371
|
+
]);
|
|
278
372
|
|
|
279
373
|
useEffect(() => {
|
|
280
374
|
const container = scrollContainerRef?.current ?? null;
|
|
@@ -294,34 +388,46 @@ export function CommentsSidebar({
|
|
|
294
388
|
subtree: true,
|
|
295
389
|
characterData: true,
|
|
296
390
|
});
|
|
391
|
+
container.addEventListener("scroll", schedule, { passive: true });
|
|
297
392
|
window.addEventListener("resize", schedule);
|
|
298
393
|
|
|
299
394
|
return () => {
|
|
300
395
|
cancelAnimationFrame(raf);
|
|
301
396
|
observer.disconnect();
|
|
397
|
+
container.removeEventListener("scroll", schedule);
|
|
302
398
|
window.removeEventListener("resize", schedule);
|
|
303
399
|
};
|
|
304
400
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
305
|
-
}, [openThreadKey, recomputeOffsets]);
|
|
401
|
+
}, [openThreadKey, pendingComment, recomputeOffsets]);
|
|
306
402
|
|
|
307
|
-
// When a thread is activated (e.g. its highlight was clicked), expand it and
|
|
308
|
-
// scroll its card into view.
|
|
309
403
|
useEffect(() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
404
|
+
const openIds = new Set(openThreads.map((thread) => thread.threadId));
|
|
405
|
+
setThreadCardHeights((prev) => {
|
|
406
|
+
if ([...prev.keys()].every((threadId) => openIds.has(threadId))) {
|
|
407
|
+
return prev;
|
|
408
|
+
}
|
|
409
|
+
const next = new Map<string, number>();
|
|
410
|
+
for (const [threadId, height] of prev) {
|
|
411
|
+
if (openIds.has(threadId)) next.set(threadId, height);
|
|
412
|
+
}
|
|
413
|
+
return next;
|
|
414
|
+
});
|
|
415
|
+
}, [openThreads]);
|
|
318
416
|
|
|
319
|
-
// Surface the resolved list automatically when there's nothing open to show.
|
|
320
417
|
useEffect(() => {
|
|
321
|
-
if (
|
|
322
|
-
|
|
418
|
+
if (!selectedThreadId) return;
|
|
419
|
+
if (!openThreads.some((t) => t.threadId === selectedThreadId)) {
|
|
420
|
+
onSelectedThreadChange?.(null);
|
|
421
|
+
setReplyingThreadId(null);
|
|
422
|
+
setReplyText("");
|
|
423
|
+
setReplyMentions([]);
|
|
424
|
+
return;
|
|
323
425
|
}
|
|
324
|
-
|
|
426
|
+
const card = window.document.querySelector(
|
|
427
|
+
`[data-thread-card="${cssEscape(selectedThreadId)}"]`,
|
|
428
|
+
);
|
|
429
|
+
card?.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
430
|
+
}, [onSelectedThreadChange, selectedThreadId, openThreads]);
|
|
325
431
|
|
|
326
432
|
const hasContent =
|
|
327
433
|
openThreads.length > 0 || !!pendingComment || resolvedThreads.length > 0;
|
|
@@ -346,7 +452,10 @@ export function CommentsSidebar({
|
|
|
346
452
|
? 0
|
|
347
453
|
: 12;
|
|
348
454
|
items.push({ thread, marginTop });
|
|
349
|
-
cursor +=
|
|
455
|
+
cursor +=
|
|
456
|
+
marginTop +
|
|
457
|
+
(threadCardHeights.get(thread.threadId) ??
|
|
458
|
+
estimateThreadCardHeight(thread));
|
|
350
459
|
}
|
|
351
460
|
|
|
352
461
|
const handleResolve = (thread: CommentThread) => {
|
|
@@ -355,7 +464,12 @@ export function CommentsSidebar({
|
|
|
355
464
|
documentId,
|
|
356
465
|
resolved: true,
|
|
357
466
|
});
|
|
358
|
-
if (
|
|
467
|
+
if (selectedThreadId === thread.threadId) onSelectedThreadChange?.(null);
|
|
468
|
+
if (replyingThreadId === thread.threadId) {
|
|
469
|
+
setReplyingThreadId(null);
|
|
470
|
+
setReplyText("");
|
|
471
|
+
setReplyMentions([]);
|
|
472
|
+
}
|
|
359
473
|
};
|
|
360
474
|
|
|
361
475
|
const handleReopen = (thread: CommentThread) => {
|
|
@@ -367,12 +481,16 @@ export function CommentsSidebar({
|
|
|
367
481
|
};
|
|
368
482
|
|
|
369
483
|
return (
|
|
370
|
-
<div
|
|
484
|
+
<div
|
|
485
|
+
ref={sidebarRef}
|
|
486
|
+
className="relative w-80 shrink-0 pb-16"
|
|
487
|
+
data-comments-sidebar
|
|
488
|
+
>
|
|
371
489
|
{/* Pending new comment — positioned at the selection Y offset */}
|
|
372
490
|
{pendingComment && (
|
|
373
491
|
<div
|
|
374
492
|
className="absolute left-2 right-4 rounded-lg bg-popover p-3 shadow-md ring-1 ring-border/50 z-10"
|
|
375
|
-
style={{ top: pendingComment.offsetTop }}
|
|
493
|
+
style={{ top: pendingOffset ?? pendingComment.offsetTop }}
|
|
376
494
|
>
|
|
377
495
|
<CommentComposer
|
|
378
496
|
ref={pendingInputRef}
|
|
@@ -383,11 +501,6 @@ export function CommentsSidebar({
|
|
|
383
501
|
onEscape={() => {
|
|
384
502
|
if (!pendingText.trim()) handlePendingCancel();
|
|
385
503
|
}}
|
|
386
|
-
onBlur={() => {
|
|
387
|
-
setTimeout(() => {
|
|
388
|
-
if (!pendingText.trim()) handlePendingCancel();
|
|
389
|
-
}, 200);
|
|
390
|
-
}}
|
|
391
504
|
members={members}
|
|
392
505
|
placeholder={t("comments.add")}
|
|
393
506
|
autoFocus
|
|
@@ -417,25 +530,28 @@ export function CommentsSidebar({
|
|
|
417
530
|
thread={thread}
|
|
418
531
|
marginTop={marginTop}
|
|
419
532
|
isActive={activeThreadId === thread.threadId}
|
|
420
|
-
isExpanded={
|
|
421
|
-
replyText={
|
|
533
|
+
isExpanded={replyingThreadId === thread.threadId}
|
|
534
|
+
replyText={replyingThreadId === thread.threadId ? replyText : ""}
|
|
422
535
|
onHoverChange={(hovered) =>
|
|
423
|
-
|
|
536
|
+
onHoveredThreadChange?.(hovered ? thread.threadId : null)
|
|
424
537
|
}
|
|
425
538
|
onExpand={() => {
|
|
426
|
-
|
|
427
|
-
|
|
539
|
+
onSelectedThreadChange?.(thread.threadId);
|
|
540
|
+
setReplyingThreadId((current) =>
|
|
541
|
+
current === thread.threadId ? null : thread.threadId,
|
|
428
542
|
);
|
|
429
543
|
setReplyText("");
|
|
430
544
|
setReplyMentions([]);
|
|
431
545
|
}}
|
|
432
546
|
onCollapse={() => {
|
|
433
|
-
|
|
547
|
+
setReplyingThreadId(null);
|
|
548
|
+
onSelectedThreadChange?.(null);
|
|
434
549
|
setReplyText("");
|
|
435
550
|
setReplyMentions([]);
|
|
436
551
|
}}
|
|
437
552
|
onReplyChange={setReplyText}
|
|
438
553
|
onReplyMentionAdd={(m) => setReplyMentions((prev) => [...prev, m])}
|
|
554
|
+
onHeightChange={handleThreadCardHeightChange}
|
|
439
555
|
members={members}
|
|
440
556
|
onSubmitReply={() => handleReply(thread.threadId)}
|
|
441
557
|
onResolve={() => handleResolve(thread)}
|
|
@@ -487,6 +603,7 @@ function ThreadView({
|
|
|
487
603
|
onCollapse,
|
|
488
604
|
onReplyChange,
|
|
489
605
|
onReplyMentionAdd,
|
|
606
|
+
onHeightChange,
|
|
490
607
|
onSubmitReply,
|
|
491
608
|
onResolve,
|
|
492
609
|
onSendToAI,
|
|
@@ -503,12 +620,14 @@ function ThreadView({
|
|
|
503
620
|
onCollapse: () => void;
|
|
504
621
|
onReplyChange: (text: string) => void;
|
|
505
622
|
onReplyMentionAdd: (entry: MentionEntry) => void;
|
|
623
|
+
onHeightChange: (threadId: string, height: number) => void;
|
|
506
624
|
onSubmitReply: () => void;
|
|
507
625
|
onResolve: () => void;
|
|
508
626
|
onSendToAI: () => void;
|
|
509
627
|
t: ReturnType<typeof useT>;
|
|
510
628
|
}) {
|
|
511
629
|
const replyInputRef = useRef<HTMLTextAreaElement>(null);
|
|
630
|
+
const cardRef = useRef<HTMLDivElement>(null);
|
|
512
631
|
|
|
513
632
|
useEffect(() => {
|
|
514
633
|
if (isExpanded) {
|
|
@@ -516,8 +635,22 @@ function ThreadView({
|
|
|
516
635
|
}
|
|
517
636
|
}, [isExpanded]);
|
|
518
637
|
|
|
638
|
+
useEffect(() => {
|
|
639
|
+
const element = cardRef.current;
|
|
640
|
+
if (!element) return;
|
|
641
|
+
const updateHeight = () => {
|
|
642
|
+
onHeightChange(thread.threadId, element.getBoundingClientRect().height);
|
|
643
|
+
};
|
|
644
|
+
updateHeight();
|
|
645
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
646
|
+
const observer = new ResizeObserver(updateHeight);
|
|
647
|
+
observer.observe(element);
|
|
648
|
+
return () => observer.disconnect();
|
|
649
|
+
}, [onHeightChange, thread.threadId]);
|
|
650
|
+
|
|
519
651
|
return (
|
|
520
652
|
<div
|
|
653
|
+
ref={cardRef}
|
|
521
654
|
data-thread-card={thread.threadId}
|
|
522
655
|
className={`group/thread mx-2 mr-4 rounded-lg bg-popover shadow-md cursor-pointer transition-shadow ${
|
|
523
656
|
isActive
|
|
@@ -586,7 +719,7 @@ function ThreadView({
|
|
|
586
719
|
))}
|
|
587
720
|
</div>
|
|
588
721
|
|
|
589
|
-
{/* Expanded: Notion-style reply input
|
|
722
|
+
{/* Expanded: Notion-style reply input */}
|
|
590
723
|
{isExpanded && (
|
|
591
724
|
<div
|
|
592
725
|
className="flex items-center gap-2 px-3 pb-3 pt-1"
|
|
@@ -610,11 +743,6 @@ function ThreadView({
|
|
|
610
743
|
onMentionAdd={onReplyMentionAdd}
|
|
611
744
|
onSubmit={onSubmitReply}
|
|
612
745
|
onEscape={onCollapse}
|
|
613
|
-
onBlur={() => {
|
|
614
|
-
setTimeout(() => {
|
|
615
|
-
if (!replyText.trim()) onCollapse();
|
|
616
|
-
}, 200);
|
|
617
|
-
}}
|
|
618
746
|
members={members}
|
|
619
747
|
placeholder={t("comments.reply")}
|
|
620
748
|
rows={1}
|