@nguyenphp/antigravity-marketing 1.0.18 → 1.0.19

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.
Files changed (127) hide show
  1. package/README.md +186 -78
  2. package/package.json +4 -3
  3. package/templates/.agent/skills/marketing-report-expert/SKILL.md +70 -0
  4. package/templates/.agent/skills/minimax-docx/LICENSE +21 -0
  5. package/templates/.agent/skills/minimax-docx/SKILL.md +274 -0
  6. package/templates/.agent/skills/minimax-docx/assets/styles/academic_styles.xml +250 -0
  7. package/templates/.agent/skills/minimax-docx/assets/styles/corporate_styles.xml +284 -0
  8. package/templates/.agent/skills/minimax-docx/assets/styles/default_styles.xml +449 -0
  9. package/templates/.agent/skills/minimax-docx/assets/xsd/aesthetic-rules.xsd +470 -0
  10. package/templates/.agent/skills/minimax-docx/assets/xsd/business-rules.xsd +130 -0
  11. package/templates/.agent/skills/minimax-docx/assets/xsd/common-types.xsd +159 -0
  12. package/templates/.agent/skills/minimax-docx/assets/xsd/wml-subset.xsd +589 -0
  13. package/templates/.agent/skills/minimax-docx/references/cjk_typography.md +357 -0
  14. package/templates/.agent/skills/minimax-docx/references/cjk_university_template_guide.md +184 -0
  15. package/templates/.agent/skills/minimax-docx/references/comments_guide.md +191 -0
  16. package/templates/.agent/skills/minimax-docx/references/design_good_bad_examples.md +829 -0
  17. package/templates/.agent/skills/minimax-docx/references/design_principles.md +819 -0
  18. package/templates/.agent/skills/minimax-docx/references/openxml_element_order.md +308 -0
  19. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part1.md +4061 -0
  20. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part2.md +2820 -0
  21. package/templates/.agent/skills/minimax-docx/references/openxml_encyclopedia_part3.md +3381 -0
  22. package/templates/.agent/skills/minimax-docx/references/openxml_namespaces.md +82 -0
  23. package/templates/.agent/skills/minimax-docx/references/openxml_units.md +72 -0
  24. package/templates/.agent/skills/minimax-docx/references/scenario_a_create.md +284 -0
  25. package/templates/.agent/skills/minimax-docx/references/scenario_b_edit_content.md +295 -0
  26. package/templates/.agent/skills/minimax-docx/references/scenario_c_apply_template.md +456 -0
  27. package/templates/.agent/skills/minimax-docx/references/track_changes_guide.md +200 -0
  28. package/templates/.agent/skills/minimax-docx/references/troubleshooting.md +506 -0
  29. package/templates/.agent/skills/minimax-docx/references/typography_guide.md +294 -0
  30. package/templates/.agent/skills/minimax-docx/references/xsd_validation_guide.md +158 -0
  31. package/templates/.agent/skills/minimax-docx/scripts/doc_to_docx.sh +40 -0
  32. package/templates/.agent/skills/minimax-docx/scripts/docx_preview.sh +37 -0
  33. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj +19 -0
  34. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs +18 -0
  35. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs +147 -0
  36. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs +322 -0
  37. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs +324 -0
  38. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs +155 -0
  39. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs +487 -0
  40. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs +108 -0
  41. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs +122 -0
  42. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs +107 -0
  43. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj +15 -0
  44. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs +169 -0
  45. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs +80 -0
  46. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs +42 -0
  47. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs +81 -0
  48. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs +81 -0
  49. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs +99 -0
  50. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs +23 -0
  51. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs +1832 -0
  52. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs +910 -0
  53. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs +999 -0
  54. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs +1048 -0
  55. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs +1038 -0
  56. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs +1020 -0
  57. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs +1121 -0
  58. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs +624 -0
  59. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs +675 -0
  60. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs +838 -0
  61. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs +917 -0
  62. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs +826 -0
  63. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs +1199 -0
  64. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs +1487 -0
  65. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs +1163 -0
  66. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs +595 -0
  67. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs +39 -0
  68. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs +24 -0
  69. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs +20 -0
  70. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs +224 -0
  71. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs +148 -0
  72. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs +23 -0
  73. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs +69 -0
  74. package/templates/.agent/skills/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx +4 -0
  75. package/templates/.agent/skills/minimax-docx/scripts/env_check.sh +196 -0
  76. package/templates/.agent/skills/minimax-docx/scripts/setup.ps1 +274 -0
  77. package/templates/.agent/skills/minimax-docx/scripts/setup.sh +504 -0
  78. package/templates/.agent/skills/minimax-multimodal-toolkit/SKILL.md +359 -0
  79. package/templates/.agent/skills/minimax-pdf/README.md +222 -0
  80. package/templates/.agent/skills/minimax-pdf/SKILL.md +201 -0
  81. package/templates/.agent/skills/minimax-pdf/design/design.md +381 -0
  82. package/templates/.agent/skills/minimax-pdf/scripts/cover.py +1579 -0
  83. package/templates/.agent/skills/minimax-pdf/scripts/fill_inspect.py +200 -0
  84. package/templates/.agent/skills/minimax-pdf/scripts/fill_write.py +242 -0
  85. package/templates/.agent/skills/minimax-pdf/scripts/make.sh +491 -0
  86. package/templates/.agent/skills/minimax-pdf/scripts/merge.py +112 -0
  87. package/templates/.agent/skills/minimax-pdf/scripts/palette.py +559 -0
  88. package/templates/.agent/skills/minimax-pdf/scripts/reformat_parse.py +374 -0
  89. package/templates/.agent/skills/minimax-pdf/scripts/render_body.py +1055 -0
  90. package/templates/.agent/skills/minimax-pdf/scripts/render_cover.cjs +111 -0
  91. package/templates/.agent/skills/minimax-xlsx/SKILL.md +138 -0
  92. package/templates/.agent/skills/minimax-xlsx/references/create.md +691 -0
  93. package/templates/.agent/skills/minimax-xlsx/references/edit.md +684 -0
  94. package/templates/.agent/skills/minimax-xlsx/references/fix.md +37 -0
  95. package/templates/.agent/skills/minimax-xlsx/references/format.md +768 -0
  96. package/templates/.agent/skills/minimax-xlsx/references/ooxml-cheatsheet.md +231 -0
  97. package/templates/.agent/skills/minimax-xlsx/references/read-analyze.md +97 -0
  98. package/templates/.agent/skills/minimax-xlsx/references/validate.md +772 -0
  99. package/templates/.agent/skills/minimax-xlsx/scripts/formula_check.py +422 -0
  100. package/templates/.agent/skills/minimax-xlsx/scripts/libreoffice_recalc.py +248 -0
  101. package/templates/.agent/skills/minimax-xlsx/scripts/shared_strings_builder.py +163 -0
  102. package/templates/.agent/skills/minimax-xlsx/scripts/style_audit.py +575 -0
  103. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_add_column.py +395 -0
  104. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_insert_row.py +274 -0
  105. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_pack.py +87 -0
  106. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_reader.py +362 -0
  107. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_shift_rows.py +396 -0
  108. package/templates/.agent/skills/minimax-xlsx/scripts/xlsx_unpack.py +130 -0
  109. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml +9 -0
  110. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/_rels/.rels +6 -0
  111. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels +19 -0
  112. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml +33 -0
  113. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml +160 -0
  114. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml +30 -0
  115. package/templates/.agent/skills/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml +70 -0
  116. package/templates/.agent/skills/pptx-generator/SKILL.md +249 -0
  117. package/templates/.agent/skills/pptx-generator/references/design-system.md +392 -0
  118. package/templates/.agent/skills/pptx-generator/references/editing.md +162 -0
  119. package/templates/.agent/skills/pptx-generator/references/pitfalls.md +112 -0
  120. package/templates/.agent/skills/pptx-generator/references/pptxgenjs.md +420 -0
  121. package/templates/.agent/skills/pptx-generator/references/slide-types.md +413 -0
  122. package/templates/.agent/skills/tutorial-video-expert/SKILL.md +88 -0
  123. package/templates/.agent/skills/ui-ux-pro-max/SKILL.md +170 -585
  124. package/templates/.agent/skills/vision-analysis/SKILL.md +174 -0
  125. package/templates/.agent/workflows/analyze.md +3 -0
  126. package/templates/.agent/workflows/brand-report.md +44 -0
  127. package/templates/.agent/workflows/report.md +49 -0
