@nguyenphp/antigravity-marketing 1.0.18 → 1.0.20
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 +130 -78
- package/package.json +4 -3
- package/templates/.agent/skills/marketing-report-expert/SKILL.md +70 -0
- package/templates/.agent/skills/minimax-docx/LICENSE +21 -0
- package/templates/.agent/skills/minimax-docx/SKILL.md +274 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/academic_styles.xml +250 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/corporate_styles.xml +284 -0
- package/templates/.agent/skills/minimax-docx/assets/styles/default_styles.xml +449 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/aesthetic-rules.xsd +470 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/business-rules.xsd +130 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/common-types.xsd +159 -0
- package/templates/.agent/skills/minimax-docx/assets/xsd/wml-subset.xsd +589 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_typography.md +357 -0
- package/templates/.agent/skills/minimax-docx/references/cjk_university_template_guide.md +184 -0
- package/templates/.agent/skills/minimax-docx/references/comments_guide.md +191 -0
- package/templates/.agent/skills/minimax-docx/references/design_good_bad_examples.md +829 -0
- package/templates/.agent/skills/minimax-docx/references/design_principles.md +819 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_element_order.md +308 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part1.md +4061 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part2.md +2820 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part3.md +3381 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_namespaces.md +82 -0
- package/templates/.agent/skills/minimax-docx/references/openxml_units.md +72 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_a_create.md +284 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_b_edit_content.md +295 -0
- package/templates/.agent/skills/minimax-docx/references/scenario_c_apply_template.md +456 -0
- package/templates/.agent/skills/minimax-docx/references/track_changes_guide.md +200 -0
- package/templates/.agent/skills/minimax-docx/references/troubleshooting.md +506 -0
- package/templates/.agent/skills/minimax-docx/references/typography_guide.md +294 -0
- package/templates/.agent/skills/minimax-docx/references/xsd_validation_guide.md +158 -0
- package/templates/.agent/skills/minimax-docx/scripts/doc_to_docx.sh +40 -0
- package/templates/.agent/skills/minimax-docx/scripts/docx_preview.sh +37 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj +19 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs +18 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs +147 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs +322 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs +324 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs +155 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs +487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs +108 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs +122 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs +107 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj +15 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs +169 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs +80 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs +42 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs +81 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs +99 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs +1121 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs +624 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs +838 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs +917 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs +826 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs +1487 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs +1163 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs +595 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs +39 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs +24 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs +20 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs +224 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs +148 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs +23 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs +69 -0
- package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx +4 -0
- package/templates/.agent/skills/minimax-docx/scripts/env_check.sh +196 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.ps1 +274 -0
- package/templates/.agent/skills/minimax-docx/scripts/setup.sh +504 -0
- package/templates/.agent/skills/minimax-multimodal-toolkit/SKILL.md +359 -0
- package/templates/.agent/skills/minimax-pdf/README.md +222 -0
- package/templates/.agent/skills/minimax-pdf/SKILL.md +201 -0
- package/templates/.agent/skills/minimax-pdf/design/design.md +381 -0
- package/templates/.agent/skills/minimax-pdf/scripts/cover.py +1579 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_inspect.py +200 -0
- package/templates/.agent/skills/minimax-pdf/scripts/fill_write.py +242 -0
- package/templates/.agent/skills/minimax-pdf/scripts/make.sh +491 -0
- package/templates/.agent/skills/minimax-pdf/scripts/merge.py +112 -0
- package/templates/.agent/skills/minimax-pdf/scripts/palette.py +559 -0
- package/templates/.agent/skills/minimax-pdf/scripts/reformat_parse.py +374 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_body.py +1055 -0
- package/templates/.agent/skills/minimax-pdf/scripts/render_cover.cjs +111 -0
- package/templates/.agent/skills/minimax-xlsx/SKILL.md +138 -0
- package/templates/.agent/skills/minimax-xlsx/references/create.md +691 -0
- package/templates/.agent/skills/minimax-xlsx/references/edit.md +684 -0
- package/templates/.agent/skills/minimax-xlsx/references/fix.md +37 -0
- package/templates/.agent/skills/minimax-xlsx/references/format.md +768 -0
- package/templates/.agent/skills/minimax-xlsx/references/ooxml-cheatsheet.md +231 -0
- package/templates/.agent/skills/minimax-xlsx/references/read-analyze.md +97 -0
- package/templates/.agent/skills/minimax-xlsx/references/validate.md +772 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/formula_check.py +422 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/libreoffice_recalc.py +248 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/shared_strings_builder.py +163 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/style_audit.py +575 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_add_column.py +395 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_insert_row.py +274 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_pack.py +87 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_reader.py +362 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_shift_rows.py +396 -0
- package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_unpack.py +130 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
- package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
- package/templates/.agent/skills/pptx-generator/SKILL.md +249 -0
- package/templates/.agent/skills/pptx-generator/references/design-system.md +392 -0
- package/templates/.agent/skills/pptx-generator/references/editing.md +162 -0
- package/templates/.agent/skills/pptx-generator/references/pitfalls.md +112 -0
- package/templates/.agent/skills/pptx-generator/references/pptxgenjs.md +420 -0
- package/templates/.agent/skills/pptx-generator/references/slide-types.md +413 -0
- package/templates/.agent/skills/tutorial-video-expert/SKILL.md +88 -0
- package/templates/.agent/skills/ui-ux-pro-max/SKILL.md +170 -585
- package/templates/.agent/skills/vision-analysis/SKILL.md +174 -0
- package/templates/.agent/workflows/analyze.md +3 -0
- package/templates/.agent/workflows/brand-report.md +44 -0
- package/templates/.agent/workflows/report.md +49 -0
- package/templates/.agent/agents/backend-specialist.md +0 -263
- package/templates/.agent/agents/database-architect.md +0 -226
- package/templates/.agent/agents/debugger.md +0 -225
- package/templates/.agent/agents/devops-engineer.md +0 -242
- package/templates/.agent/agents/frontend-specialist.md +0 -527
- package/templates/.agent/agents/game-developer.md +0 -162
- package/templates/.agent/agents/mobile-developer.md +0 -377
- package/templates/.agent/agents/penetration-tester.md +0 -188
- package/templates/.agent/agents/security-auditor.md +0 -170
- package/templates/.agent/agents/test-engineer.md +0 -158
- package/templates/.agent/skills/api-patterns/SKILL.md +0 -81
- package/templates/.agent/skills/api-patterns/api-style.md +0 -42
- package/templates/.agent/skills/api-patterns/auth.md +0 -24
- package/templates/.agent/skills/api-patterns/documentation.md +0 -26
- package/templates/.agent/skills/api-patterns/graphql.md +0 -41
- package/templates/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/templates/.agent/skills/api-patterns/response.md +0 -37
- package/templates/.agent/skills/api-patterns/rest.md +0 -40
- package/templates/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
- package/templates/.agent/skills/api-patterns/security-testing.md +0 -122
- package/templates/.agent/skills/api-patterns/trpc.md +0 -41
- package/templates/.agent/skills/api-patterns/versioning.md +0 -22
- package/templates/.agent/skills/app-builder/SKILL.md +0 -75
- package/templates/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/templates/.agent/skills/app-builder/feature-building.md +0 -53
- package/templates/.agent/skills/app-builder/project-detection.md +0 -34
- package/templates/.agent/skills/app-builder/scaffolding.md +0 -118
- package/templates/.agent/skills/app-builder/tech-stack.md +0 -40
- package/templates/.agent/skills/app-builder/templates/SKILL.md +0 -39
- package/templates/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
- package/templates/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
- package/templates/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
- package/templates/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
- package/templates/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
- package/templates/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
- package/templates/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
- package/templates/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -82
- package/templates/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -100
- package/templates/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -106
- package/templates/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -101
- package/templates/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
- package/templates/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -93
- package/templates/.agent/skills/architecture/SKILL.md +0 -55
- package/templates/.agent/skills/architecture/context-discovery.md +0 -43
- package/templates/.agent/skills/architecture/examples.md +0 -94
- package/templates/.agent/skills/architecture/pattern-selection.md +0 -68
- package/templates/.agent/skills/architecture/patterns-reference.md +0 -50
- package/templates/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/templates/.agent/skills/bash-linux/SKILL.md +0 -199
- package/templates/.agent/skills/behavioral-modes/SKILL.md +0 -242
- package/templates/.agent/skills/clean-code/SKILL.md +0 -201
- package/templates/.agent/skills/code-review-checklist/SKILL.md +0 -109
- package/templates/.agent/skills/database-design/SKILL.md +0 -52
- package/templates/.agent/skills/database-design/database-selection.md +0 -43
- package/templates/.agent/skills/database-design/indexing.md +0 -39
- package/templates/.agent/skills/database-design/migrations.md +0 -48
- package/templates/.agent/skills/database-design/optimization.md +0 -36
- package/templates/.agent/skills/database-design/orm-selection.md +0 -30
- package/templates/.agent/skills/database-design/schema-design.md +0 -56
- package/templates/.agent/skills/database-design/scripts/schema_validator.py +0 -172
- package/templates/.agent/skills/deployment-procedures/SKILL.md +0 -241
- package/templates/.agent/skills/docker-expert/SKILL.md +0 -409
- package/templates/.agent/skills/game-development/2d-games/SKILL.md +0 -119
- package/templates/.agent/skills/game-development/3d-games/SKILL.md +0 -135
- package/templates/.agent/skills/game-development/SKILL.md +0 -167
- package/templates/.agent/skills/game-development/game-art/SKILL.md +0 -185
- package/templates/.agent/skills/game-development/game-audio/SKILL.md +0 -190
- package/templates/.agent/skills/game-development/game-design/SKILL.md +0 -129
- package/templates/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
- package/templates/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
- package/templates/.agent/skills/game-development/pc-games/SKILL.md +0 -144
- package/templates/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
- package/templates/.agent/skills/game-development/web-games/SKILL.md +0 -150
- package/templates/.agent/skills/lint-and-validate/SKILL.md +0 -45
- package/templates/.agent/skills/lint-and-validate/scripts/lint_runner.py +0 -172
- package/templates/.agent/skills/lint-and-validate/scripts/type_coverage.py +0 -173
- package/templates/.agent/skills/mcp-builder/SKILL.md +0 -176
- package/templates/.agent/skills/nestjs-expert/SKILL.md +0 -552
- package/templates/.agent/skills/nextjs-best-practices/SKILL.md +0 -203
- package/templates/.agent/skills/nodejs-best-practices/SKILL.md +0 -333
- package/templates/.agent/skills/parallel-agents/SKILL.md +0 -175
- package/templates/.agent/skills/performance-profiling/SKILL.md +0 -143
- package/templates/.agent/skills/performance-profiling/scripts/lighthouse_audit.py +0 -76
- package/templates/.agent/skills/powershell-windows/SKILL.md +0 -167
- package/templates/.agent/skills/prisma-expert/SKILL.md +0 -355
- package/templates/.agent/skills/python-patterns/SKILL.md +0 -441
- package/templates/.agent/skills/react-patterns/SKILL.md +0 -198
- package/templates/.agent/skills/red-team-tactics/SKILL.md +0 -199
- package/templates/.agent/skills/server-management/SKILL.md +0 -161
- package/templates/.agent/skills/systematic-debugging/SKILL.md +0 -109
- package/templates/.agent/skills/tdd-workflow/SKILL.md +0 -149
- package/templates/.agent/skills/testing-patterns/SKILL.md +0 -178
- package/templates/.agent/skills/testing-patterns/scripts/test_runner.py +0 -219
- package/templates/.agent/skills/typescript-expert/SKILL.md +0 -429
- package/templates/.agent/skills/vue-expert/SKILL.md +0 -374
- package/templates/.agent/skills/vulnerability-scanner/SKILL.md +0 -276
- package/templates/.agent/skills/vulnerability-scanner/checklists.md +0 -121
- package/templates/.agent/skills/vulnerability-scanner/scripts/security_scan.py +0 -458
- package/templates/.agent/skills/webapp-testing/SKILL.md +0 -187
- package/templates/.agent/skills/webapp-testing/scripts/playwright_runner.py +0 -173
- package/templates/.agent/workflows/debug.md +0 -103
- package/templates/.agent/workflows/deploy.md +0 -176
- package/templates/.agent/workflows/enhance.md +0 -63
- package/templates/.agent/workflows/test.md +0 -144
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
using DocumentFormat.OpenXml;
|
|
2
|
+
using DocumentFormat.OpenXml.Packaging;
|
|
3
|
+
using DocumentFormat.OpenXml.Wordprocessing;
|
|
4
|
+
|
|
5
|
+
namespace MiniMaxAIDocx.Core.Samples;
|
|
6
|
+
|
|
7
|
+
/// <summary>
|
|
8
|
+
/// Reference implementations for revision tracking (Track Changes).
|
|
9
|
+
///
|
|
10
|
+
/// ╔══════════════════════════════════════════════════════════════════╗
|
|
11
|
+
/// ║ CRITICAL: w:del uses w:delText, NEVER w:t ║
|
|
12
|
+
/// ║ w:ins uses w:t, NEVER w:delText ║
|
|
13
|
+
/// ║ Getting this wrong silently corrupts the document. ║
|
|
14
|
+
/// ║ Word will open without error but display garbled text or ║
|
|
15
|
+
/// ║ lose content when accepting/rejecting changes. ║
|
|
16
|
+
/// ╚══════════════════════════════════════════════════════════════════╝
|
|
17
|
+
///
|
|
18
|
+
/// KEY CONCEPTS:
|
|
19
|
+
/// - Every revision element (ins, del, rPrChange, pPrChange) needs:
|
|
20
|
+
/// w:id — unique revision ID (string, must be unique across all revisions)
|
|
21
|
+
/// w:author — who made the change
|
|
22
|
+
/// w:date — ISO 8601 timestamp
|
|
23
|
+
/// - InsertedRun (w:ins) wraps normal Run elements with w:t text
|
|
24
|
+
/// - DeletedRun (w:del) wraps Run elements that use DeletedText (w:delText) instead of Text (w:t)
|
|
25
|
+
/// - MoveFrom/MoveTo track text that was moved (not just deleted+inserted)
|
|
26
|
+
/// </summary>
|
|
27
|
+
public static class TrackChangesSamples
|
|
28
|
+
{
|
|
29
|
+
/// <summary>
|
|
30
|
+
/// Thread-safe counter for generating unique revision IDs.
|
|
31
|
+
/// In production, scan the document for the max existing ID first.
|
|
32
|
+
/// </summary>
|
|
33
|
+
private static int s_revisionCounter;
|
|
34
|
+
|
|
35
|
+
// ──────────────────────────────────────────────
|
|
36
|
+
// 1. EnableTrackChanges
|
|
37
|
+
// ──────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/// <summary>
|
|
40
|
+
/// Enables revision tracking in the document settings.
|
|
41
|
+
/// This makes Word record all subsequent edits as tracked changes.
|
|
42
|
+
///
|
|
43
|
+
/// Maps to: <w:trackChanges/> in settings.xml
|
|
44
|
+
///
|
|
45
|
+
/// Note: This only controls whether NEW edits are tracked.
|
|
46
|
+
/// Existing revision marks are always preserved regardless of this setting.
|
|
47
|
+
/// </summary>
|
|
48
|
+
public static void EnableTrackChanges(DocumentSettingsPart settingsPart)
|
|
49
|
+
{
|
|
50
|
+
settingsPart.Settings ??= new Settings();
|
|
51
|
+
|
|
52
|
+
var existing = settingsPart.Settings.GetFirstChild<TrackRevisions>();
|
|
53
|
+
if (existing == null)
|
|
54
|
+
{
|
|
55
|
+
settingsPart.Settings.Append(new TrackRevisions());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
settingsPart.Settings.Save();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ──────────────────────────────────────────────
|
|
62
|
+
// 2. InsertTrackedInsertion — w:ins with w:t
|
|
63
|
+
// ──────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/// <summary>
|
|
66
|
+
/// Inserts text as a tracked insertion (w:ins).
|
|
67
|
+
///
|
|
68
|
+
/// ╔══════════════════════════════════════════════════════╗
|
|
69
|
+
/// ║ w:ins uses w:t (Text), NOT w:delText. ║
|
|
70
|
+
/// ║ The text appears with green underline in Word. ║
|
|
71
|
+
/// ╚══════════════════════════════════════════════════════╝
|
|
72
|
+
///
|
|
73
|
+
/// XML structure:
|
|
74
|
+
/// <w:ins w:id="1" w:author="John" w:date="2026-03-22T00:00:00Z">
|
|
75
|
+
/// <w:r>
|
|
76
|
+
/// <w:t>inserted text</w:t> <!-- w:t, NOT w:delText -->
|
|
77
|
+
/// </w:r>
|
|
78
|
+
/// </w:ins>
|
|
79
|
+
/// </summary>
|
|
80
|
+
public static InsertedRun InsertTrackedInsertion(Paragraph para, string text, string author)
|
|
81
|
+
{
|
|
82
|
+
var ins = new InsertedRun
|
|
83
|
+
{
|
|
84
|
+
Id = GenerateRevisionId(),
|
|
85
|
+
Author = author,
|
|
86
|
+
Date = DateTime.UtcNow
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// CORRECT: w:ins contains w:r with w:t (normal Text element)
|
|
90
|
+
ins.Append(new Run(
|
|
91
|
+
new Text(text) { Space = SpaceProcessingModeValues.Preserve }));
|
|
92
|
+
|
|
93
|
+
para.Append(ins);
|
|
94
|
+
return ins;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ──────────────────────────────────────────────
|
|
98
|
+
// 3. InsertTrackedDeletion — w:del with w:delText
|
|
99
|
+
// ──────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/// <summary>
|
|
102
|
+
/// Inserts text as a tracked deletion (w:del).
|
|
103
|
+
///
|
|
104
|
+
/// ╔══════════════════════════════════════════════════════╗
|
|
105
|
+
/// ║ w:del uses w:delText (DeletedText), NOT w:t. ║
|
|
106
|
+
/// ║ Using w:t inside w:del SILENTLY CORRUPTS the file. ║
|
|
107
|
+
/// ║ The text appears with red strikethrough in Word. ║
|
|
108
|
+
/// ╚══════════════════════════════════════════════════════╝
|
|
109
|
+
///
|
|
110
|
+
/// XML structure:
|
|
111
|
+
/// <w:del w:id="2" w:author="John" w:date="2026-03-22T00:00:00Z">
|
|
112
|
+
/// <w:r>
|
|
113
|
+
/// <w:delText xml:space="preserve">deleted text</w:delText> <!-- w:delText, NOT w:t -->
|
|
114
|
+
/// </w:r>
|
|
115
|
+
/// </w:del>
|
|
116
|
+
/// </summary>
|
|
117
|
+
public static DeletedRun InsertTrackedDeletion(Paragraph para, string deletedText, string author)
|
|
118
|
+
{
|
|
119
|
+
var del = new DeletedRun
|
|
120
|
+
{
|
|
121
|
+
Id = GenerateRevisionId(),
|
|
122
|
+
Author = author,
|
|
123
|
+
Date = DateTime.UtcNow
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// CORRECT: w:del contains w:r with w:delText (DeletedText element)
|
|
127
|
+
// WRONG would be: new Text(deletedText) — this creates w:t which corrupts the document
|
|
128
|
+
del.Append(new Run(
|
|
129
|
+
new DeletedText(deletedText) { Space = SpaceProcessingModeValues.Preserve }));
|
|
130
|
+
|
|
131
|
+
para.Append(del);
|
|
132
|
+
return del;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ──────────────────────────────────────────────
|
|
136
|
+
// 4. InsertFormattingChange — RunPropertiesChange
|
|
137
|
+
// ──────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
/// <summary>
|
|
140
|
+
/// Records a formatting change on a run (e.g., text was made bold).
|
|
141
|
+
///
|
|
142
|
+
/// RunPropertiesChange (w:rPrChange) stores the PREVIOUS formatting.
|
|
143
|
+
/// The current RunProperties on the run reflects the NEW formatting.
|
|
144
|
+
///
|
|
145
|
+
/// Example: text changed from normal to bold:
|
|
146
|
+
/// <w:rPr>
|
|
147
|
+
/// <w:b/> <!-- current: bold -->
|
|
148
|
+
/// <w:rPrChange w:id="3" w:author="John" w:date="...">
|
|
149
|
+
/// <w:rPr/> <!-- previous: no bold -->
|
|
150
|
+
/// </w:rPrChange>
|
|
151
|
+
/// </w:rPr>
|
|
152
|
+
/// </summary>
|
|
153
|
+
public static void InsertFormattingChange(Run run, string author)
|
|
154
|
+
{
|
|
155
|
+
// Ensure RunProperties exists
|
|
156
|
+
run.RunProperties ??= new RunProperties();
|
|
157
|
+
|
|
158
|
+
// Store the previous (empty/normal) formatting as the "before" state
|
|
159
|
+
var rPrChange = new RunPropertiesChange
|
|
160
|
+
{
|
|
161
|
+
Id = GenerateRevisionId(),
|
|
162
|
+
Author = author,
|
|
163
|
+
Date = DateTime.UtcNow
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// The child RunProperties inside rPrChange is the OLD formatting (before the change).
|
|
167
|
+
// An empty RunProperties means "was default/normal formatting."
|
|
168
|
+
rPrChange.Append(new PreviousRunProperties());
|
|
169
|
+
|
|
170
|
+
run.RunProperties.Append(rPrChange);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ──────────────────────────────────────────────
|
|
174
|
+
// 5. InsertParagraphFormatChange — ParagraphPropertiesChange
|
|
175
|
+
// ──────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
/// <summary>
|
|
178
|
+
/// Records a paragraph formatting change (e.g., alignment changed).
|
|
179
|
+
///
|
|
180
|
+
/// ParagraphPropertiesChange (w:pPrChange) stores the PREVIOUS paragraph properties.
|
|
181
|
+
/// The current ParagraphProperties reflects the NEW formatting.
|
|
182
|
+
///
|
|
183
|
+
/// Example: paragraph changed from left-aligned to centered:
|
|
184
|
+
/// <w:pPr>
|
|
185
|
+
/// <w:jc w:val="center"/> <!-- current: centered -->
|
|
186
|
+
/// <w:pPrChange w:id="4" w:author="John" w:date="...">
|
|
187
|
+
/// <w:pPr>
|
|
188
|
+
/// <w:jc w:val="left"/> <!-- previous: left -->
|
|
189
|
+
/// </w:pPr>
|
|
190
|
+
/// </w:pPrChange>
|
|
191
|
+
/// </w:pPr>
|
|
192
|
+
/// </summary>
|
|
193
|
+
public static void InsertParagraphFormatChange(Paragraph para, string author)
|
|
194
|
+
{
|
|
195
|
+
para.ParagraphProperties ??= new ParagraphProperties();
|
|
196
|
+
|
|
197
|
+
var pPrChange = new ParagraphPropertiesChange
|
|
198
|
+
{
|
|
199
|
+
Id = GenerateRevisionId(),
|
|
200
|
+
Author = author,
|
|
201
|
+
Date = DateTime.UtcNow
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Store previous paragraph properties (before the change)
|
|
205
|
+
// Example: was left-aligned before changing to whatever the current alignment is
|
|
206
|
+
var previousPPr = new ParagraphPropertiesExtended();
|
|
207
|
+
previousPPr.Append(new Justification { Val = JustificationValues.Left });
|
|
208
|
+
pPrChange.Append(previousPPr);
|
|
209
|
+
|
|
210
|
+
para.ParagraphProperties.Append(pPrChange);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ──────────────────────────────────────────────
|
|
214
|
+
// 6. InsertTableRowInsertion — table revision marks
|
|
215
|
+
// ──────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
/// <summary>
|
|
218
|
+
/// Marks a table row as a tracked insertion.
|
|
219
|
+
///
|
|
220
|
+
/// Table-level track changes use TableRowProperties with InsertedMathControl
|
|
221
|
+
/// mapped from w:trPr/w:ins — indicating the entire row was inserted.
|
|
222
|
+
///
|
|
223
|
+
/// Structure:
|
|
224
|
+
/// <w:tr>
|
|
225
|
+
/// <w:trPr>
|
|
226
|
+
/// <w:ins w:id="5" w:author="John" w:date="..."/>
|
|
227
|
+
/// </w:trPr>
|
|
228
|
+
/// <w:tc>...</w:tc>
|
|
229
|
+
/// </w:tr>
|
|
230
|
+
/// </summary>
|
|
231
|
+
public static void InsertTableRowInsertion(TableRow row, string author)
|
|
232
|
+
{
|
|
233
|
+
row.TableRowProperties ??= new TableRowProperties();
|
|
234
|
+
|
|
235
|
+
var inserted = new Inserted
|
|
236
|
+
{
|
|
237
|
+
Id = GenerateRevisionId(),
|
|
238
|
+
Author = author,
|
|
239
|
+
Date = DateTime.UtcNow
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
row.TableRowProperties.Append(inserted);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ──────────────────────────────────────────────
|
|
246
|
+
// 7. AcceptAllRevisions — accept all tracked changes
|
|
247
|
+
// ──────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
/// <summary>
|
|
250
|
+
/// Programmatically accepts all tracked changes in the document body.
|
|
251
|
+
///
|
|
252
|
+
/// For insertions (w:ins): unwrap the content (keep the runs, remove the w:ins wrapper)
|
|
253
|
+
/// For deletions (w:del): remove the entire element (the deleted text disappears)
|
|
254
|
+
/// For formatting changes: remove the rPrChange/pPrChange (keep new formatting)
|
|
255
|
+
/// For table row insertions: remove the w:ins from trPr
|
|
256
|
+
///
|
|
257
|
+
/// ╔══════════════════════════════════════════════════════════════╗
|
|
258
|
+
/// ║ Process deletions before insertions to avoid invalidating ║
|
|
259
|
+
/// ║ element references. Always call .ToList() before ║
|
|
260
|
+
/// ║ iterating to avoid modifying the collection during ║
|
|
261
|
+
/// ║ enumeration. ║
|
|
262
|
+
/// ╚══════════════════════════════════════════════════════════════╝
|
|
263
|
+
/// </summary>
|
|
264
|
+
public static void AcceptAllRevisions(Body body)
|
|
265
|
+
{
|
|
266
|
+
// 1. Accept deletions — remove the w:del and all its content
|
|
267
|
+
foreach (var del in body.Descendants<DeletedRun>().ToList())
|
|
268
|
+
{
|
|
269
|
+
del.Remove();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 2. Accept insertions — unwrap w:ins, keeping child runs in place
|
|
273
|
+
foreach (var ins in body.Descendants<InsertedRun>().ToList())
|
|
274
|
+
{
|
|
275
|
+
var parent = ins.Parent;
|
|
276
|
+
if (parent == null) continue;
|
|
277
|
+
|
|
278
|
+
// Move all child elements before the ins element, then remove ins
|
|
279
|
+
var children = ins.ChildElements.ToList();
|
|
280
|
+
foreach (var child in children)
|
|
281
|
+
{
|
|
282
|
+
child.Remove();
|
|
283
|
+
ins.InsertBeforeSelf(child);
|
|
284
|
+
}
|
|
285
|
+
ins.Remove();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 3. Accept formatting changes — remove rPrChange (keep new formatting)
|
|
289
|
+
foreach (var rPrChange in body.Descendants<RunPropertiesChange>().ToList())
|
|
290
|
+
{
|
|
291
|
+
rPrChange.Remove();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 4. Accept paragraph formatting changes
|
|
295
|
+
foreach (var pPrChange in body.Descendants<ParagraphPropertiesChange>().ToList())
|
|
296
|
+
{
|
|
297
|
+
pPrChange.Remove();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 5. Accept table row insertions — remove w:ins from trPr
|
|
301
|
+
foreach (var inserted in body.Descendants<TableRowProperties>()
|
|
302
|
+
.SelectMany(trPr => trPr.Elements<Inserted>()).ToList())
|
|
303
|
+
{
|
|
304
|
+
inserted.Remove();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 6. Accept MoveFrom/MoveTo — keep MoveTo content, remove MoveFrom
|
|
308
|
+
foreach (var moveFrom in body.Descendants<MoveFromRun>().ToList())
|
|
309
|
+
{
|
|
310
|
+
moveFrom.Remove();
|
|
311
|
+
}
|
|
312
|
+
foreach (var moveTo in body.Descendants<MoveToRun>().ToList())
|
|
313
|
+
{
|
|
314
|
+
var parent = moveTo.Parent;
|
|
315
|
+
if (parent == null) continue;
|
|
316
|
+
var children = moveTo.ChildElements.ToList();
|
|
317
|
+
foreach (var child in children)
|
|
318
|
+
{
|
|
319
|
+
child.Remove();
|
|
320
|
+
moveTo.InsertBeforeSelf(child);
|
|
321
|
+
}
|
|
322
|
+
moveTo.Remove();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 7. Remove move range markers
|
|
326
|
+
foreach (var marker in body.Descendants<MoveFromRangeStart>().ToList()) marker.Remove();
|
|
327
|
+
foreach (var marker in body.Descendants<MoveFromRangeEnd>().ToList()) marker.Remove();
|
|
328
|
+
foreach (var marker in body.Descendants<MoveToRangeStart>().ToList()) marker.Remove();
|
|
329
|
+
foreach (var marker in body.Descendants<MoveToRangeEnd>().ToList()) marker.Remove();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ──────────────────────────────────────────────
|
|
333
|
+
// 8. RejectAllRevisions — reject all tracked changes
|
|
334
|
+
// ──────────────────────────────────────────────
|
|
335
|
+
|
|
336
|
+
/// <summary>
|
|
337
|
+
/// Programmatically rejects all tracked changes in the document body.
|
|
338
|
+
///
|
|
339
|
+
/// For insertions (w:ins): remove the entire element (the inserted text disappears)
|
|
340
|
+
/// For deletions (w:del): unwrap the content and convert w:delText back to w:t
|
|
341
|
+
/// (the "deleted" text is restored)
|
|
342
|
+
/// For formatting changes: restore old formatting from rPrChange/pPrChange
|
|
343
|
+
///
|
|
344
|
+
/// ╔══════════════════════════════════════════════════════════════╗
|
|
345
|
+
/// ║ When rejecting deletions, you MUST convert w:delText back ║
|
|
346
|
+
/// ║ to w:t. Leaving w:delText in a non-deleted run causes ║
|
|
347
|
+
/// ║ the text to be invisible in Word. ║
|
|
348
|
+
/// ╚══════════════════════════════════════════════════════════════╝
|
|
349
|
+
/// </summary>
|
|
350
|
+
public static void RejectAllRevisions(Body body)
|
|
351
|
+
{
|
|
352
|
+
// 1. Reject insertions — remove the entire w:ins and its content
|
|
353
|
+
foreach (var ins in body.Descendants<InsertedRun>().ToList())
|
|
354
|
+
{
|
|
355
|
+
ins.Remove();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 2. Reject deletions — restore deleted text by unwrapping w:del
|
|
359
|
+
// and converting w:delText back to w:t
|
|
360
|
+
foreach (var del in body.Descendants<DeletedRun>().ToList())
|
|
361
|
+
{
|
|
362
|
+
var parent = del.Parent;
|
|
363
|
+
if (parent == null) continue;
|
|
364
|
+
|
|
365
|
+
// Convert DeletedText -> Text in each run inside the deletion
|
|
366
|
+
foreach (var run in del.Elements<Run>().ToList())
|
|
367
|
+
{
|
|
368
|
+
foreach (var delText in run.Elements<DeletedText>().ToList())
|
|
369
|
+
{
|
|
370
|
+
// IMPORTANT: convert w:delText back to w:t
|
|
371
|
+
var text = new Text(delText.Text ?? "") { Space = SpaceProcessingModeValues.Preserve };
|
|
372
|
+
delText.InsertAfterSelf(text);
|
|
373
|
+
delText.Remove();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Unwrap — move children before the del element
|
|
378
|
+
var children = del.ChildElements.ToList();
|
|
379
|
+
foreach (var child in children)
|
|
380
|
+
{
|
|
381
|
+
child.Remove();
|
|
382
|
+
del.InsertBeforeSelf(child);
|
|
383
|
+
}
|
|
384
|
+
del.Remove();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// 3. Reject formatting changes — restore old RunProperties
|
|
388
|
+
foreach (var rPrChange in body.Descendants<RunPropertiesChange>().ToList())
|
|
389
|
+
{
|
|
390
|
+
var runProperties = rPrChange.Parent as RunProperties;
|
|
391
|
+
if (runProperties == null) continue;
|
|
392
|
+
|
|
393
|
+
// Get the previous (old) formatting
|
|
394
|
+
var previousRPr = rPrChange.GetFirstChild<PreviousRunProperties>();
|
|
395
|
+
if (previousRPr != null)
|
|
396
|
+
{
|
|
397
|
+
// Remove current formatting (except the rPrChange itself)
|
|
398
|
+
var currentProps = runProperties.ChildElements
|
|
399
|
+
.Where(c => c is not RunPropertiesChange).ToList();
|
|
400
|
+
foreach (var prop in currentProps)
|
|
401
|
+
{
|
|
402
|
+
prop.Remove();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Restore old formatting from PreviousRunProperties
|
|
406
|
+
foreach (var oldProp in previousRPr.ChildElements.ToList())
|
|
407
|
+
{
|
|
408
|
+
oldProp.Remove();
|
|
409
|
+
runProperties.Append(oldProp);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
rPrChange.Remove();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// 4. Reject paragraph formatting changes — restore old ParagraphProperties
|
|
416
|
+
foreach (var pPrChange in body.Descendants<ParagraphPropertiesChange>().ToList())
|
|
417
|
+
{
|
|
418
|
+
var paragraphProperties = pPrChange.Parent as ParagraphProperties;
|
|
419
|
+
if (paragraphProperties == null) continue;
|
|
420
|
+
|
|
421
|
+
var previousPPr = pPrChange.GetFirstChild<ParagraphPropertiesExtended>();
|
|
422
|
+
if (previousPPr != null)
|
|
423
|
+
{
|
|
424
|
+
var currentProps = paragraphProperties.ChildElements
|
|
425
|
+
.Where(c => c is not ParagraphPropertiesChange).ToList();
|
|
426
|
+
foreach (var prop in currentProps)
|
|
427
|
+
{
|
|
428
|
+
prop.Remove();
|
|
429
|
+
}
|
|
430
|
+
foreach (var oldProp in previousPPr.ChildElements.ToList())
|
|
431
|
+
{
|
|
432
|
+
oldProp.Remove();
|
|
433
|
+
paragraphProperties.Append(oldProp);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
pPrChange.Remove();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// 5. Reject table row insertions — remove the entire row
|
|
440
|
+
foreach (var row in body.Descendants<TableRow>().ToList())
|
|
441
|
+
{
|
|
442
|
+
var trPr = row.TableRowProperties;
|
|
443
|
+
if (trPr?.GetFirstChild<Inserted>() != null)
|
|
444
|
+
{
|
|
445
|
+
row.Remove();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 6. Reject MoveFrom/MoveTo — keep MoveFrom content (original position), remove MoveTo
|
|
450
|
+
foreach (var moveTo in body.Descendants<MoveToRun>().ToList())
|
|
451
|
+
{
|
|
452
|
+
moveTo.Remove();
|
|
453
|
+
}
|
|
454
|
+
foreach (var moveFrom in body.Descendants<MoveFromRun>().ToList())
|
|
455
|
+
{
|
|
456
|
+
var parent = moveFrom.Parent;
|
|
457
|
+
if (parent == null) continue;
|
|
458
|
+
|
|
459
|
+
// Convert any DeletedText back to Text in MoveFrom runs
|
|
460
|
+
foreach (var run in moveFrom.Elements<Run>().ToList())
|
|
461
|
+
{
|
|
462
|
+
foreach (var delText in run.Elements<DeletedText>().ToList())
|
|
463
|
+
{
|
|
464
|
+
var text = new Text(delText.Text ?? "") { Space = SpaceProcessingModeValues.Preserve };
|
|
465
|
+
delText.InsertAfterSelf(text);
|
|
466
|
+
delText.Remove();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
var children = moveFrom.ChildElements.ToList();
|
|
471
|
+
foreach (var child in children)
|
|
472
|
+
{
|
|
473
|
+
child.Remove();
|
|
474
|
+
moveFrom.InsertBeforeSelf(child);
|
|
475
|
+
}
|
|
476
|
+
moveFrom.Remove();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 7. Remove move range markers
|
|
480
|
+
foreach (var marker in body.Descendants<MoveFromRangeStart>().ToList()) marker.Remove();
|
|
481
|
+
foreach (var marker in body.Descendants<MoveFromRangeEnd>().ToList()) marker.Remove();
|
|
482
|
+
foreach (var marker in body.Descendants<MoveToRangeStart>().ToList()) marker.Remove();
|
|
483
|
+
foreach (var marker in body.Descendants<MoveToRangeEnd>().ToList()) marker.Remove();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ──────────────────────────────────────────────
|
|
487
|
+
// 9. InsertMoveFromTo — MoveFrom + MoveTo blocks
|
|
488
|
+
// ──────────────────────────────────────────────
|
|
489
|
+
|
|
490
|
+
/// <summary>
|
|
491
|
+
/// Creates a tracked move operation (text moved from one location to another).
|
|
492
|
+
///
|
|
493
|
+
/// A move consists of:
|
|
494
|
+
/// - MoveFromRangeStart/End markers around the original location
|
|
495
|
+
/// - MoveFrom (w:moveFrom) containing the original text with w:delText
|
|
496
|
+
/// - MoveToRangeStart/End markers around the new location
|
|
497
|
+
/// - MoveTo (w:moveTo) containing the moved text with w:t
|
|
498
|
+
/// - Both share the same name attribute to link them
|
|
499
|
+
///
|
|
500
|
+
/// ╔══════════════════════════════════════════════════════════════╗
|
|
501
|
+
/// ║ MoveFrom uses w:delText (like w:del — text is "leaving") ║
|
|
502
|
+
/// ║ MoveTo uses w:t (like w:ins — text is "arriving") ║
|
|
503
|
+
/// ╚══════════════════════════════════════════════════════════════╝
|
|
504
|
+
/// </summary>
|
|
505
|
+
public static void InsertMoveFromTo(Body body, string movedText, string author)
|
|
506
|
+
{
|
|
507
|
+
string moveId = GenerateRevisionId();
|
|
508
|
+
string moveId2 = GenerateRevisionId();
|
|
509
|
+
string moveName = "move" + moveId;
|
|
510
|
+
|
|
511
|
+
// ── MoveFrom paragraph (original location — text shown with strikethrough) ──
|
|
512
|
+
var moveFromPara = new Paragraph();
|
|
513
|
+
|
|
514
|
+
moveFromPara.Append(new MoveFromRangeStart
|
|
515
|
+
{
|
|
516
|
+
Id = moveId,
|
|
517
|
+
Author = author,
|
|
518
|
+
Date = DateTime.UtcNow,
|
|
519
|
+
Name = moveName
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
var moveFrom = new MoveFromRun
|
|
523
|
+
{
|
|
524
|
+
Id = GenerateRevisionId(),
|
|
525
|
+
Author = author,
|
|
526
|
+
Date = DateTime.UtcNow
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// MoveFrom uses DeletedText (w:delText), NOT Text (w:t)
|
|
530
|
+
// The text is visually struck through in Word
|
|
531
|
+
moveFrom.Append(new Run(
|
|
532
|
+
new DeletedText(movedText) { Space = SpaceProcessingModeValues.Preserve }));
|
|
533
|
+
|
|
534
|
+
moveFromPara.Append(moveFrom);
|
|
535
|
+
moveFromPara.Append(new MoveFromRangeEnd { Id = moveId });
|
|
536
|
+
|
|
537
|
+
body.Append(moveFromPara);
|
|
538
|
+
|
|
539
|
+
// ── MoveTo paragraph (destination — text shown with double underline) ──
|
|
540
|
+
var moveToPara = new Paragraph();
|
|
541
|
+
|
|
542
|
+
moveToPara.Append(new MoveToRangeStart
|
|
543
|
+
{
|
|
544
|
+
Id = moveId2,
|
|
545
|
+
Author = author,
|
|
546
|
+
Date = DateTime.UtcNow,
|
|
547
|
+
Name = moveName
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
var moveTo = new MoveToRun
|
|
551
|
+
{
|
|
552
|
+
Id = GenerateRevisionId(),
|
|
553
|
+
Author = author,
|
|
554
|
+
Date = DateTime.UtcNow
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// MoveTo uses Text (w:t), NOT DeletedText (w:delText)
|
|
558
|
+
// The text is visually double-underlined in green in Word
|
|
559
|
+
moveTo.Append(new Run(
|
|
560
|
+
new Text(movedText) { Space = SpaceProcessingModeValues.Preserve }));
|
|
561
|
+
|
|
562
|
+
moveToPara.Append(moveTo);
|
|
563
|
+
moveToPara.Append(new MoveToRangeEnd { Id = moveId2 });
|
|
564
|
+
|
|
565
|
+
body.Append(moveToPara);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ──────────────────────────────────────────────
|
|
569
|
+
// 10. GenerateRevisionId — unique ID pattern
|
|
570
|
+
// ──────────────────────────────────────────────
|
|
571
|
+
|
|
572
|
+
/// <summary>
|
|
573
|
+
/// Generates a unique revision ID string.
|
|
574
|
+
///
|
|
575
|
+
/// Revision IDs (w:id) must be unique across ALL revision elements in the document:
|
|
576
|
+
/// ins, del, rPrChange, pPrChange, moveFrom, moveTo, table row ins/del, etc.
|
|
577
|
+
///
|
|
578
|
+
/// Word uses simple incrementing integers starting from 0.
|
|
579
|
+
/// When programmatically adding revisions to an existing document,
|
|
580
|
+
/// first scan for the maximum existing ID and start from there.
|
|
581
|
+
///
|
|
582
|
+
/// For new documents, a simple counter suffices.
|
|
583
|
+
/// For existing documents, use:
|
|
584
|
+
/// int maxId = body.Descendants()
|
|
585
|
+
/// .SelectMany(e => e.GetAttributes())
|
|
586
|
+
/// .Where(a => a.LocalName == "id")
|
|
587
|
+
/// .Select(a => int.TryParse(a.Value, out int v) ? v : 0)
|
|
588
|
+
/// .DefaultIfEmpty(0)
|
|
589
|
+
/// .Max();
|
|
590
|
+
/// </summary>
|
|
591
|
+
public static string GenerateRevisionId()
|
|
592
|
+
{
|
|
593
|
+
return Interlocked.Increment(ref s_revisionCounter).ToString();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
using DocumentFormat.OpenXml.Wordprocessing;
|
|
2
|
+
|
|
3
|
+
namespace MiniMaxAIDocx.Core.Typography;
|
|
4
|
+
|
|
5
|
+
/// <summary>
|
|
6
|
+
/// CJK mixed typography helpers for East Asian font and paragraph configuration.
|
|
7
|
+
/// </summary>
|
|
8
|
+
public static class CjkHelper
|
|
9
|
+
{
|
|
10
|
+
public const string DefaultSimplifiedChinese = "SimSun";
|
|
11
|
+
public const string DefaultJapanese = "MS Mincho";
|
|
12
|
+
public const string DefaultKorean = "Batang";
|
|
13
|
+
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// Sets the East Asia font on run properties.
|
|
16
|
+
/// </summary>
|
|
17
|
+
public static void SetEastAsiaFont(RunProperties rPr, string fontName)
|
|
18
|
+
{
|
|
19
|
+
var fonts = rPr.RunFonts;
|
|
20
|
+
if (fonts == null)
|
|
21
|
+
{
|
|
22
|
+
fonts = new RunFonts();
|
|
23
|
+
rPr.RunFonts = fonts;
|
|
24
|
+
}
|
|
25
|
+
fonts.EastAsia = fontName;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// <summary>
|
|
29
|
+
/// Configures CJK-appropriate paragraph properties.
|
|
30
|
+
/// </summary>
|
|
31
|
+
public static void ConfigureCjkParagraph(ParagraphProperties pPr)
|
|
32
|
+
{
|
|
33
|
+
// Enable word wrap for CJK
|
|
34
|
+
pPr.WordWrap = new WordWrap { Val = true };
|
|
35
|
+
// Allow auto space between CJK and Latin/numbers
|
|
36
|
+
pPr.AutoSpaceDE = new AutoSpaceDE { Val = true };
|
|
37
|
+
pPr.AutoSpaceDN = new AutoSpaceDN { Val = true };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
namespace MiniMaxAIDocx.Core.Typography;
|
|
2
|
+
|
|
3
|
+
public record FontConfig(
|
|
4
|
+
string BodyFont,
|
|
5
|
+
string HeadingFont,
|
|
6
|
+
double BodySize,
|
|
7
|
+
double Heading1Size,
|
|
8
|
+
double Heading2Size,
|
|
9
|
+
double Heading3Size,
|
|
10
|
+
double Heading4Size,
|
|
11
|
+
double Heading5Size,
|
|
12
|
+
double Heading6Size,
|
|
13
|
+
double LineSpacing);
|
|
14
|
+
|
|
15
|
+
/// <summary>
|
|
16
|
+
/// Default font configurations by document type.
|
|
17
|
+
/// </summary>
|
|
18
|
+
public static class FontDefaults
|
|
19
|
+
{
|
|
20
|
+
public static FontConfig Report => new("Calibri", "Calibri Light", 11.0, 26.0, 20.0, 16.0, 14.0, 12.0, 11.0, 1.15);
|
|
21
|
+
public static FontConfig Letter => new("Calibri", "Calibri", 11.0, 16.0, 14.0, 12.0, 11.0, 11.0, 11.0, 1.0);
|
|
22
|
+
public static FontConfig Memo => new("Arial", "Arial", 11.0, 16.0, 14.0, 12.0, 11.0, 11.0, 11.0, 1.15);
|
|
23
|
+
public static FontConfig Academic => new("Times New Roman", "Times New Roman", 12.0, 16.0, 14.0, 13.0, 12.0, 12.0, 12.0, 2.0);
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
namespace MiniMaxAIDocx.Core.Typography;
|
|
2
|
+
|
|
3
|
+
public record PageSize(int WidthDxa, int HeightDxa);
|
|
4
|
+
public record MarginConfig(int TopDxa, int BottomDxa, int LeftDxa, int RightDxa);
|
|
5
|
+
|
|
6
|
+
/// <summary>
|
|
7
|
+
/// Standard page sizes and margin presets in DXA units.
|
|
8
|
+
/// </summary>
|
|
9
|
+
public static class PageSizes
|
|
10
|
+
{
|
|
11
|
+
public static PageSize Letter => new(12240, 15840); // 8.5 x 11 inches
|
|
12
|
+
public static PageSize A4 => new(11906, 16838); // 210 x 297 mm
|
|
13
|
+
public static PageSize Legal => new(12240, 20160); // 8.5 x 14 inches
|
|
14
|
+
public static PageSize A3 => new(16838, 23811); // 297 x 420 mm
|
|
15
|
+
public static PageSize A5 => new(8391, 11906); // 148 x 210 mm
|
|
16
|
+
|
|
17
|
+
public static MarginConfig StandardMargins => new(1440, 1440, 1440, 1440); // 1 inch all
|
|
18
|
+
public static MarginConfig NarrowMargins => new(720, 720, 720, 720); // 0.5 inch all
|
|
19
|
+
public static MarginConfig WideMargins => new(1440, 1440, 2160, 2160); // 1" top/bottom, 1.5" left/right
|
|
20
|
+
}
|