Skip to content

feat: add threaded comments support (Excel 2019+)#28

Merged
Nebu1eto merged 4 commits into
mainfrom
dev/phase-d-threaded-comments
Feb 10, 2026
Merged

feat: add threaded comments support (Excel 2019+)#28
Nebu1eto merged 4 commits into
mainfrom
dev/phase-d-threaded-comments

Conversation

@Nebu1eto

Copy link
Copy Markdown
Owner

Summary

  • Add threaded comments (Excel 2019+) with conversation-style threads, replies, author tracking via shared person list, and resolved/done state
  • Implement full OOXML part handling: xl/threadedComments/threadedComment{N}.xml and xl/persons/person.xml with proper content types and relationships
  • Expose 7 API methods across Rust, Node.js bindings, and TypeScript wrapper: addThreadedComment, getThreadedComments, getThreadedCommentsByCell, deleteThreadedComment, resolveThreadedComment, addPerson, getPersons

Changes

XML Layer (sheetkit-xml)

  • New threaded_comment.rs: ThreadedComments, ThreadedComment, PersonList, Person serde types with OOXML namespace constants

Core Layer (sheetkit-core)

  • New threaded_comment.rs: business logic for add/get/delete/resolve comments, person management with auto-creation, GUID generation
  • Updated workbook/io.rs: read threaded comments from worksheet rels and person list from workbook rels; write both parts with content type overrides
  • Updated workbook/mod.rs: added sheet_threaded_comments and person_list fields to Workbook
  • Updated workbook/sheet_ops.rs: maintain threaded comments vector in sync across new/delete/copy/stream operations
  • Updated workbook/features.rs: 7 public API methods + 10 integration tests

Node.js Layer (packages/sheetkit)

  • 4 napi object types in types.rs, 7 napi methods in lib.rs
  • TypeScript wrapper methods in index.ts
  • 10 vitest tests covering add/reply/delete/resolve/persons/round-trip

Documentation

  • EN and KO threaded comments sections in docs/{en,ko}/guide/advanced.md

Test plan

  • 8 XML serialization/deserialization tests
  • 16 core unit tests (add, reply, get, get-by-cell, delete, resolve, person management)
  • 10 integration tests (file round-trip, buffer round-trip, multi-sheet, error handling)
  • 10 Node.js vitest tests (add/reply, by-cell, delete, resolve, persons, save/open round-trip)
  • All quality gates pass: cargo build/test/clippy/fmt, pnpm build/test, biome check

Generated with Claude Code

@claude

claude Bot commented Feb 10, 2026

Copy link
Copy Markdown

Code Review

I found 2 issues during the review:


Issue 1: Korean comments in code blocks (CLAUDE.md violation)

File: docs/ko/guide/advanced.md (lines 934-1010)

The code blocks contain Korean comments, which violates the project guidelines.

According to CLAUDE.md:

Documentation prose may be in other languages (e.g., Korean guide), but all code blocks within documentation must use English.

While the Korean prose in this documentation file is fine, the comments within the code examples must be in English. Please translate all inline comments in these code blocks to English.

For example:

  • // 스레드 댓글 추가 (작성자는 인물 목록에 자동 추가됩니다)// Add threaded comment (author is automatically added to person list)
  • // 기존 댓글에 답글 달기// Reply to existing comment
  • // 댓글 스레드를 해결됨으로 표시// Mark comment thread as resolved

Issue 2: Missing person list relationship in workbook_rels (bug)

File: crates/sheetkit-core/src/workbook/io.rs (lines 709-720)

The person list is written to the file but the workbook relationship is missing, which will cause Excel to fail to discover the person list.

The save code writes xl/persons/person.xml (line 849) and registers its content type (lines 709-719), but it never adds a REL_TYPE_PERSON relationship to self.workbook_rels. Without this relationship in xl/_rels/workbook.xml.rels, Excel will not discover the person list when opening the file, causing threaded comment authors to be unresolved.

The read path expects this relationship (see lines 361-391) and only uses a hardcoded fallback path. The round-trip tests pass because of this fallback, but actual Excel will not use it.

Suggested fix: Add the person relationship to workbook_rels in the has_any_threaded block after the content type override is added:

// Add person relationship to workbook_rels
if \!self.workbook_rels.relationships.iter().any(|r| {
    r.rel_type == sheetkit_xml::threaded_comment::REL_TYPE_PERSON
}) {
    let rid = crate::sheet::next_rid(&self.workbook_rels.relationships);
    self.workbook_rels.relationships.push(Relationship {
        id: rid,
        rel_type: sheetkit_xml::threaded_comment::REL_TYPE_PERSON.to_string(),
        target: "persons/person.xml".to_string(),
        target_mode: None,
    });
}

@Nebu1eto Nebu1eto left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review finding (blocking): threaded comment/person IDs are generated from a process-local counter. After process restart, IDs restart from the same sequence, which can collide with IDs already present in an existing workbook and produce duplicate IDs.

Relevant path: crates/sheetkit-core/src/threaded_comment.rs (generate_guid).

Please switch to collision-resistant UUID generation and ensure uniqueness against existing IDs in the target workbook.

@Nebu1eto Nebu1eto left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detailed review notes (blocking):

Threaded comment API is comprehensive, but ID generation strategy is unsafe.

Blocking concern:

  • GUID-like IDs are generated from a process-local monotonic counter.
  • After process restart, IDs start again from the same sequence and can collide with IDs already present in a workbook.

Relevant area:

  • crates/sheetkit-core/src/threaded_comment.rs (generate_guid).

Expected fix:

  • Use collision-resistant UUID generation.
  • Before insert, enforce uniqueness against existing comment/person IDs in the workbook.

Also consider:

  • Returning a not-found error for delete/resolve operations when ID does not exist (currently silent success path through workbook API).
  • Validating cell reference format on add to fail early.

Base automatically changed from dev/phase-d-fidelity to main February 10, 2026 07:51
Nebu1eto and others added 4 commits February 10, 2026 18:12
Implement threaded comments with conversation-style threads, replies,
author tracking via shared person list, and resolved/done state.

XML layer: ThreadedComments, Person serde types with OOXML namespaces.
Core layer: add/get/delete/resolve threaded comments, person management.
I/O layer: read/write threadedComment and person.xml parts with content
type overrides and worksheet/workbook relationships.
Node.js bindings: 7 napi methods + TypeScript wrapper methods.
Tests: 24 Rust tests + 10 Node.js tests including file round-trip.
Docs: EN and KO guide sections with Rust and TypeScript examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d artifacts

Allow Korean comments in code blocks within Korean documentation while
requiring English for variable names, string literals, and data values.
Add biome and gitignore exclusions for tsdown-generated *2.js CJS chunks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace process-local counter-based ID generation with uuid crate v4
random UUIDs in {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} format matching
Excel conventions. New IDs are checked against existing person and
comment IDs before insertion to guarantee uniqueness. Also adds
ThreadedCommentNotFound error variant, cell reference validation on add,
and comprehensive tests for ID format, uniqueness, and error cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Nebu1eto
Nebu1eto force-pushed the dev/phase-d-threaded-comments branch from b30a377 to 35e91a1 Compare February 10, 2026 09:14
@Nebu1eto
Nebu1eto merged commit 78ef20f into main Feb 10, 2026
2 of 3 checks passed
@Nebu1eto
Nebu1eto deleted the dev/phase-d-threaded-comments branch February 10, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant