diff --git a/packages/agent-use/CHANGELOG.md b/packages/agent-use/CHANGELOG.md index 9612fd9b..e9d5cfeb 100644 --- a/packages/agent-use/CHANGELOG.md +++ b/packages/agent-use/CHANGELOG.md @@ -1,5 +1,11 @@ # @eigenpal/docx-editor-agents +## 0.0.33 + +### Patch Changes + +- Add i18n + ## 0.0.32 ### Patch Changes diff --git a/packages/agent-use/package.json b/packages/agent-use/package.json index 148523f6..ee94f851 100644 --- a/packages/agent-use/package.json +++ b/packages/agent-use/package.json @@ -1,6 +1,6 @@ { "name": "@eigenpal/docx-editor-agents", - "version": "0.0.32", + "version": "0.0.33", "description": "Agent-friendly API for DOCX document review — read, comment, propose changes, accept/reject tracked changes", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 079270dc..e76bb48f 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,11 @@ # @eigenpal/docx-js-editor +## 0.0.33 + +### Patch Changes + +- Add i18n + ## 0.0.32 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index 173cbca4..7a55e89f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@eigenpal/docx-js-editor", - "version": "0.0.32", + "version": "0.0.33", "description": "A browser-based DOCX template editor with variable insertion support", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/packages/react/src/components/DocxEditor.tsx b/packages/react/src/components/DocxEditor.tsx index da11094c..8ae4dd4e 100644 --- a/packages/react/src/components/DocxEditor.tsx +++ b/packages/react/src/components/DocxEditor.tsx @@ -3898,6 +3898,51 @@ body { background: white; } if (view) { const selectionState = extractSelectionState(view.state); handleSelectionChange(selectionState); + + // Detect comment/tracked-change marks at cursor to open sidebar card. + // Collect marks from all sources — inclusive:false marks aren't + // reported by $from.marks() at boundaries, and empty arrays are + // truthy so an OR chain would short-circuit. + const $from = view.state.selection.$from; + const marks = [ + ...(view.state.storedMarks ?? []), + ...($from.nodeAfter?.marks ?? []), + ...($from.nodeBefore?.marks ?? []), + ...$from.marks(), + ]; + let cursorSidebarItem: string | null = null; + for (const mark of marks) { + if (mark.type.name === 'comment' && mark.attrs.commentId != null) { + cursorSidebarItem = `comment-${mark.attrs.commentId}`; + break; + } + if ( + (mark.type.name === 'insertion' || mark.type.name === 'deletion') && + mark.attrs.revisionId != null + ) { + const revId = String(mark.attrs.revisionId); + const prefix = `tc-${revId}-`; + let match = commentSidebarItems.find((i) => + i.id.startsWith(prefix) + ); + // Insertion side of a replacement has a different revisionId; + // check alias map to find the correct sidebar card. + if (!match && revisionIdAliases) { + const aliasedId = revisionIdAliases.get(revId); + if (aliasedId) { + match = commentSidebarItems.find((i) => i.id === aliasedId); + } + } + if (match) { + cursorSidebarItem = match.id; + break; + } + } + } + if (cursorSidebarItem) { + setShowCommentsSidebar(true); + } + setExpandedSidebarItem(cursorSidebarItem); } else { handleSelectionChange(null); } @@ -3929,7 +3974,7 @@ body { background: white; } zoom={state.zoom} editorContainerRef={scrollContainerRef} onExpandedItemChange={setExpandedSidebarItem} - revisionIdAliases={revisionIdAliases} + activeItemId={expandedSidebarItem} /> )} ; onExpandedItemChange?: (itemId: string | null) => void; - /** Maps alternate revision IDs to sidebar item IDs (e.g. insertion side of replacements). */ - revisionIdAliases?: Map; + /** Controlled: sidebar item to expand based on cursor position. */ + activeItemId?: string | null; } export function UnifiedSidebar({ @@ -33,15 +33,14 @@ export function UnifiedSidebar({ zoom, editorContainerRef, onExpandedItemChange, - revisionIdAliases, + activeItemId, }: UnifiedSidebarProps) { const { t } = useTranslation(); - const [expandedItem, setExpandedItem] = useState(null); - - // Notify parent when expanded item changes (via effect, not in setState updater) - useEffect(() => { - onExpandedItemChange?.(expandedItem); - }, [expandedItem, onExpandedItemChange]); + // Fully controlled: parent owns expansion state via activeItemId + const expandedItem = activeItemId ?? null; + // Ref keeps toggleExpand stable so card children don't re-render on every cursor move + const expandedItemRef = useRef(expandedItem); + expandedItemRef.current = expandedItem; const [initialPositionsDone, setInitialPositionsDone] = useState(false); const cardHeightsRef = useRef>(new Map()); @@ -147,52 +146,6 @@ export function UnifiedSidebar({ }; }, [expandedItem]); - useEffect(() => { - const container = editorContainerRef?.current; - if (!container) return; - - const pagesEl = container.querySelector('.paged-editor__pages'); - if (!pagesEl) return; - - const handleDocClick = (e: MouseEvent) => { - const target = e.target as HTMLElement; - if (sidebarRef.current?.contains(target)) return; - - if (pagesEl.contains(target)) { - const commentEl = target.closest('[data-comment-id]') as HTMLElement | null; - if (commentEl?.dataset.commentId) { - setExpandedItem(`comment-${commentEl.dataset.commentId}`); - return; - } - const changeEl = - (target.closest('.docx-insertion') as HTMLElement | null) || - (target.closest('.docx-deletion') as HTMLElement | null); - if (changeEl?.dataset.revisionId) { - const revId = changeEl.dataset.revisionId; - const prefix = `tc-${revId}-`; - let match = items.find((i) => i.id.startsWith(prefix)); - // For replacement tracked changes, the insertion part has a different - // revisionId than the card. Look up the alias to find the correct card. - if (!match && revisionIdAliases) { - const aliasedItemId = revisionIdAliases.get(revId); - if (aliasedItemId) { - match = items.find((i) => i.id === aliasedItemId); - } - } - if (match) { - setExpandedItem(match.id); - return; - } - } - } - - setExpandedItem(null); - }; - - container.addEventListener('click', handleDocClick); - return () => container.removeEventListener('click', handleDocClick); - }, [editorContainerRef, items, revisionIdAliases]); - const getMeasureRef = useCallback((itemId: string): ((el: HTMLDivElement | null) => void) => { let fn = measureRefsRef.current.get(itemId); if (!fn) { @@ -210,9 +163,12 @@ export function UnifiedSidebar({ return fn; }, []); - const toggleExpand = useCallback((itemId: string) => { - setExpandedItem((prev) => (prev === itemId ? null : itemId)); - }, []); + const toggleExpand = useCallback( + (itemId: string) => { + onExpandedItemChange?.(expandedItemRef.current === itemId ? null : itemId); + }, + [onExpandedItemChange] + ); if (items.length === 0) return null;