@beyondwork/docx-react-component 1.0.0 → 1.0.2
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 +44 -104
- package/package.json +66 -15
- package/src/api/public-types.ts +1 -1
- package/src/compare/diff-engine.ts +530 -0
- package/src/compare/export-redlines.ts +162 -0
- package/src/compare/snapshot.ts +37 -0
- package/src/core/commands/index.ts +1 -1
- package/src/core/state/editor-state.ts +2 -2
- package/src/index.ts +45 -0
- package/src/legal/bookmarks.ts +196 -0
- package/src/legal/cross-references.ts +356 -0
- package/src/legal/defined-terms.ts +203 -0
- package/src/runtime/document-runtime.ts +3 -5
- package/src/runtime/table-commands.ts +4 -1
- package/src/runtime/table-schema.ts +17 -2
- package/src/runtime/virtualized-rendering.ts +258 -0
- package/src/ui/WordReviewEditor.tsx +256 -35
- package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +2 -2
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +16 -2
- package/.codex/config.toml +0 -5
- package/.corepack/v1/pnpm/10.30.3/.corepack +0 -1
- package/.corepack/v1/pnpm/10.30.3/LICENSE +0 -22
- package/.corepack/v1/pnpm/10.30.3/README.md +0 -240
- package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp +0 -6
- package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp.cmd +0 -5
- package/.corepack/v1/pnpm/10.30.3/dist/pnpm.cjs +0 -195400
- package/.corepack/v1/pnpm/10.30.3/dist/pnpmrc +0 -2
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-arm64-2HJ4WGO6.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-x64-3G3H6IW4.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-arm64-msvc-Q6BARPPB.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-x64-msvc-J2TZHRQI.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.bash +0 -31
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.fish +0 -22
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.ps1 +0 -193
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.zsh +0 -27
- package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x64.exe +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x86.exe +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/worker.js +0 -10119
- package/.corepack/v1/pnpm/10.30.3/package.json +0 -192
- package/.cursor/mcp.json +0 -7
- package/.github/workflows/ci.yml +0 -35
- package/.mcp.json +0 -7
- package/.openclaw/workspace-state.json +0 -4
- package/.pnpmrc.json +0 -1
- package/.wave-launch.sh +0 -7
- package/.workspace-marker +0 -1
- package/AGENTS.md +0 -78
- package/CHANGELOG.md +0 -177
- package/DESIGN.md +0 -929
- package/HEARTBEAT.md +0 -7
- package/IDENTITY.md +0 -23
- package/SOUL.md +0 -36
- package/TOOLS.md +0 -40
- package/USER.md +0 -17
- package/docs/README.md +0 -107
- package/docs/agents/wave-cont-eval-role.md +0 -36
- package/docs/agents/wave-cont-qa-role.md +0 -52
- package/docs/agents/wave-deploy-verifier-role.md +0 -34
- package/docs/agents/wave-design-role.md +0 -47
- package/docs/agents/wave-documentation-role.md +0 -34
- package/docs/agents/wave-infra-role.md +0 -34
- package/docs/agents/wave-integration-role.md +0 -37
- package/docs/agents/wave-launcher-role.md +0 -41
- package/docs/agents/wave-orchestrator-role.md +0 -52
- package/docs/agents/wave-planner-role.md +0 -39
- package/docs/agents/wave-security-role.md +0 -40
- package/docs/architecture/docx/README.md +0 -10
- package/docs/architecture/future/README.md +0 -8
- package/docs/architecture/ooxml-upgrade-analysis.md +0 -134
- package/docs/architecture/platform/shared-openxml-editor-platform.md +0 -153
- package/docs/architecture/xlsx/canonical-workbook-model-and-commands.md +0 -187
- package/docs/architecture/xlsx/spreadsheet-editor-frontend-architecture.md +0 -150
- package/docs/comment-redline-overview.md +0 -350
- package/docs/concepts/context7-vs-skills.md +0 -118
- package/docs/concepts/operating-modes.md +0 -91
- package/docs/concepts/runtime-agnostic-orchestration.md +0 -111
- package/docs/concepts/what-is-a-wave.md +0 -217
- package/docs/context7/bundles.json +0 -222
- package/docs/context7/planner-agent/README.md +0 -28
- package/docs/context7/planner-agent/manifest.json +0 -83
- package/docs/context7/planner-agent/papers/cooperbench-why-coding-agents-cannot-be-your-teammates-yet.md +0 -3283
- package/docs/context7/planner-agent/papers/dova-deliberation-first-multi-agent-orchestration-for-autonomous-research-automation.md +0 -1699
- package/docs/context7/planner-agent/papers/dpbench-large-language-models-struggle-with-simultaneous-coordination.md +0 -2251
- package/docs/context7/planner-agent/papers/incremental-planning-to-control-a-blackboard-based-problem-solver.md +0 -1729
- package/docs/context7/planner-agent/papers/silo-bench-a-scalable-environment-for-evaluating-distributed-coordination-in-multi-agent-llm-systems.md +0 -3747
- package/docs/context7/planner-agent/papers/todoevolve-learning-to-architect-agent-planning-systems.md +0 -1675
- package/docs/context7/planner-agent/papers/verified-multi-agent-orchestration-a-plan-execute-verify-replan-framework-for-complex-query-resolution.md +0 -1173
- package/docs/context7/planner-agent/papers/why-do-multi-agent-llm-systems-fail.md +0 -5211
- package/docs/context7/planner-agent/topics/planning-and-orchestration.md +0 -24
- package/docs/evals/arm-templates/README.md +0 -13
- package/docs/evals/arm-templates/full-wave.json +0 -15
- package/docs/evals/arm-templates/single-agent.json +0 -15
- package/docs/evals/benchmark-catalog.json +0 -670
- package/docs/evals/cases/README.md +0 -47
- package/docs/evals/cases/wave-blackboard-inbox-targeting.json +0 -73
- package/docs/evals/cases/wave-contradiction-conflict.json +0 -104
- package/docs/evals/cases/wave-expert-routing-preservation.json +0 -69
- package/docs/evals/cases/wave-hidden-profile-private-evidence.json +0 -81
- package/docs/evals/cases/wave-premature-closure-guard.json +0 -71
- package/docs/evals/cases/wave-silo-cross-agent-state.json +0 -77
- package/docs/evals/cases/wave-simultaneous-lockstep.json +0 -92
- package/docs/evals/external-benchmarks.json +0 -85
- package/docs/evals/external-command-config.sample.json +0 -9
- package/docs/evals/external-command-config.swe-bench-pro.json +0 -8
- package/docs/evals/pilots/README.md +0 -47
- package/docs/evals/pilots/swe-bench-pro-public-full-wave-review-10.json +0 -64
- package/docs/evals/pilots/swe-bench-pro-public-pilot.json +0 -111
- package/docs/evals/wave-benchmark-program.md +0 -302
- package/docs/guides/planner.md +0 -220
- package/docs/guides/recommendations-0.8.9.md +0 -133
- package/docs/guides/signal-wrappers.md +0 -165
- package/docs/guides/terminal-surfaces.md +0 -96
- package/docs/image copy.png +0 -0
- package/docs/image.png +0 -0
- package/docs/images/image.png +0 -0
- package/docs/legal-feedback-architecture.md +0 -498
- package/docs/plans/component-cutover-matrix.json +0 -1072
- package/docs/plans/component-cutover-matrix.md +0 -307
- package/docs/plans/context7-wave-orchestrator.md +0 -155
- package/docs/plans/current-state.md +0 -198
- package/docs/plans/docx/README.md +0 -9
- package/docs/plans/examples/wave-benchmark-improvement.md +0 -108
- package/docs/plans/examples/wave-example-live-proof.md +0 -435
- package/docs/plans/master-plan.md +0 -224
- package/docs/plans/migration.md +0 -538
- package/docs/plans/operations/README.md +0 -7
- package/docs/plans/operations/wave-10-word-certification.md +0 -87
- package/docs/plans/operations/wave-8-railway-staging.md +0 -153
- package/docs/plans/operations/wave-9-manual-certification.md +0 -73
- package/docs/plans/platform/README.md +0 -9
- package/docs/plans/reference/legal-checklist-coverage.md +0 -258
- package/docs/plans/wave-orchestrator.md +0 -423
- package/docs/plans/waves/README.md +0 -75
- package/docs/plans/waves/completed/wave-0.md +0 -195
- package/docs/plans/waves/completed/wave-1.md +0 -379
- package/docs/plans/waves/completed/wave-10.md +0 -670
- package/docs/plans/waves/completed/wave-11.md +0 -335
- package/docs/plans/waves/completed/wave-12.md +0 -417
- package/docs/plans/waves/completed/wave-13.md +0 -316
- package/docs/plans/waves/completed/wave-14.md +0 -319
- package/docs/plans/waves/completed/wave-15.md +0 -321
- package/docs/plans/waves/completed/wave-16.md +0 -316
- package/docs/plans/waves/completed/wave-17.md +0 -331
- package/docs/plans/waves/completed/wave-18.md +0 -328
- package/docs/plans/waves/completed/wave-2.md +0 -438
- package/docs/plans/waves/completed/wave-3.md +0 -435
- package/docs/plans/waves/completed/wave-4.md +0 -430
- package/docs/plans/waves/completed/wave-5.md +0 -430
- package/docs/plans/waves/completed/wave-6.md +0 -430
- package/docs/plans/waves/completed/wave-7.md +0 -526
- package/docs/plans/waves/completed/wave-8.md +0 -596
- package/docs/plans/waves/completed/wave-9.md +0 -552
- package/docs/plans/waves/deferred/README.md +0 -14
- package/docs/plans/waves/deferred/encrypted-intake-contracts.md +0 -282
- package/docs/plans/waves/deferred/legal-feedback-wave-expansion.md +0 -308
- package/docs/plans/waves/deferred/wave-encrypted-intake.md +0 -451
- package/docs/plans/waves/design/README.md +0 -5
- package/docs/plans/waves/design/wave-1-a1.md +0 -309
- package/docs/plans/waves/reviews/README.md +0 -5
- package/docs/plans/waves/reviews/wave-0-cont-qa.md +0 -151
- package/docs/plans/waves/reviews/wave-1-cont-qa.md +0 -46
- package/docs/plans/waves/reviews/wave-10-accessibility-and-design.md +0 -51
- package/docs/plans/waves/reviews/wave-10-cont-qa.md +0 -24
- package/docs/plans/waves/reviews/wave-10-dashboard-proof.md +0 -46
- package/docs/plans/waves/reviews/wave-10-performance-signoff.md +0 -55
- package/docs/plans/waves/reviews/wave-10-regression-proof.md +0 -23
- package/docs/plans/waves/reviews/wave-10-release-audit.md +0 -31
- package/docs/plans/waves/reviews/wave-10-service-proof.md +0 -83
- package/docs/plans/waves/reviews/wave-10-word-certification.md +0 -31
- package/docs/plans/waves/reviews/wave-18-ai-contract-closure.md +0 -277
- package/docs/plans/waves/reviews/wave-18-cont-qa.md +0 -255
- package/docs/plans/waves/reviews/wave-18-parity-proof.md +0 -271
- package/docs/plans/waves/reviews/wave-19-cont-qa.md +0 -59
- package/docs/plans/waves/reviews/wave-2-cont-qa.md +0 -72
- package/docs/plans/waves/reviews/wave-20-cont-qa.md +0 -60
- package/docs/plans/waves/reviews/wave-25-cont-qa.md +0 -48
- package/docs/plans/waves/reviews/wave-28-cont-qa.md +0 -46
- package/docs/plans/waves/reviews/wave-29-cont-qa.md +0 -53
- package/docs/plans/waves/reviews/wave-3-cont-qa.md +0 -53
- package/docs/plans/waves/reviews/wave-3-core-proof.md +0 -77
- package/docs/plans/waves/reviews/wave-3-validator-proof.md +0 -73
- package/docs/plans/waves/reviews/wave-32-cont-qa.md +0 -43
- package/docs/plans/waves/reviews/wave-33-cont-qa.md +0 -526
- package/docs/plans/waves/reviews/wave-34-cont-qa.md +0 -100
- package/docs/plans/waves/reviews/wave-35-cont-qa.md +0 -145
- package/docs/plans/waves/reviews/wave-4-cont-qa.md +0 -47
- package/docs/plans/waves/reviews/wave-4-structure-proof.md +0 -69
- package/docs/plans/waves/reviews/wave-5-comment-proof.md +0 -158
- package/docs/plans/waves/reviews/wave-5-cont-qa.md +0 -68
- package/docs/plans/waves/reviews/wave-6-cont-qa.md +0 -416
- package/docs/plans/waves/reviews/wave-6-redline-proof.md +0 -130
- package/docs/plans/waves/reviews/wave-7-cont-qa.md +0 -82
- package/docs/plans/waves/reviews/wave-7-ooxml-compliance.md +0 -85
- package/docs/plans/waves/reviews/wave-7-preservation-proof.md +0 -119
- package/docs/plans/waves/reviews/wave-7-trust-ux.md +0 -87
- package/docs/plans/waves/reviews/wave-8-accessibility-and-design.md +0 -128
- package/docs/plans/waves/reviews/wave-8-cont-qa.md +0 -92
- package/docs/plans/waves/reviews/wave-8-live-proof.md +0 -140
- package/docs/plans/waves/reviews/wave-8-security.md +0 -47
- package/docs/plans/waves/reviews/wave-9-editor-embedding.md +0 -39
- package/docs/plans/waves/reviews/wave-9-fixture-runner.md +0 -56
- package/docs/plans/waves/reviews/wave-9-live-proof.md +0 -105
- package/docs/plans/waves/reviews/wave-9-usability-and-performance.md +0 -152
- package/docs/plans/waves/specs/README.md +0 -5
- package/docs/plans/waves/specs/wave-1-component-boundaries.md +0 -322
- package/docs/plans/waves/specs/wave-1-ooxml-contracts.md +0 -323
- package/docs/plans/waves/specs/wave-1-review-and-ui-contracts.md +0 -339
- package/docs/plans/waves/specs/wave-1-runtime-contracts.md +0 -509
- package/docs/plans/waves/wave-19.md +0 -341
- package/docs/plans/waves/wave-20.md +0 -308
- package/docs/plans/waves/wave-21.md +0 -289
- package/docs/plans/waves/wave-22.md +0 -221
- package/docs/plans/waves/wave-23.md +0 -295
- package/docs/plans/waves/wave-24.md +0 -286
- package/docs/plans/waves/wave-25.md +0 -313
- package/docs/plans/waves/wave-26.md +0 -300
- package/docs/plans/waves/wave-27.md +0 -299
- package/docs/plans/waves/wave-28.md +0 -368
- package/docs/plans/waves/wave-29.md +0 -303
- package/docs/plans/waves/wave-30.md +0 -307
- package/docs/plans/waves/wave-31.md +0 -231
- package/docs/plans/waves/wave-32.md +0 -152
- package/docs/plans/waves/wave-33.md +0 -147
- package/docs/plans/waves/wave-34.md +0 -148
- package/docs/plans/waves/wave-35.md +0 -141
- package/docs/plans/waves/wave-36.md +0 -146
- package/docs/plans/xlsx/README.md +0 -14
- package/docs/plans/xlsx/xlsx-fixture-corpus-and-certification-plan.md +0 -126
- package/docs/reference/cli-reference.md +0 -600
- package/docs/reference/coordination-and-closure.md +0 -487
- package/docs/reference/deep-research-report (15).md +0 -25
- package/docs/reference/docx/README.md +0 -10
- package/docs/reference/legal-checklist.md +0 -445
- package/docs/reference/live-proof-waves.md +0 -199
- package/docs/reference/ooxml-compliance.md +0 -129
- package/docs/reference/ooxml-feature-parity-matrix.md +0 -172
- package/docs/reference/platform/shared-ooxml-platform-guidance.md +0 -77
- package/docs/reference/prototype-agent-prompt-legal-fidelity.md +0 -155
- package/docs/reference/public-api.md +0 -456
- package/docs/reference/repository-guidance.md +0 -58
- package/docs/reference/runtime-config/README.md +0 -182
- package/docs/reference/runtime-config/claude.md +0 -110
- package/docs/reference/runtime-config/codex.md +0 -82
- package/docs/reference/runtime-config/opencode.md +0 -93
- package/docs/reference/sample-waves.md +0 -105
- package/docs/reference/skills.md +0 -237
- package/docs/reference/templates/AGENTS.md +0 -78
- package/docs/reference/templates/HEARTBEAT.md +0 -7
- package/docs/reference/templates/IDENTITY.md +0 -23
- package/docs/reference/templates/SOUL.md +0 -36
- package/docs/reference/templates/TOOLS.md +0 -40
- package/docs/reference/templates/USER.md +0 -17
- package/docs/reference/wave-control.md +0 -184
- package/docs/reference/wave-planning-lessons.md +0 -167
- package/docs/reference/word-review-editor-frontend-architecture.md +0 -479
- package/docs/reference/word-review-editor-ux-guide.md +0 -253
- package/docs/reference/xlsx/xlsx-ooxml-compliance.md +0 -137
- package/docs/research/agent-context-sources.md +0 -178
- package/docs/research/coordination-failure-review.md +0 -290
- package/docs/research/docx-react-component/Canonical Document Schema Specification for a React-based Word-compatible Editor.md +0 -2317
- package/docs/research/docx-react-component/Feature Compatibility Matrix for a React Word Compatible Legal Editor v1.md +0 -219
- package/docs/research/docx-react-component/React Component Architecture and Front-End Structure Specification for a Word-Compatible Legal Review Editor.md +0 -1112
- package/docs/research/docx-react-component/document_compatibility_and_testing_spec.md +0 -751
- package/docs/research/xlsx/raw/README.md +0 -13
- package/docs/roadmap.md +0 -174
- package/docs/superpowers/plans/2026-03-28-harness-control-bar.md +0 -677
- package/docs/superpowers/specs/2026-03-28-harness-control-bar-design.md +0 -274
- package/docs/xlsx-react/README.md +0 -38
- package/docs/xlsx-react/agent-llm-interaction-layer-docx-xlsx.md +0 -621
- package/docs/xlsx-react/canonical-workbook-model-and-commands.md +0 -948
- package/docs/xlsx-react/shared-openxml-editor-platform-docx-xlsx.md +0 -228
- package/docs/xlsx-react/spreadsheet-editor-component-architecture.md +0 -809
- package/docs/xlsx-react/spreadsheet-editor-frontend-architecture.md +0 -537
- package/docs/xlsx-react/spreadsheet-editor-ux-guide.md +0 -520
- package/docs/xlsx-react/xlsx-editor-research-pack.md +0 -871
- package/docs/xlsx-react/xlsx-fixture-corpus-and-certification-plan.md +0 -436
- package/docs/xlsx-react/xlsx-ooxml-compliance.md +0 -320
- package/examples/README.md +0 -16
- package/memory/MEMORY.md +0 -24
- package/pnpm-workspace.yaml +0 -4
- package/scripts/check-no-authored-js.sh +0 -13
- package/scripts/context7-api-check.sh +0 -65
- package/scripts/context7-export-env.sh +0 -42
- package/scripts/run-context7-mcp.sh +0 -8
- package/scripts/run-workspace-tests.sh +0 -15
- package/scripts/start-wave-10-local.sh +0 -189
- package/scripts/wave-agent-attach.sh +0 -47
- package/scripts/wave-auto-answer.sh +0 -118
- package/scripts/wave-dashboard-attach.sh +0 -13
- package/scripts/wave-launch.sh +0 -273
- package/scripts/wave-overnight-supervisor.sh +0 -145
- package/scripts/wave-status.sh +0 -379
- package/scripts/wave-watch.sh +0 -231
- package/services/README.md +0 -17
- package/services/openxml-validator/Dockerfile +0 -29
- package/services/openxml-validator/OpenXmlValidator.Api.csproj +0 -12
- package/services/openxml-validator/Program.cs +0 -436
- package/services/openxml-validator/README.md +0 -152
- package/services/openxml-validator/railway.json +0 -16
- package/services/react-word-editor/.tmp-a4/src/api/public-types.ts +0 -318
- package/services/react-word-editor/.tmp-a4/src/ui/WordReviewEditor.tsx +0 -1302
- package/services/react-word-editor/.tmp-a4/src/ui/editor-surface/editor-surface.tsx +0 -546
- package/services/react-word-editor/.tmp-a4/test/ui/word-review-editor.test.tsx +0 -146
- package/services/react-word-editor/.tmp-a4-build/src/api/public-types.js +0 -2
- package/services/react-word-editor/.tmp-a4-build/src/ui/WordReviewEditor.js +0 -818
- package/services/react-word-editor/.tmp-a4-build/src/ui/editor-surface/editor-surface.js +0 -229
- package/services/react-word-editor/.tmp-a4-build/test/ui/word-review-editor.test.js +0 -121
- package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.json +0 -21
- package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.tsbuildinfo +0 -1
- package/services/react-word-editor/Dockerfile +0 -26
- package/services/react-word-editor/README.md +0 -254
- package/services/react-word-editor/app/api/certification/route.ts +0 -79
- package/services/react-word-editor/app/api/demo-sessions/route.ts +0 -109
- package/services/react-word-editor/app/api/deploy-health/route.ts +0 -23
- package/services/react-word-editor/app/api/exports/[exportId]/route.ts +0 -34
- package/services/react-word-editor/app/api/exports/route.ts +0 -81
- package/services/react-word-editor/app/api/fixtures/[fixtureId]/run/route.ts +0 -100
- package/services/react-word-editor/app/api/health/route.ts +0 -70
- package/services/react-word-editor/app/api/runs/[runId]/route.ts +0 -36
- package/services/react-word-editor/app/api/scenarios/[scenarioId]/run/route.ts +0 -85
- package/services/react-word-editor/app/api/sessions/[sessionId]/route.ts +0 -199
- package/services/react-word-editor/app/api/sessions/[sessionId]/source/route.ts +0 -45
- package/services/react-word-editor/app/api/uploads/route.ts +0 -70
- package/services/react-word-editor/app/api/validate/route.ts +0 -310
- package/services/react-word-editor/app/certification/[runId]/page.tsx +0 -14
- package/services/react-word-editor/app/certification/page.tsx +0 -32
- package/services/react-word-editor/app/dashboard/page.tsx +0 -7
- package/services/react-word-editor/app/demo/page.tsx +0 -30
- package/services/react-word-editor/app/demo/prototype-client.tsx +0 -1080
- package/services/react-word-editor/app/editor/[sessionId]/page.tsx +0 -33
- package/services/react-word-editor/app/fixtures/page.tsx +0 -7
- package/services/react-word-editor/app/globals.css +0 -121
- package/services/react-word-editor/app/layout.tsx +0 -32
- package/services/react-word-editor/app/page.tsx +0 -30
- package/services/react-word-editor/app/runs/[runId]/page.tsx +0 -34
- package/services/react-word-editor/app/wave-10-word-review/page.tsx +0 -7
- package/services/react-word-editor/components/harness-control-bar.tsx +0 -289
- package/services/react-word-editor/components/harness-editor-session-client.tsx +0 -1214
- package/services/react-word-editor/components/harness-workspace-page.tsx +0 -715
- package/services/react-word-editor/components/reduced-motion-toggle.tsx +0 -79
- package/services/react-word-editor/components/workspace-certification-panel.tsx +0 -307
- package/services/react-word-editor/lib/certification-bundle.ts +0 -796
- package/services/react-word-editor/lib/certification-store.ts +0 -661
- package/services/react-word-editor/lib/demo-fixtures.test.mjs +0 -195
- package/services/react-word-editor/lib/demo-fixtures.ts +0 -1519
- package/services/react-word-editor/lib/editor-session-summary.test.mjs +0 -68
- package/services/react-word-editor/lib/editor-session-summary.ts +0 -14
- package/services/react-word-editor/lib/editor-session.ts +0 -228
- package/services/react-word-editor/lib/exports-route.test.mjs +0 -32
- package/services/react-word-editor/lib/harness-client.ts +0 -347
- package/services/react-word-editor/lib/harness-config.json +0 -30
- package/services/react-word-editor/lib/harness-config.test.mjs +0 -31
- package/services/react-word-editor/lib/harness-config.ts +0 -21
- package/services/react-word-editor/lib/harness-editor-datastore.test.mjs +0 -220
- package/services/react-word-editor/lib/harness-editor-datastore.ts +0 -161
- package/services/react-word-editor/lib/private-mode.test.mjs +0 -42
- package/services/react-word-editor/lib/private-mode.ts +0 -61
- package/services/react-word-editor/lib/regression-report.test.mjs +0 -352
- package/services/react-word-editor/lib/regression-report.ts +0 -896
- package/services/react-word-editor/lib/run-artifacts.ts +0 -934
- package/services/react-word-editor/lib/run-history.ts +0 -755
- package/services/react-word-editor/lib/scenario-artifacts.test.mjs +0 -41
- package/services/react-word-editor/lib/scenario-artifacts.ts +0 -44
- package/services/react-word-editor/lib/storage.ts +0 -953
- package/services/react-word-editor/lib/validator-client.test.mjs +0 -54
- package/services/react-word-editor/lib/validator-client.ts +0 -95
- package/services/react-word-editor/lib/workspace-navigation.ts +0 -79
- package/services/react-word-editor/middleware.ts +0 -35
- package/services/react-word-editor/next-env.d.ts +0 -6
- package/services/react-word-editor/next.config.mjs +0 -15
- package/services/react-word-editor/package.json +0 -38
- package/services/react-word-editor/postcss.config.mjs +0 -8
- package/services/react-word-editor/railway.json +0 -21
- package/services/react-word-editor/scripts/wave-10-certification.mjs +0 -101
- package/services/react-word-editor/scripts/wave-9-live-usability-pilot.mjs +0 -911
- package/services/react-word-editor/tsconfig.json +0 -39
- package/services/react-word-editor/tsconfig.tsbuildinfo +0 -1
- package/skills/README.md +0 -48
- package/skills/domain-docx-compatibility/SKILL.md +0 -44
- package/skills/domain-docx-compatibility/skill.json +0 -19
- package/skills/domain-editor-architecture/SKILL.md +0 -49
- package/skills/domain-editor-architecture/skill.json +0 -19
- package/skills/domain-legal-review/SKILL.md +0 -39
- package/skills/domain-legal-review/skill.json +0 -19
- package/skills/provider-aws/SKILL.md +0 -117
- package/skills/provider-aws/adapters/claude.md +0 -1
- package/skills/provider-aws/adapters/codex.md +0 -1
- package/skills/provider-aws/references/service-verification.md +0 -39
- package/skills/provider-aws/skill.json +0 -54
- package/skills/provider-custom-deploy/SKILL.md +0 -64
- package/skills/provider-custom-deploy/skill.json +0 -50
- package/skills/provider-docker-compose/SKILL.md +0 -96
- package/skills/provider-docker-compose/adapters/local.md +0 -1
- package/skills/provider-docker-compose/skill.json +0 -53
- package/skills/provider-github-release/SKILL.md +0 -121
- package/skills/provider-github-release/adapters/claude.md +0 -1
- package/skills/provider-github-release/adapters/codex.md +0 -1
- package/skills/provider-github-release/skill.json +0 -55
- package/skills/provider-kubernetes/SKILL.md +0 -143
- package/skills/provider-kubernetes/adapters/claude.md +0 -1
- package/skills/provider-kubernetes/adapters/codex.md +0 -1
- package/skills/provider-kubernetes/references/kubectl-patterns.md +0 -58
- package/skills/provider-kubernetes/skill.json +0 -52
- package/skills/provider-railway/SKILL.md +0 -123
- package/skills/provider-railway/adapters/claude.md +0 -1
- package/skills/provider-railway/adapters/codex.md +0 -1
- package/skills/provider-railway/adapters/local.md +0 -1
- package/skills/provider-railway/adapters/opencode.md +0 -1
- package/skills/provider-railway/references/verification-commands.md +0 -39
- package/skills/provider-railway/skill.json +0 -71
- package/skills/provider-ssh-manual/SKILL.md +0 -97
- package/skills/provider-ssh-manual/skill.json +0 -54
- package/skills/repo-coding-rules/SKILL.md +0 -55
- package/skills/repo-coding-rules/skill.json +0 -34
- package/skills/role-cont-eval/SKILL.md +0 -91
- package/skills/role-cont-eval/adapters/codex.md +0 -1
- package/skills/role-cont-eval/skill.json +0 -36
- package/skills/role-cont-qa/SKILL.md +0 -100
- package/skills/role-cont-qa/adapters/claude.md +0 -1
- package/skills/role-cont-qa/skill.json +0 -36
- package/skills/role-deploy/SKILL.md +0 -97
- package/skills/role-deploy/skill.json +0 -36
- package/skills/role-design/SKILL.md +0 -50
- package/skills/role-design/skill.json +0 -36
- package/skills/role-documentation/SKILL.md +0 -76
- package/skills/role-documentation/skill.json +0 -36
- package/skills/role-implementation/SKILL.md +0 -45
- package/skills/role-implementation/skill.json +0 -36
- package/skills/role-infra/SKILL.md +0 -81
- package/skills/role-infra/skill.json +0 -36
- package/skills/role-integration/SKILL.md +0 -91
- package/skills/role-integration/skill.json +0 -36
- package/skills/role-planner/SKILL.md +0 -39
- package/skills/role-planner/skill.json +0 -21
- package/skills/role-research/SKILL.md +0 -65
- package/skills/role-research/skill.json +0 -36
- package/skills/role-security/SKILL.md +0 -60
- package/skills/role-security/skill.json +0 -36
- package/skills/runtime-claude/SKILL.md +0 -66
- package/skills/runtime-claude/skill.json +0 -36
- package/skills/runtime-codex/SKILL.md +0 -58
- package/skills/runtime-codex/skill.json +0 -36
- package/skills/runtime-local/SKILL.md +0 -46
- package/skills/runtime-local/skill.json +0 -36
- package/skills/runtime-opencode/SKILL.md +0 -58
- package/skills/runtime-opencode/skill.json +0 -36
- package/skills/signal-hygiene/SKILL.md +0 -51
- package/skills/signal-hygiene/skill.json +0 -20
- package/skills/tui-design/SKILL.md +0 -77
- package/skills/tui-design/references/tui-design.md +0 -259
- package/skills/tui-design/skill.json +0 -36
- package/skills/wave-core/SKILL.md +0 -141
- package/skills/wave-core/references/marker-syntax.md +0 -70
- package/skills/wave-core/skill.json +0 -35
- package/test/README.md +0 -16
- package/test/core/formatting-commands.test.ts +0 -285
- package/test/core/image-commands.test.ts +0 -298
- package/test/core/mapping.test.ts +0 -186
- package/test/core/text-commands.test.ts +0 -176
- package/test/fixtures/docx/F01-basic-contract.docx +0 -0
- package/test/fixtures/docx/F01-basic-contract.md +0 -33
- package/test/fixtures/docx/F02-headings-styles.docx +0 -0
- package/test/fixtures/docx/F02-headings-styles.md +0 -33
- package/test/fixtures/docx/F03-legal-outline-numbering.docx +0 -0
- package/test/fixtures/docx/F03-legal-outline-numbering.md +0 -34
- package/test/fixtures/docx/F04-restart-numbering-schedules.docx +0 -0
- package/test/fixtures/docx/F04-restart-numbering-schedules.md +0 -33
- package/test/fixtures/docx/F05-table-heavy-agreement.docx +0 -0
- package/test/fixtures/docx/F05-table-heavy-agreement.md +0 -34
- package/test/fixtures/docx/F06-merged-cells-signature-table.docx +0 -0
- package/test/fixtures/docx/F06-merged-cells-signature-table.md +0 -34
- package/test/fixtures/docx/F07-inline-images-exhibit.docx +0 -0
- package/test/fixtures/docx/F07-inline-images-exhibit.md +0 -34
- package/test/fixtures/docx/F08-hyperlinks.docx +0 -0
- package/test/fixtures/docx/F08-hyperlinks.md +0 -33
- package/test/fixtures/docx/F09-comments-single-paragraph.docx +0 -0
- package/test/fixtures/docx/F09-comments-single-paragraph.md +0 -33
- package/test/fixtures/docx/F10-threaded-comments-resolve.docx +0 -0
- package/test/fixtures/docx/F10-threaded-comments-resolve.md +0 -33
- package/test/fixtures/docx/F11-redlines-basic.docx +0 -0
- package/test/fixtures/docx/F11-redlines-basic.md +0 -33
- package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.docx +0 -0
- package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.md +0 -33
- package/test/fixtures/docx/F13-comments-on-deleted-text.docx +0 -0
- package/test/fixtures/docx/F13-comments-on-deleted-text.md +0 -33
- package/test/fixtures/docx/F14-revisions-in-tables-and-lists.docx +0 -0
- package/test/fixtures/docx/F14-revisions-in-tables-and-lists.md +0 -33
- package/test/fixtures/docx/F15-sections-headers-footers.docx +0 -0
- package/test/fixtures/docx/F15-sections-headers-footers.md +0 -33
- package/test/fixtures/docx/F16-footnotes-endnotes.docx +0 -0
- package/test/fixtures/docx/F16-footnotes-endnotes.md +0 -33
- package/test/fixtures/docx/F17-fields-and-toc.docx +0 -0
- package/test/fixtures/docx/F17-fields-and-toc.md +0 -33
- package/test/fixtures/docx/F18-content-controls-template.docx +0 -0
- package/test/fixtures/docx/F18-content-controls-template.md +0 -33
- package/test/fixtures/docx/F19-custom-xml-doc-assembly.docx +0 -0
- package/test/fixtures/docx/F19-custom-xml-doc-assembly.md +0 -35
- package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.docx +0 -0
- package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.md +0 -33
- package/test/fixtures/docx/F21-malformed-broken-docx.docx +0 -0
- package/test/fixtures/docx/F21-malformed-broken-docx.md +0 -33
- package/test/fixtures/docx/README.md +0 -74
- package/test/fixtures/docx/certification-manifest.json +0 -104
- package/test/fixtures/docx/fixtures.manifest.json +0 -196
- package/test/fixtures/encrypted-docx/README.md +0 -27
- package/test/fixtures/encrypted-docx/certification-manifest.json +0 -9
- package/test/fixtures/encrypted-docx/fixtures.manifest.json +0 -47
- package/test/fixtures/scenarios/docx/README.md +0 -25
- package/test/fixtures/scenarios/docx/S01-sow-template.docx +0 -0
- package/test/fixtures/scenarios/docx/S01-sow-template.md +0 -30
- package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.docx +0 -0
- package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.md +0 -32
- package/test/fixtures/scenarios/docx/scenario-manifest.json +0 -53
- package/test/formats/xlsx/io/xlsx-import.test.ts +0 -766
- package/test/formats/xlsx/model/workbook.test.ts +0 -669
- package/test/helpers/dom-setup.ts +0 -124
- package/test/io/comment-roundtrip.test.ts +0 -272
- package/test/io/complex-content-roundtrip.test.ts +0 -632
- package/test/io/docx-compatibility-regression.test.ts +0 -199
- package/test/io/docx-session.test.ts +0 -1495
- package/test/io/footnotes-roundtrip.test.ts +0 -318
- package/test/io/headers-footers-roundtrip.test.ts +0 -547
- package/test/io/numbering-roundtrip.test.ts +0 -234
- package/test/io/package-reader.test.ts +0 -199
- package/test/io/paragraph-properties-roundtrip.test.ts +0 -129
- package/test/io/preserved-package-roundtrip.test.ts +0 -365
- package/test/io/property-completeness.test.ts +0 -292
- package/test/io/revision-roundtrip.test.ts +0 -347
- package/test/io/structural-blocks.test.ts +0 -202
- package/test/io/table-media-roundtrip.test.ts +0 -448
- package/test/io/table-properties-roundtrip.test.ts +0 -569
- package/test/io/table-roundtrip.test.ts +0 -302
- package/test/io/text-roundtrip.test.ts +0 -344
- package/test/model/canonical-document.test.ts +0 -285
- package/test/preservation/opaque-fragment-store.test.ts +0 -121
- package/test/preservation/package-preservation.test.ts +0 -395
- package/test/preservation/store.test.ts +0 -84
- package/test/review/comment-remapping.test.ts +0 -220
- package/test/review/comment-store.test.ts +0 -180
- package/test/review/move-revisions.test.ts +0 -143
- package/test/review/property-change-revisions.test.ts +0 -225
- package/test/review/revision-actions.test.ts +0 -330
- package/test/review/revision-store.test.ts +0 -193
- package/test/runtime/session-capabilities.test.ts +0 -260
- package/test/runtime/table-commands.test.ts +0 -356
- package/test/runtime/table-schema.test.ts +0 -221
- package/test/runtime/tracked-changes-toggle.test.ts +0 -107
- package/test/ui/comment-review-surface.test.tsx +0 -114
- package/test/ui/reduced-motion-toggle.test.tsx +0 -137
- package/test/ui/word-review-editor.imported-scenarios.test.tsx +0 -169
- package/test/ui/word-review-editor.interaction.test.tsx +0 -1198
- package/test/ui/word-review-editor.test.js +0 -188
- package/test/ui/word-review-editor.test.tsx +0 -280
- package/test/ui-tailwind/search-plugin.test.ts +0 -286
- package/test/validation/compatibility-engine.test.ts +0 -336
- package/test/validation/compatibility-report.test.ts +0 -189
- package/test/validation/low-priority-word-surfaces.test.ts +0 -282
- package/test/validation/malformed-doc.test.ts +0 -113
- package/test-results/.last-run.json +0 -4
- package/wave.config.json +0 -406
|
@@ -1,948 +0,0 @@
|
|
|
1
|
-
# Canonical Workbook Model, Selection Semantics, and Command/Transaction Design
|
|
2
|
-
|
|
3
|
-
This research paper is source material.
|
|
4
|
-
|
|
5
|
-
Canonical repo-aligned planned doc:
|
|
6
|
-
|
|
7
|
-
- `docs/architecture/xlsx/canonical-workbook-model-and-commands.md`
|
|
8
|
-
|
|
9
|
-
## Interpretation of the problem
|
|
10
|
-
|
|
11
|
-
You want an XLSX sibling to an existing React-based OOXML DOCX editor, with the same “runtime-owned truth + rigorous preservation” posture, but without pretending spreadsheets are “rich text with cells bolted on.”
|
|
12
|
-
|
|
13
|
-
Concretely, this document defines an implementation-ready canonical workbook model and the mutation pipeline for an embeddable React spreadsheet editor that:
|
|
14
|
-
|
|
15
|
-
- Treats `.xlsx` as an **OOXML/Open Packaging Conventions (OPC)** package with parts + relationships + content types, not as a handful of XML files.
|
|
16
|
-
- Keeps a **runtime-owned canonical state** as the source of truth; React renders a projection and never owns or mutates the canonical workbook directly.
|
|
17
|
-
- Routes all changes through **commands → transactions → commit**, with explicit mapping/remapping so anchors and references remain stable and auditable.
|
|
18
|
-
- Never silently drops unsupported content; instead it classifies features and actions explicitly: **preserve, lock, warn, block, fail**, with export safety gated by real Excel reopen behavior (not just schema validation).
|
|
19
|
-
|
|
20
|
-
The goal here is not to spec *every* Excel feature. The goal is the **core internal model** that makes an Excel-compatible grid editor safe, testable, and extensible—plus the command/transaction structure that makes correctness enforceable.
|
|
21
|
-
|
|
22
|
-
## Research plan
|
|
23
|
-
|
|
24
|
-
Information needs to answer this well:
|
|
25
|
-
|
|
26
|
-
1. **OPC + SpreadsheetML structure requirements** (parts, relationships, content types, minimal workbook requirements, what must exist for a valid workbook).
|
|
27
|
-
2. **Cell value + formula storage semantics** (how values, shared strings, formulas, cached formula results, shared formulas, merges are represented).
|
|
28
|
-
3. **Anchorable objects** (comments/notes, hyperlinks, drawings/images, defined names) and how they bind to cells/ranges and to worksheet/workbook relationships.
|
|
29
|
-
4. **Observed Excel behavior that affects safety** (row/column limits, merge semantics deleting non-top-left contents, reference adjustment rules, R1C1 concepts, deletion producing `#REF!`, structured references).
|
|
30
|
-
5. **Proven architecture patterns from your DOCX editor** (runtime snapshot projection, selection/anchor mapping model, transactions/history boundaries, explicit compatibility taxonomy).
|
|
31
|
-
|
|
32
|
-
Execution approach:
|
|
33
|
-
|
|
34
|
-
- Start from your existing repo’s “non‑negotiables” and runtime/transaction patterns, treating them as the baseline for the XLSX sibling’s philosophy and host contract.
|
|
35
|
-
- Use official/primary references for spreadsheet package structure and markup behavior, especially Microsoft Learn’s Open XML SDK docs (which quote ISO/IEC 29500 sections) and Microsoft’s Open Specifications for Excel extensions that drive modern Excel behavior.
|
|
36
|
-
- Separate **normative format/markup constraints** from **observed Excel UX/behavior constraints**, and call out when something is a design decision rather than a guaranteed Excel rule.
|
|
37
|
-
- Convert findings into repo-ready outputs: module boundaries, canonical types, command taxonomy, mapping rules, invariants, undo/redo boundaries, fixture + test matrices, and release gates.
|
|
38
|
-
|
|
39
|
-
## Findings
|
|
40
|
-
|
|
41
|
-
### Normative and spec-adjacent constraints (format correctness)
|
|
42
|
-
|
|
43
|
-
A `.xlsx` workbook is an OPC ZIP container whose contents are discovered and understood via **part names, content types, and relationships**, including a package-level relationships part (`_rels/.rels`) and a content types part (`[Content_Types].xml`).
|
|
44
|
-
|
|
45
|
-
A minimum SpreadsheetML workbook requires (at least) a workbook part and at least one worksheet part connected through relationships. The Open XML SDK documentation’s “minimum workbook scenario” description (and its generated `workbook.xml` + `workbook.xml.rels` examples) makes this concrete: `workbook.xml` contains `<sheets><sheet ... r:id="..."/></sheets>`, and `workbook.xml.rels` maps that relationship ID to the worksheet part.
|
|
46
|
-
|
|
47
|
-
Cell values are stored in the `v` (Cell Value) element. If the cell stores a string via the shared string table, `v` stores an index into the shared string table; otherwise `v` stores the value directly. Cells with formulas store the formula in `f` and the **last calculated result** in `v`.
|
|
48
|
-
|
|
49
|
-
Shared strings are a workbook-level construct (`<sst/>`) containing de-duplicated string instances; strings can also be stored as inline strings under `<c>` using `<is>` instead of `<v>`. Rich text within strings is represented as multiple runs in the shared string table (`<si>` with rich text runs) rather than “cell formatting.”
|
|
50
|
-
|
|
51
|
-
Merged cells are represented as a worksheet-level collection `<mergeCells>` containing `<mergeCell ref="...">` ranges, and SpreadsheetML stores the merged range’s formatting and content “in the top left cell.”
|
|
52
|
-
|
|
53
|
-
Hyperlinks are represented in a worksheet as a `<hyperlinks>` collection containing `<hyperlink ref="A11" r:id="rId1" .../>`, where the `r:id` targets a relationship from the worksheet to the hyperlink target resource.
|
|
54
|
-
|
|
55
|
-
Legacy (non-threaded) comments for a worksheet are stored in a separate comments part whose root is `<comments>`, and the worksheet’s relationships part links to the correct comments part for that sheet. Individual comments have an author and can contain richly formatted text.
|
|
56
|
-
|
|
57
|
-
Defined names are workbook-level entities (`definedName` elements) representing a cell, range, formula, or constant; they can be scoped to a sheet (`localSheetId`) and can refer across workbooks. The spec-adjacent guidance also notes that a defined name ambiguous with a cell reference can override it, and that a compliant producer/consumer should treat a defined name *in the A1–XFD1048576 range* as an error (i.e., names that look like cell references are special).
|
|
58
|
-
|
|
59
|
-
Modern Excel includes additional part types and relationships beyond base SpreadsheetML—e.g., **threaded comments** (worksheet-level threaded comment parts) and a workbook-level **persons** part. The Excel extensions spec (MS-XLSX) describes these part content types and mandatory implicit relationships (e.g., persons part from workbook; threaded comments part from worksheet).
|
|
60
|
-
|
|
61
|
-
Modern Excel also introduces newer workbook constructs such as rich value data and related “rich” part families (rich value data, rich styles, supporting property bag structures, etc.), with required part presence and required implicit relationship chains. These are high-risk for an editor that doesn’t implement them because they can be semantically linked to cell values and metadata.
|
|
62
|
-
|
|
63
|
-
### Observed Excel behavior constraints (host correctness)
|
|
64
|
-
|
|
65
|
-
Excel has hard worksheet size limits: 16,384 columns (up to XFD) by 1,048,576 rows. This matters for canonical bounds, validations, and for defining “illegal operations” that should fail or block early.
|
|
66
|
-
|
|
67
|
-
When merging cells in Excel UI, **only one cell’s contents remain**—for left-to-right locales, the upper-left cell’s contents are kept and the other merged cells’ contents are deleted. This is destructive both in UI and in export semantics, so merge needs explicit warn/block behavior.
|
|
68
|
-
|
|
69
|
-
Relative vs absolute references: Excel’s default is relative referencing, and copying formulas adjusts relative references; absolute references (with `$`) do not adjust. This drives copy/paste logic and formula AST representation.
|
|
70
|
-
|
|
71
|
-
Excel supports both A1 and R1C1 reference styles; R1C1 is explicitly described as useful for computing row/column positions and is a first-class concept in Excel options and macro recording. This impacts how you normalize formulas and how you define “same formula” for shared-formula classification (shared formulas are defined relative to R1C1 equivalence in spec-adjacent docs).
|
|
72
|
-
|
|
73
|
-
Inserting rows/columns/cells shifts cells, and cell references “automatically adjust to match the location of the shifted cells” (explicitly stated in official Excel for Mac guidance). This makes reference remapping a **must**, not a nice-to-have.
|
|
74
|
-
|
|
75
|
-
Deleting referenced rows/columns can produce `#REF!` because the formula refers to an invalid cell. Excel’s support guidance shows this specifically with deleting a referenced column and Excel rewriting the formula to include `#REF!`. This is a key failure mode for partial editors that do not remap or that incorrectly remap references during structural edits.
|
|
76
|
-
|
|
77
|
-
Modern Excel has two distinct annotation models: threaded **comments** vs legacy **notes**. Notes behave like older comments; comments are threaded discussions. This has direct implications for “comment/note” modeling and for preserve-only decisions around threaded comments if you don’t implement the persons + threaded comments structures.
|
|
78
|
-
|
|
79
|
-
### Architecture constraints inherited from your DOCX editor
|
|
80
|
-
|
|
81
|
-
Your DOCX editor’s compliance stance is explicit: treat the document as an OPC package, preserve unknown parts/relationships, classify every feature as supported-roundtrip vs preserve-only vs unsupported-fatal, and never silently discard preservable content.
|
|
82
|
-
|
|
83
|
-
The DOCX editor’s runtime takes full ownership of the live session; React consumes a derived render snapshot via `useSyncExternalStore`, and all changes flow through runtime dispatch and transactions.
|
|
84
|
-
|
|
85
|
-
The repo implements a clear command/transaction separation: commands are executed against the runtime state to produce a transaction containing `nextState`, a `mapping` (used to remap anchors), explicit `effects`, and a history boundary (`push` or `skip`), then committed into runtime history.
|
|
86
|
-
|
|
87
|
-
Selection/anchor remapping in the DOCX editor is implemented as a deterministic “mapping steps” pipeline with explicit detached-anchor outcomes for deleted/invalidated structures. This is a strong reusable pattern for spreadsheet anchors (selection ranges, comments, hyperlinks, drawings) that must survive structural edits.
|
|
88
|
-
|
|
89
|
-
## Recommended design / decisions
|
|
90
|
-
|
|
91
|
-
This section is the core “implementation-ready spec” for the canonical workbook model, selection semantics, and command/transaction design. Where decisions reflect unavoidable Excel behavior, the rationale ties back to the findings; where the decision is an architectural choice, the tradeoffs and failure modes are called out explicitly.
|
|
92
|
-
|
|
93
|
-
### Canonical state boundaries
|
|
94
|
-
|
|
95
|
-
**Decision: the runtime owns exactly one canonical workbook state (“WorkbookState”). React owns only transient UI state.** This matches the proven pattern in your DOCX editor: UI renders from a runtime snapshot projection and never mutates canonical state.
|
|
96
|
-
|
|
97
|
-
Canonical state MUST include everything required to:
|
|
98
|
-
|
|
99
|
-
- deterministically project the visible grid (values, formats, merges, row/col properties, selection-relevant constraints), and
|
|
100
|
-
- export an `.xlsx` that reopens in modern Excel without repair prompts or silent loss of content the editor claims to support.
|
|
101
|
-
|
|
102
|
-
Non-canonical (derived) data MUST be recomputable from canonical state: indexes, dependency graphs, render caches, tile caches, etc.
|
|
103
|
-
|
|
104
|
-
UI state MUST NOT be used as persistence, and MUST NOT become a second “truth” (e.g., don’t let React local state “remember” cell values).
|
|
105
|
-
|
|
106
|
-
### Stable identity strategy
|
|
107
|
-
|
|
108
|
-
Spreadsheets force a split between “identity by address” and “identity by object.”
|
|
109
|
-
|
|
110
|
-
**Decision:**
|
|
111
|
-
- Use **stable IDs** for objects that are not intrinsically identified by grid position (sheets, defined names, styles, comments/notes, hyperlink objects, drawing objects, preservation fragments).
|
|
112
|
-
- Use **address identity** for cell occupancy in the grid (sheetId + row + col) because SpreadsheetML and Excel semantics are fundamentally address-based for most core behavior (formulas, merges, hyperlinks, comment anchors).
|
|
113
|
-
|
|
114
|
-
This yields a clean rule: a cell “record” is the content currently occupying that address; structural edits *move* those records by remapping addresses (not by generating new per-cell IDs).
|
|
115
|
-
|
|
116
|
-
**Failure mode avoided:** per-cell UUID identity encourages treating content as a “floating object” that can detach from the grid, which diverges from how Excel persists the core grid and how formulas/anchors refer to it.
|
|
117
|
-
|
|
118
|
-
### Sparse vs dense storage
|
|
119
|
-
|
|
120
|
-
Excel’s grid is huge (up to 1,048,576 × 16,384), but real workbooks are sparse.
|
|
121
|
-
|
|
122
|
-
**Decision: canonical sheet storage is sparse by default.**
|
|
123
|
-
- Store only non-empty or non-default cells.
|
|
124
|
-
- Store row/column properties only where they differ from defaults.
|
|
125
|
-
- Maintain merges, hyperlinks, comments, and drawings as separate sparse collections indexed by ranges/anchors (not as “cell subfields everywhere”).
|
|
126
|
-
|
|
127
|
-
**Tradeoff:** A sparse model complicates “iterate contiguous ranges” operations and rendering. Solve this with derived indexes and tile caches (non-canonical) rather than densifying canonical state.
|
|
128
|
-
|
|
129
|
-
### Cell value typing
|
|
130
|
-
|
|
131
|
-
SpreadsheetML differentiates types via the cell’s `t` attribute and value representation (`<v>` vs `<is>`).
|
|
132
|
-
|
|
133
|
-
**Decision: canonical value is a discriminated union that preserves both semantic type and roundtrip-critical raw representation.**
|
|
134
|
-
|
|
135
|
-
Key rule: **do not coerce values “for convenience”** (e.g., do not convert everything into JS numbers/Date objects in canonical state). The canonical state must be export-safe, and export safety depends on preserving certain lexical and structural distinctions (shared strings vs inline strings, cached formula results, date typing when used, error values).
|
|
136
|
-
|
|
137
|
-
Recommended canonical union:
|
|
138
|
-
|
|
139
|
-
- `empty` (cell record absent in sparse map)
|
|
140
|
-
- `number` (store as string + optional parsed double in derived cache)
|
|
141
|
-
- `boolean`
|
|
142
|
-
- `error` (`#REF!`, `#DIV/0!`, etc.)
|
|
143
|
-
- `text` (plain string)
|
|
144
|
-
- `richText` (SpreadsheetML rich runs; preserve-only if you don’t implement rich text editing)
|
|
145
|
-
- `dateIso` (only if imported/created using `t="d"` semantics; keep ISO string)
|
|
146
|
-
- `formula` (see next section)
|
|
147
|
-
|
|
148
|
-
**Explicit honesty requirement:** if you can’t roundtrip rich text edits safely, mark those cells as **preserve-only** and lock editing, rather than flattening rich runs into plain strings and silently losing formatting.
|
|
149
|
-
|
|
150
|
-
### Formula storage and reference representation
|
|
151
|
-
|
|
152
|
-
SpreadsheetML stores formula text in `<f>` and cached last result in `<v>`.
|
|
153
|
-
Excel supports shared formulas (`t="shared"` with shared index `si` and applied range `ref`) and considers formulas “the same” based on R1C1 equivalence.
|
|
154
|
-
|
|
155
|
-
Your editor needs formula handling for two distinct reasons:
|
|
156
|
-
|
|
157
|
-
1. **Editing** (user changes formula text).
|
|
158
|
-
2. **Structural remapping** (insert/delete rows/cols, rename sheets, copy/paste) — Excel adjusts references automatically and will emit `#REF!` when references become invalid.
|
|
159
|
-
|
|
160
|
-
**Decision: store formulas as both syntax and structured reference tokens (not as a computed semantic graph), and treat the dependency graph as derived index only.**
|
|
161
|
-
|
|
162
|
-
- **Syntax layer (canonical):** original formula string (normalized) + a token stream preserving unrecognized segments (“lossless tokenization”).
|
|
163
|
-
- **Reference layer (canonical):** extracted reference tokens represented as typed nodes with explicit absolute/relative flags and sheet binding.
|
|
164
|
-
- **Semantic graph (derived):** dependency edges computed from recognized reference tokens for UI affordances only (precedents/dependents highlighting, recalculation “dirty” marking), not for full calculation.
|
|
165
|
-
|
|
166
|
-
This is directly aligned with your “don’t pretend you can safely edit what you can’t represent” posture: if a formula cannot be tokenized into a lossless structure that supports remapping, structural edits that require remapping become block/export-block (or preserve-only with locked structural ops).
|
|
167
|
-
|
|
168
|
-
**A1 vs R1C1:**
|
|
169
|
-
- **Canonical internal references should be sheetId + row/col with absolute/relative flags**, because that is what you must update under copy/paste and row/col edits.
|
|
170
|
-
- Formula strings can be normalized to A1 for display/editing, but you should be able to derive an R1C1 representation when needed for equivalence checks (e.g., shared-formula grouping) and for some API surfaces.
|
|
171
|
-
|
|
172
|
-
**Shared formulas:**
|
|
173
|
-
- Editing shared formulas correctly is complex because `si` groups and relative expansion can be invalidated by intermediate explicit formulas.
|
|
174
|
-
- **Decision: runtime canonical form stores per-cell explicit formulas**, and shared-formula storage is treated as an export optimization only (optional). If you can’t prove your shared-formula encoder matches Excel behavior for all formula forms you support, export explicit formulas instead.
|
|
175
|
-
- **Tradeoff:** larger files.
|
|
176
|
-
- **Benefit:** reduced risk of “Excel repaired content” prompts due to invalid shared-formula semantics.
|
|
177
|
-
- Compatibility posture: size is cheap; trust is expensive.
|
|
178
|
-
|
|
179
|
-
### Selection model
|
|
180
|
-
|
|
181
|
-
Spreadsheets need a selection model that supports:
|
|
182
|
-
- active sheet, active cell
|
|
183
|
-
- rectangular range selection
|
|
184
|
-
- row/column/sheet selection
|
|
185
|
-
- optional multi-range selection (Excel supports discontiguous selections)
|
|
186
|
-
|
|
187
|
-
Separately, SpreadsheetML may persist view selection in `sheetViews`/`selection` markup, but your runtime selection is primarily for correctness of edit semantics, not just UI.
|
|
188
|
-
|
|
189
|
-
**Decision: represent selection ranges as rectangles in “gridline coordinates” (half-open intervals), not as inclusive cell pairs.**
|
|
190
|
-
|
|
191
|
-
Rationale: insertion/deletion acts on boundaries between rows/cols. Using half-open intervals makes remapping deterministic and avoids ambiguous boundary behavior (the same design principle as your DOCX editor’s position mapping and anchor association model).
|
|
192
|
-
|
|
193
|
-
Terminology:
|
|
194
|
-
|
|
195
|
-
- `Row` is 1..1,048,576; `RowLine` is 1..1,048,577 (lines between rows, including after last row).
|
|
196
|
-
- `Col` is 1..16,384; `ColLine` is 1..16,385.
|
|
197
|
-
|
|
198
|
-
A single cell `(r,c)` is the rectangle `[r, r+1) × [c, c+1)` in lines.
|
|
199
|
-
|
|
200
|
-
Selection shape:
|
|
201
|
-
|
|
202
|
-
- `activeSheetId: SheetId`
|
|
203
|
-
- `activeCell: CellAddr` (always a single cell)
|
|
204
|
-
- `primary: RectRange` (gridline rectangle)
|
|
205
|
-
- `ranges?: RectRange[]` (optional multi-range; if unsupported in v1, treat as singleton array and explicitly block Ctrl+multi-select UI)
|
|
206
|
-
- `mode: "cells" | "rows" | "cols" | "sheet"`
|
|
207
|
-
- `anchor: CellAddr` (for keyboard extend; analogous to DOCX anchor/head)
|
|
208
|
-
|
|
209
|
-
**Merged cells and selection:**
|
|
210
|
-
- SpreadsheetML stores merged ranges but content is top-left.
|
|
211
|
-
- Excel UI merge behavior deletes non-top-left values.
|
|
212
|
-
- **Decision:** runtime selection always “snaps” into a merged region:
|
|
213
|
-
- If activeCell is inside a merged range, selection primary becomes exactly the merge rectangle, and activeCell becomes the merge’s top-left cell.
|
|
214
|
-
- Editing within a merge writes to the top-left cell only; other cells in the merge are not independently editable.
|
|
215
|
-
- Any command that would partially cut a merge (delete only some cells inside) is either escalated to a merge-aware operation (unmerge first) or blocked depending on your feature commitments.
|
|
216
|
-
|
|
217
|
-
**Failure mode avoided:** allowing a selection that targets a non-top-left merged cell and writing value there produces an export that disagrees with Excel’s interpretation and can create “lost” user edits.
|
|
218
|
-
|
|
219
|
-
### Comments/notes anchors
|
|
220
|
-
|
|
221
|
-
Legacy comments/notes are stored per worksheet in a comments part linked by worksheet relationships, and each comment is anchored conceptually to a cell reference.
|
|
222
|
-
Modern Excel comments are threaded and require additional parts (threaded comments part per sheet; persons part per workbook).
|
|
223
|
-
|
|
224
|
-
**Decision: canonical model supports two annotation concepts:**
|
|
225
|
-
- `Note` (legacy, single-cell anchored; “what older Excel called comments”)
|
|
226
|
-
- `CommentThread` (threaded, single-cell anchored), but default v1 stance should be **preserve-only** unless you implement persons + threaded comments export fully.
|
|
227
|
-
|
|
228
|
-
If you do not implement threaded comments as supported-roundtrip, you still must:
|
|
229
|
-
- preserve threaded comment parts and persons part, and
|
|
230
|
-
- block edits that would require you to semantically rewrite those threads or their anchors (e.g., deleting cells that contain threaded comments).
|
|
231
|
-
|
|
232
|
-
Anchor representation:
|
|
233
|
-
|
|
234
|
-
- `CellAnchor = { sheetId, cell: CellAddr }`
|
|
235
|
-
- When row/col insert/delete shifts the anchored cell, remap the anchor via transaction mapping. This is identical in spirit to remapping DOCX comment anchors through content mappings.
|
|
236
|
-
|
|
237
|
-
Detach semantics:
|
|
238
|
-
- If the anchor cell is deleted, the note/comment becomes `detached` (retained in preservation store) or deleted depending on feature class:
|
|
239
|
-
- supported-roundtrip notes: either delete (matching Excel likely behavior) or detach with explicit warning; pick one and test fixtures.
|
|
240
|
-
- preserve-only threaded comments: **detach + warn + preserve**, because deleting them silently is forbidden.
|
|
241
|
-
|
|
242
|
-
### Hyperlinks anchors
|
|
243
|
-
|
|
244
|
-
Hyperlinks are stored as worksheet `hyperlinks` with `ref` and relationship target.
|
|
245
|
-
|
|
246
|
-
**Decision: represent hyperlinks as distinct objects with range anchors**:
|
|
247
|
-
- `Hyperlink = { id, sheetId, range: RectRange, target: ExternalUrl | InternalLocation, tooltip?, display? }`
|
|
248
|
-
|
|
249
|
-
Remap `range` through transactions. If a structural edit splits the hyperlink range into disjoint ranges, decide:
|
|
250
|
-
- If you support multi-range hyperlinks: split into multiple hyperlinks.
|
|
251
|
-
- If not: block the operation or collapse to intersection with warn (explicit).
|
|
252
|
-
Given Excel’s object model is range-based but UI is cell-based, safest is: **block** disjoint splits in v1 unless you prove Excel’s behavior and replicate it.
|
|
253
|
-
|
|
254
|
-
### Images/drawings references
|
|
255
|
-
|
|
256
|
-
Drawings are high risk: they involve additional parts and coordinate anchors that must remap under row/col operations. Even the Open XML SDK surface treats drawings as separate parts linked from worksheets.
|
|
257
|
-
|
|
258
|
-
**Decision: include drawings/images only as preserve-only objects in canonical state in v1**, unless you explicitly commit to supporting anchor remapping and safe export for at least one drawing anchor type.
|
|
259
|
-
|
|
260
|
-
Canonical representation should still exist so the runtime can:
|
|
261
|
-
- detect overlap with user operations,
|
|
262
|
-
- lock regions/operations that would break them, and
|
|
263
|
-
- preserve parts and relationships.
|
|
264
|
-
|
|
265
|
-
Minimal representation:
|
|
266
|
-
- `DrawingObject = { drawingId, sheetId, kind, anchor: CellOrRangeAnchor (approx), rels: { partName, rId }, preserveRef }`
|
|
267
|
-
- `preserveRef` binds to the preservation store which keeps the original parts/relationships intact.
|
|
268
|
-
|
|
269
|
-
**Honesty rule:** if you cannot prove remapping of drawing anchors under insert/delete, you must treat row/col structural edits that intersect drawing anchor bands as **block export** or **block operation** (not “best effort”), because Excel reopen correctness is the standard.
|
|
270
|
-
|
|
271
|
-
### Preserve-only regions and unsupported content binding
|
|
272
|
-
|
|
273
|
-
Modern Excel workbooks can include advanced part families (threaded comments, persons, rich values, named sheet views, etc.) that may be present even when the visible grid looks simple.
|
|
274
|
-
|
|
275
|
-
**Decision: introduce a first-class `PreservedFeature` and `PreserveOnlyRegion` model** that binds unknown/unsupported semantics back to canonical state as locked placeholders or constraints.
|
|
276
|
-
|
|
277
|
-
Core requirements, directly inherited from your DOCX editor’s compliance model:
|
|
278
|
-
|
|
279
|
-
- Feature classes: `supported-roundtrip`, `preserve-only`, `unsupported-fatal`.
|
|
280
|
-
- Response model: preserve, lock, warn, block, fail.
|
|
281
|
-
|
|
282
|
-
In spreadsheets, preserve-only must cover both:
|
|
283
|
-
- **Package-level preserved parts and relationships** (unknown parts, future extLst, vendor namespaces).
|
|
284
|
-
- **Grid-affecting preserved regions** where edits would require semantic rewrites you don’t support (tables, pivot caches, rich value bindings, threaded comments anchors).
|
|
285
|
-
|
|
286
|
-
Model them explicitly as constraints:
|
|
287
|
-
- `PreserveOnlyRegion = { id, sheetId, range: RectRange, featureKey, severity, behaviorOnEdit }`
|
|
288
|
-
- `behaviorOnEdit` ∈ { lock, warn, block, fail } with a message string and exportBlock flag if necessary.
|
|
289
|
-
|
|
290
|
-
This makes “host honesty” enforceable: if you haven’t implemented a feature’s semantics, the runtime can still prevent destructive edits and still preserve the underlying bytes/markup.
|
|
291
|
-
|
|
292
|
-
### Command taxonomy
|
|
293
|
-
|
|
294
|
-
Follow the DOCX editor’s proven pattern: a discriminated union of commands, executed against runtime state to yield deterministic transactions.
|
|
295
|
-
|
|
296
|
-
Recommended command categories (v1 scope-focused):
|
|
297
|
-
|
|
298
|
-
**Selection + navigation**
|
|
299
|
-
- `selection.set` (set active sheet/cell + ranges + mode)
|
|
300
|
-
- `sheet.activate`
|
|
301
|
-
|
|
302
|
-
**Cell content**
|
|
303
|
-
- `cell.setValue`
|
|
304
|
-
- `cell.clear`
|
|
305
|
-
- `cell.setFormula`
|
|
306
|
-
- `range.paste` (with paste payload + mode: values/formulas/formats)
|
|
307
|
-
|
|
308
|
-
**Structural (high risk; must be mapping-backed)**
|
|
309
|
-
- `rows.insert`, `rows.delete`
|
|
310
|
-
- `cols.insert`, `cols.delete`
|
|
311
|
-
- `cells.insertShiftRight`, `cells.insertShiftDown` (optional)
|
|
312
|
-
- `cells.deleteShiftLeft`, `cells.deleteShiftUp` (optional)
|
|
313
|
-
|
|
314
|
-
**Formatting**
|
|
315
|
-
- `cell.setStyle`, `range.setStyle`, `row.setProps`, `col.setProps`
|
|
316
|
-
|
|
317
|
-
**Merges**
|
|
318
|
-
- `merge.apply`, `merge.remove`, `merge.toggle`
|
|
319
|
-
|
|
320
|
-
**Objects/anchors (initially preserve-only or limited)**
|
|
321
|
-
- `note.add/edit/remove` (legacy note)
|
|
322
|
-
- `hyperlink.add/edit/remove`
|
|
323
|
-
- `commentThread.*` (threaded comments: preserve-only unless fully supported)
|
|
324
|
-
|
|
325
|
-
**Workbook structure**
|
|
326
|
-
- `sheet.add`, `sheet.delete`, `sheet.rename`, `sheet.reorder`
|
|
327
|
-
- `name.define`, `name.delete`, `name.rename`
|
|
328
|
-
|
|
329
|
-
**Runtime/diagnostics/history**
|
|
330
|
-
- `warning.add/clear`, `runtime.setReadOnly`, `history.undo/redo` (pattern-aligned with DOCX editor).
|
|
331
|
-
|
|
332
|
-
### Transaction structure
|
|
333
|
-
|
|
334
|
-
Adopt the DOCX editor’s transaction contract shape, but replace “document mapping” with “workbook mapping” that spans sheets + rows + cols + rectangle anchors.
|
|
335
|
-
|
|
336
|
-
**Decision: every mutating command produces exactly one `WorkbookTransaction`.** Batching is handled by a single command producing a transaction with multiple low-level ops, not by multiple commits.
|
|
337
|
-
|
|
338
|
-
Transaction fields (recommended):
|
|
339
|
-
|
|
340
|
-
- `nextState: WorkbookState`
|
|
341
|
-
- `mapping: WorkbookMapping` (used to remap selection, anchors, formulas, names)
|
|
342
|
-
- `effects: TransactionEffects` (e.g., “selection changed”, “warnings added”, “export blocked changed”)
|
|
343
|
-
- `historyBoundary: "push" | "skip"`
|
|
344
|
-
- `markDirty: boolean`
|
|
345
|
-
|
|
346
|
-
This parallels the DOCX runtime’s commit behavior and history boundaries.
|
|
347
|
-
|
|
348
|
-
**Undo/redo boundary rule:** undo/redo happens at transaction granularity, not per keystroke unless you intentionally coalesce commands into a single transaction (e.g., typing in formula bar). This matches the DOCX editor’s `historyBoundary` semantics.
|
|
349
|
-
|
|
350
|
-
### Mapping/remap rules
|
|
351
|
-
|
|
352
|
-
Excel’s documented behavior requires reference adjustment when inserting cells/rows/cols, and invalid references yield `#REF!`.
|
|
353
|
-
|
|
354
|
-
**Decision: mapping is first-class and explicit, and is applied to:**
|
|
355
|
-
- selection rectangles
|
|
356
|
-
- merged ranges
|
|
357
|
-
- hyperlink ranges
|
|
358
|
-
- comment/note anchors
|
|
359
|
-
- defined name formulas
|
|
360
|
-
- cell formula reference tokens (not raw strings)
|
|
361
|
-
|
|
362
|
-
#### Mapping model
|
|
363
|
-
|
|
364
|
-
Borrow the DOCX mapping “steps” model, but apply it per axis and per sheet. The DOCX editor maps positions through a list of `{from,to,insertSize}` steps and can detach anchors when deleted/invalidated.
|
|
365
|
-
|
|
366
|
-
Spreadsheet version:
|
|
367
|
-
|
|
368
|
-
- `RowLineMappingSteps[]` and `ColLineMappingSteps[]` per affected sheet
|
|
369
|
-
- `SheetMapping` for renames/reorders (sheetId stable; name changes tracked for formula string regeneration)
|
|
370
|
-
- `CellMoveMapping` for copy/paste “relative reference translation” operations (optional explicit representation; often derived from source/dest addresses)
|
|
371
|
-
|
|
372
|
-
Anchor mapping returns:
|
|
373
|
-
- updated anchor, or
|
|
374
|
-
- detached anchor with reason `deleted` or `invalidatedByStructureChange` (mirror DOCX).
|
|
375
|
-
|
|
376
|
-
#### Specific remap rules you must implement
|
|
377
|
-
|
|
378
|
-
**Row/column insert/delete**
|
|
379
|
-
- Shift cells’ addresses in sparse cell map.
|
|
380
|
-
- Remap:
|
|
381
|
-
- selection
|
|
382
|
-
- merges (`mergeCell ref`)
|
|
383
|
-
- hyperlinks (`hyperlink ref`)
|
|
384
|
-
- legacy note/comment anchors (comments part references)
|
|
385
|
-
- formula reference tokens and defined name formulas
|
|
386
|
-
- If a referenced cell/range is deleted, rewrite reference node to `#REF!` at export and mark formula as having an invalid reference (and surface warning). This matches Excel’s `#REF!` behavior when referenced cells are deleted.
|
|
387
|
-
|
|
388
|
-
**Sheet rename**
|
|
389
|
-
- Keep `sheetId` stable; update `sheet.name`.
|
|
390
|
-
- Formula internal references should use `sheetId` where resolvable, so renames do not require semantic remapping—only formula string regeneration on export.
|
|
391
|
-
- If a formula references a sheet by name that cannot be resolved at import (external workbook, missing sheet), keep it as an “external sheet reference token” and treat rename as non-applicable.
|
|
392
|
-
|
|
393
|
-
**Merge/unmerge**
|
|
394
|
-
- `merge.apply`:
|
|
395
|
-
- If merging a range where multiple cells contain values, Excel deletes all but top-left.
|
|
396
|
-
- Therefore:
|
|
397
|
-
- default action should be **warn** (explicit consent) or **block** (if host says “no destructive merges”) unless the range is value-empty except top-left.
|
|
398
|
-
- `merge.remove`:
|
|
399
|
-
- Unmerge does not restore deleted values; it only changes structure.
|
|
400
|
-
- Structural ops interacting with merges:
|
|
401
|
-
- Partial delete of a merge region is either blocked or requires “unmerge then apply operation” behavior, depending on your supported-roundtrip commitments.
|
|
402
|
-
|
|
403
|
-
**Copy/paste**
|
|
404
|
-
- Values: straightforward copy of `CellValue` union.
|
|
405
|
-
- Formulas: apply translation vector `(dRow, dCol)` from source cell to destination cell to adjust **relative** references; do not change absolute references.
|
|
406
|
-
- Formats: style references copied, but style records deduplicated via style IDs (see style model below).
|
|
407
|
-
|
|
408
|
-
**Formula edits**
|
|
409
|
-
- Parsing must be at least reference-token aware. If you can’t parse/roundtrip a formula without risk, classify that formula as preserve-only and lock structural operations that would require token rewriting (row/col insert/delete, range paste that shifts). This is the same strictness rule your DOCX editor applies to unsupported OOXML: no “best effort” silent corruption.
|
|
410
|
-
|
|
411
|
-
### Invariants (must always hold)
|
|
412
|
-
|
|
413
|
-
These are runtime-enforced invariants; violating them is an internal error and should surface as `internal_invariant` error and block export, consistent with your DOCX error posture.
|
|
414
|
-
|
|
415
|
-
Core invariants:
|
|
416
|
-
|
|
417
|
-
- Sheet bounds: all row/col indexes in canonical state are within Excel limits (1..1,048,576 rows; 1..16,384 cols).
|
|
418
|
-
- Sheet IDs are stable and unique; sheet names are unique (Excel requirement; enforce at runtime).
|
|
419
|
-
- Sparse cell map contains no duplicate addresses and no cells outside bounds.
|
|
420
|
-
- Merge ranges do not overlap within a sheet; each merge is a valid rectangle within bounds; merge content exists only in top-left cell (content in other cells is either empty or treated as “masked by merge”).
|
|
421
|
-
- Hyperlink ranges do not overlap in ways your exporter cannot represent (if overlapping is possible in OOXML, still decide if your editor supports it; otherwise mark preserve-only and block edits).
|
|
422
|
-
- Comments/notes anchors point to valid cells or are explicitly detached with a reason.
|
|
423
|
-
- Defined names that conflict with cell references are validated according to the spec-adjacent rule that names in A1–XFD1048576 are treated as an error.
|
|
424
|
-
- Unsupported/preserve-only parts and regions have explicit feature entries and are never silently discarded.
|
|
425
|
-
|
|
426
|
-
### Serialization to persistence snapshot
|
|
427
|
-
|
|
428
|
-
Follow the DOCX editor’s distinction between a persisted snapshot and runtime render snapshots: persisted snapshot stores canonical state + compatibility/warnings; render snapshots are derived for UI and are not a persistence contract.
|
|
429
|
-
|
|
430
|
-
**Decision: persisted snapshot includes:**
|
|
431
|
-
- `canonicalWorkbook` (WorkbookState)
|
|
432
|
-
- preservation store (package parts/relationships and opaque fragments required for non-dropping guarantee)
|
|
433
|
-
- compatibility report + warning log (explicit feature entries, export-block flags)
|
|
434
|
-
|
|
435
|
-
**Export rule:** for “package-first” fidelity, exporter rewrites only editor-owned parts and reattaches preserved parts/relationships, matching your DOCX package-first rules.
|
|
436
|
-
|
|
437
|
-
### What belongs where (canonical vs derived vs UI)
|
|
438
|
-
|
|
439
|
-
**Canonical state (must persist, must roundtrip):**
|
|
440
|
-
- workbook metadata and settings that affect meaning (date system if you choose to support; calculation settings if you touch them)
|
|
441
|
-
- sheets, rows, columns, cells, merges
|
|
442
|
-
- style references (not necessarily fully expanded style tables; see below)
|
|
443
|
-
- defined names
|
|
444
|
-
- notes/comments/hyperlinks that you claim supported-roundtrip
|
|
445
|
-
- preservation store + preserve-only region constraints
|
|
446
|
-
- compatibility feature entries + warnings that affect export (block/warn state)
|
|
447
|
-
|
|
448
|
-
**Derived indexes (rebuildable):**
|
|
449
|
-
- used-range bounding boxes
|
|
450
|
-
- row/col property interval indexes
|
|
451
|
-
- merge overlap index (interval tree)
|
|
452
|
-
- formula dependency edges (from reference tokens)
|
|
453
|
-
- render tiles (viewport cell matrices), text measurement caches, style resolution caches
|
|
454
|
-
|
|
455
|
-
**UI state (ephemeral):**
|
|
456
|
-
- scroll offsets; viewport size; zoom
|
|
457
|
-
- editing mode (formula bar active, in-cell edit)
|
|
458
|
-
- selection handles/drag state; clipboard UI state
|
|
459
|
-
- hover/active menus, open panels
|
|
460
|
-
|
|
461
|
-
This is exactly the separation your DOCX editor enforces: UI local state exists, but it never becomes a parallel document truth.
|
|
462
|
-
|
|
463
|
-
## Risks and open questions
|
|
464
|
-
|
|
465
|
-
### Formula completeness is the biggest correctness cliff
|
|
466
|
-
|
|
467
|
-
Excel formulas span huge surface area: shared formulas, structured references (tables), external references, dynamic arrays, newer “rich values,” etc. The risk is not “we can’t calculate”—you can ship without a calc engine—but “we can’t **remap** references safely under structural edits.” Excel explicitly adjusts references on cell shifts, and invalid refs become `#REF!`.
|
|
468
|
-
|
|
469
|
-
Open question you must answer early (release gate): **What formula grammar is supported-roundtrip?** Everything else must be preserve-only or unsupported-fatal, and structural operations that would require rewriting unsupported formulas must be blocked with explicit reason.
|
|
470
|
-
|
|
471
|
-
### Merges are inherently destructive and need explicit UX+API policy
|
|
472
|
-
|
|
473
|
-
Excel merge deletes non-top-left data. That is not “user error,” it is the product’s behavior.
|
|
474
|
-
If your editor allows merge across non-empty cells without an explicit warn/confirm gate, you will violate the “response model must stay explicit” requirement.
|
|
475
|
-
|
|
476
|
-
### Threaded comments, persons part, and modern Excel part families
|
|
477
|
-
|
|
478
|
-
Threaded comments require additional parts and implicit relationships (worksheet threaded comments part, workbook persons part).
|
|
479
|
-
If you don’t implement them fully, treat them as preserve-only and block destructive edits to their anchors. Otherwise, you risk silent supported-content loss.
|
|
480
|
-
|
|
481
|
-
Similarly, rich value data and supporting property bag parts indicate modern semantic payloads that may bind into cell metadata.
|
|
482
|
-
If present, they are likely preserve-only at first—and may force you to block some edits.
|
|
483
|
-
|
|
484
|
-
### Drawings/images are a structural-edit trap
|
|
485
|
-
|
|
486
|
-
Drawings are separate parts and may have anchors that must move under insert/delete.
|
|
487
|
-
If you can preserve them but not remap anchors, you must block structural operations that intersect them (or treat workbook as export-blocked after such operations). “Best effort” will produce broken layouts or Excel repairs.
|
|
488
|
-
|
|
489
|
-
### Performance + undo/redo memory pressure
|
|
490
|
-
|
|
491
|
-
Unlike DOCX text, spreadsheet edits can touch thousands of sparse cells at once (pastes, fills). The DOCX runtime history stores whole previous states in memory stacks. That approach will not scale for large sheets.
|
|
492
|
-
You need either persistent data structures or patch/inverse-patch storage to keep undo feasible.
|
|
493
|
-
|
|
494
|
-
### Validation gate ambiguity: schema-valid vs Excel-correct
|
|
495
|
-
|
|
496
|
-
Open XML SDK schema validation helps but is not sufficient; your DOCX editor explicitly states host behavior is authoritative and SDK validation is necessary-but-not-sufficient.
|
|
497
|
-
Same applies here: your release gates must include Excel reopen tests on a fixture corpus.
|
|
498
|
-
|
|
499
|
-
## Concrete deliverable in the requested format
|
|
500
|
-
|
|
501
|
-
This section is intentionally “repo-ready”: module boundaries, canonical type sketches, command & transaction types, mapping rules, invariants, and example transactions.
|
|
502
|
-
|
|
503
|
-
### Proposed module boundaries
|
|
504
|
-
|
|
505
|
-
A minimal but clean separation (mirrors your DOCX repo’s separation of model/core/runtime/io/preservation/validation).
|
|
506
|
-
|
|
507
|
-
- `src/model/`
|
|
508
|
-
- `workbook-types.ts` (canonical types)
|
|
509
|
-
- `formula-types.ts` (token + ref nodes)
|
|
510
|
-
- `style-types.ts`
|
|
511
|
-
- `anchors.ts` (grid anchors + mapping)
|
|
512
|
-
- `src/core/`
|
|
513
|
-
- `commands.ts` (public command union)
|
|
514
|
-
- `transaction.ts` (transaction shape + commit helpers)
|
|
515
|
-
- `mapping.ts` (row/col line mapping, anchor remap, reference remap)
|
|
516
|
-
- `invariants.ts` (assertions + validators)
|
|
517
|
-
- `src/runtime/`
|
|
518
|
-
- `workbook-runtime.ts` (subscribe, dispatch, undo/redo, getSnapshot, export)
|
|
519
|
-
- `render-snapshot.ts` (derived UI projection; viewport slicing)
|
|
520
|
-
- `src/io/`
|
|
521
|
-
- `xlsx-import.ts` (OPC parse → canonical + preservation)
|
|
522
|
-
- `xlsx-export.ts` (canonical + preserved package → OPC)
|
|
523
|
-
- `src/preservation/`
|
|
524
|
-
- `package-preservation.ts` (parts/relationships/content types)
|
|
525
|
-
- `preserve-only-regions.ts` (feature constraints + overlap checks)
|
|
526
|
-
- `src/validation/`
|
|
527
|
-
- `compatibility-engine.ts` (feature entries, export-block decisions)
|
|
528
|
-
|
|
529
|
-
### Canonical data model sketches (TypeScript)
|
|
530
|
-
|
|
531
|
-
These are sketches meant to be copied into code and refined. They intentionally mirror your DOCX editor’s “canonical + preservation + diagnostics” structure.
|
|
532
|
-
|
|
533
|
-
```ts
|
|
534
|
-
// identifiers
|
|
535
|
-
export type WorkbookId = string;
|
|
536
|
-
export type SheetId = string;
|
|
537
|
-
export type StyleId = string;
|
|
538
|
-
export type NameId = string;
|
|
539
|
-
export type NoteId = string;
|
|
540
|
-
export type CommentThreadId = string;
|
|
541
|
-
export type HyperlinkId = string;
|
|
542
|
-
export type DrawingId = string;
|
|
543
|
-
export type PreserveId = string;
|
|
544
|
-
|
|
545
|
-
// bounds per Excel limits
|
|
546
|
-
export type Row = number; // 1..1_048_576
|
|
547
|
-
export type Col = number; // 1..16_384
|
|
548
|
-
export type RowLine = number; // 1..1_048_577 (half-open ranges)
|
|
549
|
-
export type ColLine = number; // 1..16_385
|
|
550
|
-
|
|
551
|
-
export interface CellAddr { row: Row; col: Col; }
|
|
552
|
-
|
|
553
|
-
export interface RectRange {
|
|
554
|
-
sheetId: SheetId;
|
|
555
|
-
row0: RowLine; // inclusive
|
|
556
|
-
row1: RowLine; // exclusive
|
|
557
|
-
col0: ColLine; // inclusive
|
|
558
|
-
col1: ColLine; // exclusive
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
export type FeatureClass = "supported-roundtrip" | "preserve-only" | "unsupported-fatal";
|
|
562
|
-
|
|
563
|
-
export type ResponseMode = "preserve" | "lock" | "warn" | "block" | "fail";
|
|
564
|
-
|
|
565
|
-
export interface CompatibilityFeatureEntry {
|
|
566
|
-
featureEntryId: string;
|
|
567
|
-
featureKey: string; // e.g. "threaded_comments", "rich_values"
|
|
568
|
-
featureClass: FeatureClass;
|
|
569
|
-
response: ResponseMode;
|
|
570
|
-
message: string;
|
|
571
|
-
affectedRange?: RectRange;
|
|
572
|
-
details?: Record<string, unknown>;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
export interface PreservationStore {
|
|
576
|
-
// Package-level (OPC) preservation
|
|
577
|
-
// parts include unknown parts + any parts we choose to preserve raw
|
|
578
|
-
packageParts: Record<string, {
|
|
579
|
-
partName: string; // OPC part name (e.g. "/xl/threadedComments/threadedComment1.xml")
|
|
580
|
-
contentType: string;
|
|
581
|
-
bytesBase64: string; // or Uint8Array in runtime, base64 in persisted snapshot
|
|
582
|
-
relationships?: Array<{ id: string; type: string; target: string; targetMode?: "Internal"|"External" }>;
|
|
583
|
-
}>;
|
|
584
|
-
|
|
585
|
-
// Opaque XML fragments inside parts we rewrite (if you implement fragment placeholders)
|
|
586
|
-
opaqueFragments: Record<string, {
|
|
587
|
-
fragmentId: PreserveId;
|
|
588
|
-
owningPartName: string;
|
|
589
|
-
xml: string; // original markup
|
|
590
|
-
featureKey: string;
|
|
591
|
-
bindsTo?: { sheetId?: SheetId; range?: RectRange; cell?: CellAddr };
|
|
592
|
-
}>;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
export type CellValue =
|
|
596
|
-
| { kind: "number"; raw: string } // preserve lexical; parse in derived cache
|
|
597
|
-
| { kind: "boolean"; value: boolean }
|
|
598
|
-
| { kind: "error"; code: "#REF!" | "#DIV/0!" | "#VALUE!" | "#NAME?" | "#N/A" | "#NUM!" | "#NULL!" }
|
|
599
|
-
| { kind: "text"; text: string }
|
|
600
|
-
| { kind: "richText"; runs: Array<{ text: string; style?: unknown }> } // preserve-only unless you implement editing
|
|
601
|
-
| { kind: "dateIso"; iso: string }; // used when cell t="d"
|
|
602
|
-
|
|
603
|
-
export interface FormulaRef {
|
|
604
|
-
// A reference token inside a formula
|
|
605
|
-
kind: "cell" | "range";
|
|
606
|
-
sheet: { kind: "localSheet"; sheetId: SheetId } | { kind: "external"; workbook?: string; sheetName?: string };
|
|
607
|
-
// absolute/relative flags follow Excel copy semantics
|
|
608
|
-
colAbs: boolean;
|
|
609
|
-
rowAbs: boolean;
|
|
610
|
-
// for kind==="cell"
|
|
611
|
-
col?: Col;
|
|
612
|
-
row?: Row;
|
|
613
|
-
// for kind==="range"
|
|
614
|
-
col2?: Col;
|
|
615
|
-
row2?: Row;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
export interface CellFormulaModel {
|
|
619
|
-
// The original normalized formula text (e.g. without leading '=' in storage, add on render)
|
|
620
|
-
text: string;
|
|
621
|
-
// Lossless tokenization so we can rewrite refs without rewriting everything
|
|
622
|
-
tokens: Array<
|
|
623
|
-
| { kind: "literal"; text: string }
|
|
624
|
-
| { kind: "ref"; ref: FormulaRef; sourceText: string }
|
|
625
|
-
>;
|
|
626
|
-
// Cached last calculated result from <v> (we do not calculate; Excel will on load)
|
|
627
|
-
cachedResult?: CellValue;
|
|
628
|
-
// If tokenization failed, we cannot safely remap => preserve-only
|
|
629
|
-
parseState: "ok" | "lossy" | "failed";
|
|
630
|
-
preserveOnlyReason?: string;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
export interface CellModel {
|
|
634
|
-
addr: CellAddr;
|
|
635
|
-
value?: CellValue; // absent implies empty
|
|
636
|
-
formula?: CellFormulaModel; // if present, value is cachedResult semantics
|
|
637
|
-
styleId?: StyleId;
|
|
638
|
-
// Additional low-level flags that map to cell attributes (kept minimal in canonical)
|
|
639
|
-
// e.g. "cm", "ph" could be preserve-only until fully supported.
|
|
640
|
-
flags?: Record<string, unknown>;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
export interface RowProps { height?: number; hidden?: boolean; outlineLevel?: number; styleId?: StyleId; }
|
|
644
|
-
export interface ColProps { width?: number; hidden?: boolean; outlineLevel?: number; styleId?: StyleId; }
|
|
645
|
-
|
|
646
|
-
export interface MergeModel { id: string; range: RectRange; }
|
|
647
|
-
|
|
648
|
-
export interface SheetModel {
|
|
649
|
-
sheetId: SheetId;
|
|
650
|
-
name: string;
|
|
651
|
-
// Sparse cells keyed by "R{row}C{col}" or similar
|
|
652
|
-
cells: Record<string, CellModel>;
|
|
653
|
-
|
|
654
|
-
// Sparse row/col properties
|
|
655
|
-
rows: Record<Row, RowProps>;
|
|
656
|
-
cols: Array<{ colMin: Col; colMax: Col; props: ColProps }>; // interval representation
|
|
657
|
-
|
|
658
|
-
merges: Record<string, MergeModel>;
|
|
659
|
-
hyperlinks: Record<HyperlinkId, {
|
|
660
|
-
id: HyperlinkId;
|
|
661
|
-
range: RectRange;
|
|
662
|
-
target: { kind: "externalUrl"; url: string } | { kind: "internal"; location: string };
|
|
663
|
-
tooltip?: string;
|
|
664
|
-
display?: string;
|
|
665
|
-
}>;
|
|
666
|
-
|
|
667
|
-
// “Notes” (legacy comments) and threaded comments.
|
|
668
|
-
// Threaded comments should be preserve-only unless fully implemented with persons + part rules.
|
|
669
|
-
notes: Record<NoteId, { id: NoteId; cell: CellAddr; author: string; body: string; richText?: boolean; status: "active" | "detached"; detachedReason?: string }>;
|
|
670
|
-
threadedComments: Record<CommentThreadId, { id: CommentThreadId; cell: CellAddr; status: "preserve-only" | "detached"; preserveRef: PreserveId }>;
|
|
671
|
-
|
|
672
|
-
drawings: Record<DrawingId, { id: DrawingId; status: "preserve-only"; preserveRef: PreserveId; approxAnchor?: RectRange }>;
|
|
673
|
-
|
|
674
|
-
preserveOnlyRegions: Record<string, { id: string; range: RectRange; featureKey: string; response: ResponseMode; message: string }>;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
export interface DefinedNameModel {
|
|
678
|
-
id: NameId;
|
|
679
|
-
name: string;
|
|
680
|
-
scope: "workbook" | { sheetId: SheetId };
|
|
681
|
-
// DefinedName text is itself formula-like (ranges/formulas/constants)
|
|
682
|
-
formula: CellFormulaModel;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
export interface StyleModel {
|
|
686
|
-
id: StyleId;
|
|
687
|
-
// canonical style is normalized; exporter maps to styles.xml tables
|
|
688
|
-
// keep minimal in v1: number format, font, fill, border, alignment
|
|
689
|
-
def: {
|
|
690
|
-
numFmt?: string;
|
|
691
|
-
font?: unknown;
|
|
692
|
-
fill?: unknown;
|
|
693
|
-
border?: unknown;
|
|
694
|
-
alignment?: unknown;
|
|
695
|
-
};
|
|
696
|
-
// preserve unknown style features here (extLst etc)
|
|
697
|
-
preserved?: PreserveId[];
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
export interface WorkbookModel {
|
|
701
|
-
workbookId: WorkbookId;
|
|
702
|
-
// workbook-level settings that affect meaning
|
|
703
|
-
// (date system, calc mode, etc) should be explicit if you support editing them.
|
|
704
|
-
settings: Record<string, unknown>;
|
|
705
|
-
|
|
706
|
-
sheets: SheetModel[];
|
|
707
|
-
definedNames: Record<NameId, DefinedNameModel>;
|
|
708
|
-
styles: Record<StyleId, StyleModel>;
|
|
709
|
-
|
|
710
|
-
preservation: PreservationStore;
|
|
711
|
-
|
|
712
|
-
diagnostics: {
|
|
713
|
-
compatibility: {
|
|
714
|
-
blockExport: boolean;
|
|
715
|
-
featureEntries: CompatibilityFeatureEntry[];
|
|
716
|
-
};
|
|
717
|
-
warnings: Array<{ warningId: string; code: string; message: string; affectedRange?: RectRange }>;
|
|
718
|
-
errors: Array<{ errorId: string; code: string; message: string; isFatal: boolean }>;
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
Design notes tied to sources:
|
|
724
|
-
|
|
725
|
-
- Cell values and formulas mirror SpreadsheetML’s separation of `<f>` and cached `<v>` value.
|
|
726
|
-
- Shared strings vs inline strings must be preserved at IO boundaries; canonical may normalize but must not silently drop rich text.
|
|
727
|
-
- Merges are explicitly modeled because SpreadsheetML stores merges separately and content lives in top-left.
|
|
728
|
-
- Comments/notes and threaded comments are separated because Excel treats them as different features and the OOXML packaging uses different part families.
|
|
729
|
-
|
|
730
|
-
### Selection model sketch
|
|
731
|
-
|
|
732
|
-
```ts
|
|
733
|
-
export type SelectionMode = "cells" | "rows" | "cols" | "sheet";
|
|
734
|
-
|
|
735
|
-
export interface WorkbookSelection {
|
|
736
|
-
activeSheetId: SheetId;
|
|
737
|
-
|
|
738
|
-
// Always a single cell, snapped to merge top-left if within a merge.
|
|
739
|
-
activeCell: CellAddr;
|
|
740
|
-
|
|
741
|
-
// Primary selection rectangle in gridline coordinates (half-open).
|
|
742
|
-
primary: RectRange;
|
|
743
|
-
|
|
744
|
-
// Optional multi-range selection (Excel supports discontiguous selections).
|
|
745
|
-
// If not in v1: keep empty and block multi-select UI actions.
|
|
746
|
-
ranges?: RectRange[];
|
|
747
|
-
|
|
748
|
-
// Controls behavior of row/col header clicks, Ctrl+Space, Shift+Space, etc.
|
|
749
|
-
mode: SelectionMode;
|
|
750
|
-
|
|
751
|
-
// Anchor cell for keyboard extension, analogous to anchor/head in DOCX.
|
|
752
|
-
anchorCell: CellAddr;
|
|
753
|
-
}
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
### Commands + transactions (shape aligned to your DOCX runtime)
|
|
757
|
-
|
|
758
|
-
Your DOCX runtime uses a `dispatch(command)` that executes a command into a transaction and commits, with undo/redo via history stacks.
|
|
759
|
-
|
|
760
|
-
```ts
|
|
761
|
-
export interface CommandOrigin {
|
|
762
|
-
source: "keyboard" | "toolbar" | "context_menu" | "formula_bar" | "api" | "runtime";
|
|
763
|
-
timestamp: string;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
export type WorkbookCommand =
|
|
767
|
-
| { type: "selection.set"; selection: WorkbookSelection; origin?: CommandOrigin }
|
|
768
|
-
| { type: "cell.setValue"; sheetId: SheetId; addr: CellAddr; value: CellValue; origin?: CommandOrigin }
|
|
769
|
-
| { type: "cell.setFormula"; sheetId: SheetId; addr: CellAddr; formulaText: string; origin?: CommandOrigin }
|
|
770
|
-
| { type: "rows.insert"; sheetId: SheetId; atRow: Row; count: number; origin?: CommandOrigin }
|
|
771
|
-
| { type: "rows.delete"; sheetId: SheetId; fromRow: Row; count: number; origin?: CommandOrigin }
|
|
772
|
-
| { type: "cols.insert"; sheetId: SheetId; atCol: Col; count: number; origin?: CommandOrigin }
|
|
773
|
-
| { type: "cols.delete"; sheetId: SheetId; fromCol: Col; count: number; origin?: CommandOrigin }
|
|
774
|
-
| { type: "merge.apply"; sheetId: SheetId; range: RectRange; origin?: CommandOrigin }
|
|
775
|
-
| { type: "merge.remove"; sheetId: SheetId; mergeId: string; origin?: CommandOrigin }
|
|
776
|
-
| { type: "hyperlink.add"; sheetId: SheetId; range: RectRange; target: string; origin?: CommandOrigin }
|
|
777
|
-
| { type: "note.add"; sheetId: SheetId; cell: CellAddr; body: string; origin?: CommandOrigin }
|
|
778
|
-
| { type: "sheet.rename"; sheetId: SheetId; name: string; origin?: CommandOrigin }
|
|
779
|
-
| { type: "name.define"; name: string; scope: "workbook" | { sheetId: SheetId }; formula: string; origin?: CommandOrigin }
|
|
780
|
-
| { type: "runtime.setReadOnly"; readOnly: boolean; origin?: CommandOrigin }
|
|
781
|
-
| { type: "history.undo"; origin?: CommandOrigin }
|
|
782
|
-
| { type: "history.redo"; origin?: CommandOrigin };
|
|
783
|
-
|
|
784
|
-
export interface AxisMappingStep {
|
|
785
|
-
// Gridline coordinates, not cell coords.
|
|
786
|
-
from: number;
|
|
787
|
-
to: number;
|
|
788
|
-
insertSize: number;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
export interface WorkbookMapping {
|
|
792
|
-
sheets?: Record<SheetId, {
|
|
793
|
-
rowSteps?: AxisMappingStep[];
|
|
794
|
-
colSteps?: AxisMappingStep[];
|
|
795
|
-
// optional: rename info for formula string regeneration
|
|
796
|
-
renamedFrom?: string;
|
|
797
|
-
renamedTo?: string;
|
|
798
|
-
}>;
|
|
799
|
-
metadata?: {
|
|
800
|
-
invalidatesStructures?: boolean;
|
|
801
|
-
affectsFormulas?: boolean;
|
|
802
|
-
affectsNotes?: boolean;
|
|
803
|
-
affectsHyperlinks?: boolean;
|
|
804
|
-
affectsMerges?: boolean;
|
|
805
|
-
affectsPreserveOnlyRegions?: boolean;
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
export interface TransactionEffects {
|
|
810
|
-
warningsAdded: string[];
|
|
811
|
-
warningsCleared: string[];
|
|
812
|
-
exportBlockChanged?: boolean;
|
|
813
|
-
selectionChanged?: boolean;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
export interface WorkbookTransaction {
|
|
817
|
-
nextState: WorkbookModel;
|
|
818
|
-
mapping: WorkbookMapping;
|
|
819
|
-
effects: TransactionEffects;
|
|
820
|
-
historyBoundary: "push" | "skip";
|
|
821
|
-
markDirty: boolean;
|
|
822
|
-
}
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
The point here is structural: it is the same “command → transaction → commit” skeleton your DOCX editor uses, with a mapping object that exists to remap anchors reliably.
|
|
826
|
-
|
|
827
|
-
### Transaction examples
|
|
828
|
-
|
|
829
|
-
#### Example: insert one row at row 10
|
|
830
|
-
|
|
831
|
-
Intent: insert a row, shift everything below down, adjust references, remap merges/hyperlinks/notes, and update selection.
|
|
832
|
-
|
|
833
|
-
Key constraint: Excel states references automatically adjust when inserting rows/cells.
|
|
834
|
-
|
|
835
|
-
Transaction sketch (illustrative JSON):
|
|
836
|
-
|
|
837
|
-
```json
|
|
838
|
-
{
|
|
839
|
-
"command": { "type": "rows.insert", "sheetId": "S1", "atRow": 10, "count": 1 },
|
|
840
|
-
"mapping": {
|
|
841
|
-
"sheets": {
|
|
842
|
-
"S1": {
|
|
843
|
-
"rowSteps": [{ "from": 10, "to": 10, "insertSize": 1 }]
|
|
844
|
-
}
|
|
845
|
-
},
|
|
846
|
-
"metadata": {
|
|
847
|
-
"affectsFormulas": true,
|
|
848
|
-
"affectsNotes": true,
|
|
849
|
-
"affectsHyperlinks": true,
|
|
850
|
-
"affectsMerges": true
|
|
851
|
-
}
|
|
852
|
-
},
|
|
853
|
-
"historyBoundary": "push",
|
|
854
|
-
"markDirty": true
|
|
855
|
-
}
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
Remap consequences:
|
|
859
|
-
- All cell addresses with row ≥ 10 shift by +1 (sparse map key rewrite).
|
|
860
|
-
- Any formula ref token with row ≥ 10 shifts if that token is absolute to sheet coords; relative tokens are handled by copy/paste rules, not insert rules.
|
|
861
|
-
- Merges/hyperlinks anchored below shift their `row0/row1`.
|
|
862
|
-
- Notes/comments anchored to a cell below shift.
|
|
863
|
-
|
|
864
|
-
#### Example: merge A1:C1 with non-empty B1
|
|
865
|
-
|
|
866
|
-
Excel merge deletes non-top-left contents.
|
|
867
|
-
|
|
868
|
-
So `merge.apply` must produce either:
|
|
869
|
-
- `warn` requiring explicit user acknowledgement, or
|
|
870
|
-
- `block` if host policy forbids destructive operations.
|
|
871
|
-
|
|
872
|
-
Transaction outline:
|
|
873
|
-
|
|
874
|
-
```json
|
|
875
|
-
{
|
|
876
|
-
"command": { "type": "merge.apply", "sheetId": "S1", "range": "S1:[row0=1,row1=2,col0=1,col1=4]" },
|
|
877
|
-
"preflight": {
|
|
878
|
-
"nonTopLeftNonEmptyCells": ["B1"],
|
|
879
|
-
"response": "warn",
|
|
880
|
-
"message": "Merging will delete values in B1 and C1. Excel keeps only the top-left cell’s contents."
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
```
|
|
884
|
-
|
|
885
|
-
If user accepts:
|
|
886
|
-
- clear B1/C1 values in canonical state,
|
|
887
|
-
- add merge range,
|
|
888
|
-
- snap selection activeCell to A1 and primary range to merge.
|
|
889
|
-
|
|
890
|
-
This matches SpreadsheetML’s “top-left stores content” and Excel UI behavior.
|
|
891
|
-
|
|
892
|
-
### Release gates and fixture plan (minimum)
|
|
893
|
-
|
|
894
|
-
Your DOCX editor’s release gates include: fixture-based import/export tests, package integrity checks, Open XML SDK validation in CI/services, preserve-only survival checks, and host application reopen certification.
|
|
895
|
-
|
|
896
|
-
Spreadsheet v1 needs the same, adapted:
|
|
897
|
-
|
|
898
|
-
- **Fixture corpus** (frozen “gate fixtures”):
|
|
899
|
-
- F01: minimum workbook (1 sheet, blank) proof (structure + reopen).
|
|
900
|
-
- F02: shared strings + inline strings (including rich runs) preserve-only behavior.
|
|
901
|
-
- F03: formulas with cached results; ensure export preserves `<f>` + cached `<v>` and reopens.
|
|
902
|
-
- F04: insert row/col remaps references (validated by Excel reopen and correct formula updates).
|
|
903
|
-
- F05: merge with multi-values surfaces warn; merge export matches Excel semantics.
|
|
904
|
-
- F06: hyperlinks roundtrip (`hyperlink ref` + rel).
|
|
905
|
-
- F07: legacy notes preserved + moved under row insert (or explicitly blocked if not supported).
|
|
906
|
-
- F08: threaded comments present ⇒ preserve-only parts survive; destructive edits blocked.
|
|
907
|
-
- F09: defined names remap with sheet rename and row insert, or are preserved/blocked if formula parsing fails.
|
|
908
|
-
- F10: rich value parts present ⇒ preserve-only; verify survival.
|
|
909
|
-
|
|
910
|
-
- **Export certification gate**: Excel desktop reopen with no repair prompts and no silent supported-content loss for fixtures you classify as supported-roundtrip.
|
|
911
|
-
|
|
912
|
-
- **Preserve-only survival gate**: for preserve-only fixtures, verify byte-level retention (or semantically equivalent retention) of preserved parts/relationships + clear warnings surfaced to host.
|
|
913
|
-
|
|
914
|
-
## Sources
|
|
915
|
-
|
|
916
|
-
Your DOCX runtime architecture and compliance philosophy (primary internal reference):
|
|
917
|
-
|
|
918
|
-
- DOCX compliance taxonomy, package-first preservation rules, and the explicit preserve/lock/warn/block/fail response model.
|
|
919
|
-
- Public API posture: uncontrolled component, runtime-owned live session, persisted snapshot vs render snapshot separation.
|
|
920
|
-
- Frontend architecture: runtime emits render snapshots consumed via `useSyncExternalStore`; UI never holds canonical truth; mutations flow through runtime callbacks.
|
|
921
|
-
- Runtime + command pipeline: `executeEditorCommand` returns transaction with mapping/history boundary; runtime commits, maintains undo/redo, and remaps anchors through mapping.
|
|
922
|
-
- Mapping tests showing the intended semantics of mappings and remapping stored anchors through document replacement.
|
|
923
|
-
- Repo agent guidance emphasizing: OPC-first, canonical state not DOM, commands/transactions only, no silent dropping.
|
|
924
|
-
|
|
925
|
-
OOXML / SpreadsheetML structure and markup references:
|
|
926
|
-
|
|
927
|
-
- SpreadsheetML document structure, minimum workbook scenario, and example workbook.xml/workbook.xml.rels/worksheet.xml structure from Learn (Open XML SDK documentation).
|
|
928
|
-
- OPC packaging overview and requirements excerpted in Library of Congress format description (content types part, relationships parts, discoverability of parts).
|
|
929
|
-
- Cell value semantics (`<v>` as shared string index vs direct value; cached formula result stored in `<v>`; inline string `<is>` alternative).
|
|
930
|
-
- Cell value type enum mapping to XML `t` values (b/n/e/s/str/inlineStr/d).
|
|
931
|
-
- MergeCells semantics (“formatting and content always stored in top left cell”).
|
|
932
|
-
- Hyperlinks markup and relationship-based target linking.
|
|
933
|
-
- Comments markup: comments part per sheet, wired via sheet relationships; single comment has author and may contain rich formatted text.
|
|
934
|
-
- Defined names semantics and constraints (scope, formula/value use, ambiguity with cell references, A1–XFD1048576 rule).
|
|
935
|
-
|
|
936
|
-
Modern Excel extension part families (MS-XLSX, Microsoft Open Specifications):
|
|
937
|
-
|
|
938
|
-
- MS-XLSX introduction (normative sections statement) and part definitions including threaded comments, persons, named sheet views, rich value data, and related relationship requirements.
|
|
939
|
-
|
|
940
|
-
Observed Excel behavior (host correctness constraints):
|
|
941
|
-
|
|
942
|
-
- Excel row/column limits and inserted-row formatting behavior.
|
|
943
|
-
- Merge UI behavior deleting non-top-left values (destructive merge semantics).
|
|
944
|
-
- Relative/absolute/mixed references and copy-adjust behavior.
|
|
945
|
-
- R1C1 reference style explanation and examples.
|
|
946
|
-
- Insert cells/rows/cols shifts and automatic adjustment of cell references (Excel for Mac guidance).
|
|
947
|
-
- `#REF!` error caused by deleting referenced columns (Excel rewrites to `#REF!`).
|
|
948
|
-
- Structured references behavior and how table edits/renames adjust structured references.
|