@@ -0,0 +1,224 @@
1
+ using System.IO.Compression;
2
+ using System.Xml.Linq;
3
+
4
+ namespace MiniMaxAIDocx.Core.Validation;
5
+
6
+ public class BusinessRuleValidator
7
+ {
8
+ private static readonly XNamespace W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
9
+ private static readonly XNamespace R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
10
+ private static readonly XNamespace WP = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
11
+ private static readonly XNamespace A = "http://schemas.openxmlformats.org/drawingml/2006/main";
12
+
13
+ private const int MinMarginDxa = 360; // 0.25 inch
14
+ private const int MaxMarginDxa = 4320; // 3 inches
15
+ private const int MinBodyFontHps = 16; // 8pt
16
+ private const int MaxBodyFontHps = 144; // 72pt
17
+ private const int MinHeadingFontHps = 20; // 10pt
18
+ private const int MaxHeadingFontHps = 192; // 96pt
19
+
20
+ public ValidationResult Validate(string docxPath)
21
+ {
22
+ var result = new ValidationResult();
23
+
24
+ using var zip = ZipFile.OpenRead(docxPath);
25
+ var docEntry = zip.GetEntry("word/document.xml")
26
+ ?? throw new InvalidOperationException("Missing word/document.xml");
27
+
28
+ var doc = LoadXml(docEntry);
29
+ var body = doc.Root?.Element(W + "body");
30
+ if (body == null)
31
+ {
32
+ result.Errors.Add(Error("Document has no body element"));
33
+ return result;
34
+ }
35
+
36
+ ValidateMargins(body, result);
37
+ ValidateFontSizes(body, result);
38
+ ValidateHeadingHierarchy(body, result);
39
+ ValidateTableColumnWidths(body, result);
40
+ ValidateRelationships(zip, doc, result);
41
+ ValidateComments(zip, result);
42
+
43
+ return result;
44
+ }
45
+
46
+ private void ValidateMargins(XElement body, ValidationResult result)
47
+ {
48
+ foreach (var sectPr in body.Descendants(W + "sectPr"))
49
+ {
50
+ var pgMar = sectPr.Element(W + "pgMar");
51
+ if (pgMar == null) continue;
52
+
53
+ foreach (var attr in new[] { "top", "bottom", "left", "right" })
54
+ {
55
+ var val = (string?)pgMar.Attribute(W + attr);
56
+ if (val != null && int.TryParse(val, out var dxa))
57
+ {
58
+ var absDxa = Math.Abs(dxa);
59
+ if (absDxa < MinMarginDxa)
60
+ result.Errors.Add(Error($"Margin '{attr}' is {absDxa} DXA ({absDxa / 1440.0:F2}\"), below minimum {MinMarginDxa} DXA"));
61
+ if (absDxa > MaxMarginDxa)
62
+ result.Warnings.Add(Warning($"Margin '{attr}' is {absDxa} DXA ({absDxa / 1440.0:F2}\"), above maximum {MaxMarginDxa} DXA"));
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ private void ValidateFontSizes(XElement body, ValidationResult result)
69
+ {
70
+ foreach (var p in body.Descendants(W + "p"))
71
+ {
72
+ var pStyle = p.Element(W + "pPr")?.Element(W + "pStyle")?.Attribute(W + "val")?.Value;
73
+ bool isHeading = pStyle?.StartsWith("Heading", StringComparison.OrdinalIgnoreCase) == true;
74
+
75
+ foreach (var rPr in p.Descendants(W + "rPr"))
76
+ {
77
+ var szEl = rPr.Element(W + "sz");
78
+ var val = (string?)szEl?.Attribute(W + "val");
79
+ if (val != null && int.TryParse(val, out var hps))
80
+ {
81
+ int min = isHeading ? MinHeadingFontHps : MinBodyFontHps;
82
+ int max = isHeading ? MaxHeadingFontHps : MaxBodyFontHps;
83
+ if (hps < min || hps > max)
84
+ result.Warnings.Add(Warning($"Font size {hps / 2.0}pt is outside {(isHeading ? "heading" : "body")} range ({min / 2}-{max / 2}pt)"));
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ private void ValidateHeadingHierarchy(XElement body, ValidationResult result)
91
+ {
92
+ int lastLevel = 0;
93
+ foreach (var p in body.Descendants(W + "p"))
94
+ {
95
+ var pStyle = p.Element(W + "pPr")?.Element(W + "pStyle")?.Attribute(W + "val")?.Value;
96
+ if (pStyle == null) continue;
97
+
98
+ int level = 0;
99
+ if (pStyle.StartsWith("Heading", StringComparison.OrdinalIgnoreCase))
100
+ {
101
+ var numPart = pStyle.AsSpan(7);
102
+ if (int.TryParse(numPart, out var parsed)) level = parsed;
103
+ }
104
+
105
+ if (level > 0)
106
+ {
107
+ if (lastLevel > 0 && level > lastLevel + 1)
108
+ result.Warnings.Add(Warning($"Heading level skips from {lastLevel} to {level} (missing Heading{lastLevel + 1})"));
109
+ lastLevel = level;
110
+ }
111
+ }
112
+ }
113
+
114
+ private void ValidateTableColumnWidths(XElement body, ValidationResult result)
115
+ {
116
+ var sectPr = body.Element(W + "sectPr");
117
+ if (sectPr == null) return;
118
+
119
+ var pgSz = sectPr.Element(W + "pgSz");
120
+ var pgMar = sectPr.Element(W + "pgMar");
121
+ if (pgSz == null || pgMar == null) return;
122
+
123
+ if (!int.TryParse((string?)pgSz.Attribute(W + "w"), out var pageWidth)) return;
124
+ int.TryParse((string?)pgMar.Attribute(W + "left"), out var marginLeft);
125
+ int.TryParse((string?)pgMar.Attribute(W + "right"), out var marginRight);
126
+ var contentWidth = pageWidth - marginLeft - marginRight;
127
+
128
+ int tableIndex = 0;
129
+ foreach (var tbl in body.Descendants(W + "tbl"))
130
+ {
131
+ tableIndex++;
132
+ var firstRow = tbl.Element(W + "tr");
133
+ if (firstRow == null) continue;
134
+
135
+ int totalWidth = 0;
136
+ foreach (var tc in firstRow.Elements(W + "tc"))
137
+ {
138
+ var tcW = tc.Element(W + "tcPr")?.Element(W + "tcW");
139
+ var w = (string?)tcW?.Attribute(W + "w");
140
+ if (w != null && int.TryParse(w, out var cellWidth))
141
+ totalWidth += cellWidth;
142
+ }
143
+
144
+ if (totalWidth > 0)
145
+ {
146
+ var tolerance = contentWidth * 0.02;
147
+ if (Math.Abs(totalWidth - contentWidth) > tolerance)
148
+ result.Warnings.Add(Warning($"Table {tableIndex}: column widths sum to {totalWidth} DXA but content width is {contentWidth} DXA"));
149
+ }
150
+ }
151
+ }
152
+
153
+ private void ValidateRelationships(ZipArchive zip, XDocument doc, ValidationResult result)
154
+ {
155
+ var relsEntry = zip.GetEntry("word/_rels/document.xml.rels");
156
+ if (relsEntry == null) return;
157
+
158
+ var relDoc = LoadXml(relsEntry);
159
+ var ns = relDoc.Root?.Name.Namespace ?? XNamespace.None;
160
+ var definedIds = new HashSet<string>();
161
+
162
+ foreach (var rel in relDoc.Descendants(ns + "Relationship"))
163
+ {
164
+ var id = (string?)rel.Attribute("Id");
165
+ if (id != null) definedIds.Add(id);
166
+ }
167
+
168
+ var referencedIds = new HashSet<string>();
169
+ foreach (var el in doc.Descendants())
170
+ {
171
+ var rid = (string?)el.Attribute(R + "id") ?? (string?)el.Attribute(R + "embed");
172
+ if (rid != null) referencedIds.Add(rid);
173
+ }
174
+
175
+ foreach (var id in referencedIds.Except(definedIds))
176
+ result.Errors.Add(Error($"Reference r:id='{id}' has no matching relationship"));
177
+
178
+ foreach (var id in definedIds.Except(referencedIds))
179
+ result.Warnings.Add(Warning($"Orphaned relationship: Id='{id}' is defined but never referenced"));
180
+ }
181
+
182
+ private void ValidateComments(ZipArchive zip, ValidationResult result)
183
+ {
184
+ var commentFiles = new[] { "word/comments.xml", "word/commentsExtended.xml", "word/commentsIds.xml", "word/commentsExtensible.xml" };
185
+ var existing = commentFiles.Where(f => zip.GetEntry(f) != null).ToList();
186
+
187
+ if (existing.Count > 0 && existing.Count < 4)
188
+ {
189
+ var missing = commentFiles.Except(existing);
190
+ result.Warnings.Add(Warning($"Comments partially present. Missing: {string.Join(", ", missing)}"));
191
+ }
192
+
193
+ if (zip.GetEntry("word/comments.xml") is { } commentsEntry)
194
+ {
195
+ var commentsDoc = LoadXml(commentsEntry);
196
+ var commentIds = commentsDoc.Descendants(W + "comment")
197
+ .Select(c => (string?)c.Attribute(W + "id"))
198
+ .Where(id => id != null)
199
+ .ToHashSet();
200
+
201
+ if (zip.GetEntry("word/commentsExtended.xml") is { } extEntry)
202
+ {
203
+ var W15 = XNamespace.Get("http://schemas.microsoft.com/office/word/2012/wordml");
204
+ var extDoc = LoadXml(extEntry);
205
+ var extIds = extDoc.Descendants(W15 + "commentEx")
206
+ .Select(c => (string?)c.Attribute(W15 + "paraId"))
207
+ .Where(id => id != null)
208
+ .ToHashSet();
209
+
210
+ if (commentIds.Count > 0 && extIds.Count == 0)
211
+ result.Warnings.Add(Warning("comments.xml has entries but commentsExtended.xml has none"));
212
+ }
213
+ }
214
+ }
215
+
216
+ private static XDocument LoadXml(ZipArchiveEntry entry)
217
+ {
218
+ using var stream = entry.Open();
219
+ return XDocument.Load(stream);
220
+ }
221
+
222
+ private static ValidationError Error(string msg) => new() { Message = msg, Severity = "Error" };
223
+ private static ValidationError Warning(string msg) => new() { Message = msg, Severity = "Warning" };
224
+ }
@@ -0,0 +1,148 @@
1
+ using System.IO.Compression;
2
+ using System.Xml.Linq;
3
+
4
+ namespace MiniMaxAIDocx.Core.Validation;
5
+
6
+ public class GateCheckResult
7
+ {
8
+ public bool Passed => Violations.Count == 0;
9
+ public List<string> Violations { get; set; } = new();
10
+ }
11
+
12
+ public class GateCheckValidator
13
+ {
14
+ private static readonly XNamespace W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
15
+
16
+ public GateCheckResult Validate(string outputDocxPath, string templateDocxPath)
17
+ {
18
+ var result = new GateCheckResult();
19
+
20
+ var templateStyles = ExtractStyles(templateDocxPath);
21
+ var outputStyles = ExtractStyles(outputDocxPath);
22
+ var templateSectPr = ExtractSectionProperties(templateDocxPath);
23
+ var outputSectPr = ExtractSectionProperties(outputDocxPath);
24
+
25
+ // All template styles must exist in output
26
+ foreach (var style in templateStyles)
27
+ {
28
+ if (!outputStyles.Contains(style))
29
+ result.Violations.Add($"Missing style: '{style}' defined in template but absent from output");
30
+ }
31
+
32
+ // Page margins must match
33
+ if (templateSectPr.Margins != null && outputSectPr.Margins != null)
34
+ {
35
+ var tm = templateSectPr.Margins;
36
+ var om = outputSectPr.Margins;
37
+ if (tm.Top != om.Top || tm.Bottom != om.Bottom || tm.Left != om.Left || tm.Right != om.Right)
38
+ result.Violations.Add($"Page margins mismatch: template=({tm.Top},{tm.Bottom},{tm.Left},{tm.Right}) output=({om.Top},{om.Bottom},{om.Left},{om.Right})");
39
+ }
40
+
41
+ // Page size must match
42
+ if (templateSectPr.PageWidth != outputSectPr.PageWidth || templateSectPr.PageHeight != outputSectPr.PageHeight)
43
+ result.Violations.Add($"Page size mismatch: template=({templateSectPr.PageWidth}x{templateSectPr.PageHeight}) output=({outputSectPr.PageWidth}x{outputSectPr.PageHeight})");
44
+
45
+ // Default font must match
46
+ var templateFont = ExtractDefaultFont(templateDocxPath);
47
+ var outputFont = ExtractDefaultFont(outputDocxPath);
48
+ if (templateFont != null && outputFont != null && templateFont != outputFont)
49
+ result.Violations.Add($"Default font mismatch: template='{templateFont}' output='{outputFont}'");
50
+
51
+ // Heading font hierarchy consistency
52
+ ValidateHeadingFontHierarchy(outputDocxPath, result);
53
+
54
+ return result;
55
+ }
56
+
57
+ private HashSet<string> ExtractStyles(string docxPath)
58
+ {
59
+ using var zip = ZipFile.OpenRead(docxPath);
60
+ var entry = zip.GetEntry("word/styles.xml");
61
+ if (entry == null) return new();
62
+
63
+ using var stream = entry.Open();
64
+ var doc = XDocument.Load(stream);
65
+ return doc.Descendants(W + "style")
66
+ .Select(s => (string?)s.Attribute(W + "styleId"))
67
+ .Where(id => id != null)
68
+ .ToHashSet()!;
69
+ }
70
+
71
+ private record SectionProps(int PageWidth, int PageHeight, MarginInfo? Margins);
72
+ private record MarginInfo(int Top, int Bottom, int Left, int Right);
73
+
74
+ private SectionProps ExtractSectionProperties(string docxPath)
75
+ {
76
+ using var zip = ZipFile.OpenRead(docxPath);
77
+ var entry = zip.GetEntry("word/document.xml")!;
78
+ using var stream = entry.Open();
79
+ var doc = XDocument.Load(stream);
80
+
81
+ var sectPr = doc.Descendants(W + "sectPr").LastOrDefault();
82
+ if (sectPr == null) return new(0, 0, null);
83
+
84
+ int.TryParse((string?)sectPr.Element(W + "pgSz")?.Attribute(W + "w"), out var pw);
85
+ int.TryParse((string?)sectPr.Element(W + "pgSz")?.Attribute(W + "h"), out var ph);
86
+
87
+ var pgMar = sectPr.Element(W + "pgMar");
88
+ MarginInfo? margins = null;
89
+ if (pgMar != null)
90
+ {
91
+ int.TryParse((string?)pgMar.Attribute(W + "top"), out var t);
92
+ int.TryParse((string?)pgMar.Attribute(W + "bottom"), out var b);
93
+ int.TryParse((string?)pgMar.Attribute(W + "left"), out var l);
94
+ int.TryParse((string?)pgMar.Attribute(W + "right"), out var r);
95
+ margins = new(t, b, l, r);
96
+ }
97
+
98
+ return new(pw, ph, margins);
99
+ }
100
+
101
+ private string? ExtractDefaultFont(string docxPath)
102
+ {
103
+ using var zip = ZipFile.OpenRead(docxPath);
104
+ var entry = zip.GetEntry("word/styles.xml");
105
+ if (entry == null) return null;
106
+
107
+ using var stream = entry.Open();
108
+ var doc = XDocument.Load(stream);
109
+
110
+ var defaultStyle = doc.Descendants(W + "style")
111
+ .FirstOrDefault(s => (string?)s.Attribute(W + "type") == "paragraph"
112
+ && (string?)s.Attribute(W + "default") == "1");
113
+
114
+ return (string?)defaultStyle?.Descendants(W + "rFonts").FirstOrDefault()?.Attribute(W + "ascii");
115
+ }
116
+
117
+ private void ValidateHeadingFontHierarchy(string docxPath, GateCheckResult result)
118
+ {
119
+ using var zip = ZipFile.OpenRead(docxPath);
120
+ var entry = zip.GetEntry("word/styles.xml");
121
+ if (entry == null) return;
122
+
123
+ using var stream = entry.Open();
124
+ var doc = XDocument.Load(stream);
125
+
126
+ var headingSizes = new SortedDictionary<int, int>();
127
+ foreach (var style in doc.Descendants(W + "style"))
128
+ {
129
+ var id = (string?)style.Attribute(W + "styleId");
130
+ if (id == null || !id.StartsWith("Heading", StringComparison.OrdinalIgnoreCase)) continue;
131
+
132
+ var numPart = id.AsSpan(7);
133
+ if (!int.TryParse(numPart, out var level)) continue;
134
+
135
+ var sz = (string?)style.Descendants(W + "sz").FirstOrDefault()?.Attribute(W + "val");
136
+ if (sz != null && int.TryParse(sz, out var hps))
137
+ headingSizes[level] = hps;
138
+ }
139
+
140
+ int prevSize = int.MaxValue;
141
+ foreach (var (level, size) in headingSizes)
142
+ {
143
+ if (size > prevSize)
144
+ result.Violations.Add($"Heading{level} ({size / 2}pt) is larger than a higher-level heading ({prevSize / 2}pt)");
145
+ prevSize = size;
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,23 @@
1
+ namespace MiniMaxAIDocx.Core.Validation;
2
+
3
+ public class ValidationResult
4
+ {
5
+ public bool IsValid => Errors.Count == 0;
6
+ public List<ValidationError> Errors { get; set; } = new();
7
+ public List<ValidationError> Warnings { get; set; } = new();
8
+
9
+ public void Merge(ValidationResult other)
10
+ {
11
+ Errors.AddRange(other.Errors);
12
+ Warnings.AddRange(other.Warnings);
13
+ }
14
+ }
15
+
16
+ public class ValidationError
17
+ {
18
+ public int LineNumber { get; set; }
19
+ public int LinePosition { get; set; }
20
+ public string Element { get; set; } = "";
21
+ public string Message { get; set; } = "";
22
+ public string Severity { get; set; } = "Error";
23
+ }
@@ -0,0 +1,69 @@
1
+ using System.IO.Compression;
2
+ using System.Xml;
3
+ using System.Xml.Schema;
4
+
5
+ namespace MiniMaxAIDocx.Core.Validation;
6
+
7
+ public class XsdValidator
8
+ {
9
+ public ValidationResult Validate(string docxPath, string xsdPath)
10
+ {
11
+ using var zip = ZipFile.OpenRead(docxPath);
12
+ var entry = zip.GetEntry("word/document.xml")
13
+ ?? throw new InvalidOperationException("DOCX does not contain word/document.xml");
14
+
15
+ using var stream = entry.Open();
16
+ using var reader = new StreamReader(stream);
17
+ var xmlContent = reader.ReadToEnd();
18
+
19
+ return ValidateXml(xmlContent, xsdPath);
20
+ }
21
+
22
+ public ValidationResult ValidateXml(string xmlContent, string xsdPath)
23
+ {
24
+ var result = new ValidationResult();
25
+ var settings = new XmlReaderSettings();
26
+
27
+ var schemaSet = new XmlSchemaSet();
28
+ schemaSet.Add(null, xsdPath);
29
+ settings.Schemas = schemaSet;
30
+ settings.ValidationType = ValidationType.Schema;
31
+ settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
32
+
33
+ settings.ValidationEventHandler += (sender, e) =>
34
+ {
35
+ var error = new ValidationError
36
+ {
37
+ LineNumber = e.Exception?.LineNumber ?? 0,
38
+ LinePosition = e.Exception?.LinePosition ?? 0,
39
+ Message = e.Message,
40
+ Severity = e.Severity == XmlSeverityType.Warning ? "Warning" : "Error"
41
+ };
42
+
43
+ if (e.Severity == XmlSeverityType.Warning)
44
+ result.Warnings.Add(error);
45
+ else
46
+ result.Errors.Add(error);
47
+ };
48
+
49
+ using var stringReader = new StringReader(xmlContent);
50
+ using var xmlReader = XmlReader.Create(stringReader, settings);
51
+
52
+ try
53
+ {
54
+ while (xmlReader.Read()) { }
55
+ }
56
+ catch (XmlException ex)
57
+ {
58
+ result.Errors.Add(new ValidationError
59
+ {
60
+ LineNumber = ex.LineNumber,
61
+ LinePosition = ex.LinePosition,
62
+ Message = $"XML parse error: {ex.Message}",
63
+ Severity = "Error"
64
+ });
65
+ }
66
+
67
+ return result;
68
+ }
69
+ }
@@ -0,0 +1,4 @@
1
+ <Solution>
2
+ <Project Path="MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj" />
3
+ <Project Path="MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj" />
4
+ </Solution>
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env bash
2
+ # minimax-docx Quick Environment Check
3
+ # Cross-platform: macOS, Linux, WSL, Git Bash
4
+ # Run this BEFORE any minimax-docx operation. Use setup.sh for initial installation.
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
9
+ DOTNET_DIR="$SCRIPT_DIR/dotnet"
10
+
11
+ # Force English output for dotnet CLI
12
+ export DOTNET_CLI_UI_LANGUAGE=en
13
+
14
+ echo "=== minimax-docx Environment Check ==="
15
+ echo ""
16
+
17
+ STATUS="READY"
18
+ WARNINGS=0
19
+
20
+ # --- Detect platform ---
21
+ OS="unknown"
22
+ case "$(uname -s)" in
23
+ Darwin) OS="macos" ;;
24
+ Linux)
25
+ OS="linux"
26
+ grep -qi microsoft /proc/version 2>/dev/null && OS="wsl"
27
+ ;;
28
+ MINGW*|MSYS*|CYGWIN*) OS="windows-shell" ;;
29
+ esac
30
+
31
+ # --- Critical: .NET SDK ---
32
+ if ! command -v dotnet &>/dev/null; then
33
+ printf "[FAIL] %-14s not found\n" "dotnet"
34
+ echo ""
35
+ echo " .NET SDK is REQUIRED. Install it:"
36
+ case "$OS" in
37
+ macos) echo " brew install --cask dotnet-sdk" ;;
38
+ linux|wsl)
39
+ echo " # Option 1: Microsoft install script"
40
+ echo " wget https://dot.net/v1/dotnet-install.sh -O /tmp/dotnet-install.sh"
41
+ echo " chmod +x /tmp/dotnet-install.sh && /tmp/dotnet-install.sh --channel 8.0"
42
+ echo " # Option 2 (Ubuntu/Debian): sudo apt-get install -y dotnet-sdk-8.0"
43
+ ;;
44
+ windows-shell) echo " winget install Microsoft.DotNet.SDK.8" ;;
45
+ *) echo " https://dotnet.microsoft.com/download" ;;
46
+ esac
47
+ echo ""
48
+ echo " Or run the full setup: bash scripts/setup.sh"
49
+ echo ""
50
+ STATUS="NOT READY"
51
+ else
52
+ local_ver=$(dotnet --version 2>/dev/null || echo "0.0.0")
53
+ local_major="${local_ver%%.*}"
54
+ if [ "$local_major" -ge 8 ] 2>/dev/null; then
55
+ printf "[OK] %-14s %s (>= 8.0)\n" "dotnet" "$local_ver"
56
+ else
57
+ printf "[FAIL] %-14s %s (requires >= 8.0)\n" "dotnet" "$local_ver"
58
+ STATUS="NOT READY"
59
+ fi
60
+ fi
61
+
62
+ # --- Critical: NuGet packages ---
63
+ if [ -d "$DOTNET_DIR" ]; then
64
+ if [ -f "$DOTNET_DIR/MiniMaxAIDocx.Cli/bin/Debug/net10.0/MiniMaxAIDocx.Cli.dll" ] || \
65
+ [ -f "$DOTNET_DIR/MiniMaxAIDocx.Cli/bin/Debug/net8.0/MiniMaxAIDocx.Cli.dll" ]; then
66
+ printf "[OK] %-14s built\n" "project"
67
+ else
68
+ # Try restore + build
69
+ if dotnet restore "$DOTNET_DIR" --verbosity quiet &>/dev/null; then
70
+ printf "[OK] %-14s packages restored\n" "nuget"
71
+ if dotnet build "$DOTNET_DIR" --verbosity quiet --no-restore &>/dev/null; then
72
+ printf "[OK] %-14s build succeeded\n" "project"
73
+ else
74
+ printf "[FAIL] %-14s build failed (run: dotnet build %s)\n" "project" "$DOTNET_DIR"
75
+ STATUS="NOT READY"
76
+ fi
77
+ else
78
+ printf "[FAIL] %-14s restore failed\n" "nuget"
79
+ echo ""
80
+ echo " Common causes:"
81
+ echo " - No internet access (NuGet needs to download packages)"
82
+ echo " - Corporate proxy blocking nuget.org"
83
+ echo " - SSL certificate issues (try: dotnet nuget list source)"
84
+ echo ""
85
+ STATUS="NOT READY"
86
+ fi
87
+ fi
88
+ else
89
+ printf "[FAIL] %-14s directory not found: %s\n" "project" "$DOTNET_DIR"
90
+ STATUS="NOT READY"
91
+ fi
92
+
93
+ # --- Optional: pandoc ---
94
+ if command -v pandoc &>/dev/null; then
95
+ pandoc_ver=$(pandoc --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "?")
96
+ printf "[OK] %-14s %s (content preview)\n" "pandoc" "$pandoc_ver"
97
+ else
98
+ printf "[WARN] %-14s not found — docx_preview.sh will use fallback\n" "pandoc"
99
+ WARNINGS=$((WARNINGS + 1))
100
+ case "$OS" in
101
+ macos) echo " Install: brew install pandoc" ;;
102
+ linux|wsl) echo " Install: sudo apt-get install pandoc # or dnf/pacman" ;;
103
+ windows-shell) echo " Install: winget install JohnMacFarlane.Pandoc" ;;
104
+ esac
105
+ fi
106
+
107
+ # --- Optional: LibreOffice ---
108
+ if command -v soffice &>/dev/null; then
109
+ soffice_ver=$(soffice --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 || echo "?")
110
+ printf "[OK] %-14s %s (.doc conversion)\n" "soffice" "$soffice_ver"
111
+ else
112
+ # Check common paths
113
+ soffice_found=false
114
+ for p in \
115
+ "/Applications/LibreOffice.app/Contents/MacOS/soffice" \
116
+ "/usr/lib/libreoffice/program/soffice" \
117
+ "/snap/bin/libreoffice" \
118
+ "/opt/libreoffice/program/soffice"; do
119
+ if [ -x "$p" ]; then
120
+ printf "[OK] %-14s found at %s (.doc conversion)\n" "soffice" "$p"
121
+ soffice_found=true
122
+ break
123
+ fi
124
+ done
125
+ if ! $soffice_found; then
126
+ printf "[WARN] %-14s not found — .doc files cannot be converted\n" "soffice"
127
+ WARNINGS=$((WARNINGS + 1))
128
+ case "$OS" in
129
+ macos) echo " Install: brew install --cask libreoffice" ;;
130
+ linux|wsl) echo " Install: sudo apt-get install libreoffice-core" ;;
131
+ windows-shell) echo " Install: winget install TheDocumentFoundation.LibreOffice" ;;
132
+ esac
133
+ fi
134
+ fi
135
+
136
+ # --- Optional: zip/unzip ---
137
+ zip_ok=true
138
+ if ! command -v zip &>/dev/null; then
139
+ printf "[WARN] %-14s not found (optional, .NET handles DOCX natively)\n" "zip"
140
+ zip_ok=false
141
+ WARNINGS=$((WARNINGS + 1))
142
+ fi
143
+ if ! command -v unzip &>/dev/null; then
144
+ printf "[WARN] %-14s not found (optional, .NET handles DOCX natively)\n" "unzip"
145
+ zip_ok=false
146
+ WARNINGS=$((WARNINGS + 1))
147
+ fi
148
+ if $zip_ok; then
149
+ printf "[OK] %-14s available\n" "zip/unzip"
150
+ fi
151
+
152
+ # --- Encoding check ---
153
+ current_lang="${LANG:-}"
154
+ if [ -n "$current_lang" ] && echo "$current_lang" | grep -qi "utf-8\|utf8"; then
155
+ printf "[OK] %-14s %s\n" "locale" "$current_lang"
156
+ else
157
+ if [ -z "$current_lang" ]; then
158
+ printf "[WARN] %-14s LANG not set (CJK text may have issues)\n" "locale"
159
+ else
160
+ printf "[WARN] %-14s %s (not UTF-8, CJK text may have issues)\n" "locale" "$current_lang"
161
+ fi
162
+ WARNINGS=$((WARNINGS + 1))
163
+ echo " Fix: export LANG=en_US.UTF-8"
164
+ fi
165
+
166
+ # --- Shell script permissions ---
167
+ perm_issues=0
168
+ for s in "$SCRIPT_DIR"/*.sh; do
169
+ if [ -f "$s" ] && [ ! -x "$s" ]; then
170
+ perm_issues=$((perm_issues + 1))
171
+ fi
172
+ done
173
+ if [ "$perm_issues" -gt 0 ]; then
174
+ printf "[WARN] %-14s %d script(s) not executable\n" "permissions" "$perm_issues"
175
+ echo " Fix: chmod +x scripts/*.sh"
176
+ WARNINGS=$((WARNINGS + 1))
177
+ else
178
+ printf "[OK] %-14s all scripts executable\n" "permissions"
179
+ fi
180
+
181
+ # --- Result ---
182
+ echo ""
183
+ if [ "$STATUS" = "READY" ]; then
184
+ if [ "$WARNINGS" -gt 0 ]; then
185
+ echo "Status: READY (with $WARNINGS warning(s) — optional features may be limited)"
186
+ else
187
+ echo "Status: READY"
188
+ fi
189
+ else
190
+ echo "Status: NOT READY"
191
+ echo ""
192
+ echo "Critical dependencies missing. Run the full setup:"
193
+ echo " bash scripts/setup.sh # macOS / Linux / WSL"
194
+ echo " powershell scripts/setup.ps1 # Windows PowerShell"
195
+ exit 1
196
+ fi