diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db index 4bdc55a..b3f5ffa 100644 Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db differ diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db index f051733..6d7dd6c 100644 Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db differ diff --git a/WordAddIn/.vs/WordAddIn/v17/.suo b/WordAddIn/.vs/WordAddIn/v17/.suo index c243eff..3b19fbd 100644 Binary files a/WordAddIn/.vs/WordAddIn/v17/.suo and b/WordAddIn/.vs/WordAddIn/v17/.suo differ diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json index 131493c..196c58c 100644 --- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json +++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json @@ -1,7 +1,20 @@ { "Version": 1, "WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\", - "Documents": [], + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + } + ], "DocumentGroupContainers": [ { "Orientation": 0, @@ -9,11 +22,46 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": -1, + "SelectedChildIndex": 1, "Children": [ { "$type": "Bookmark", "Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "AiRibbon.cs", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs", + "RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs", + "RelativeToolTip": "WordAddIn\\AiRibbon.cs", + "ViewState": "AgIAACMAAAAAAAAAAAAowD8AAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:05:10.056Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:05:02.111Z" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:04:59.465Z" } ] } diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json index 131493c..196c58c 100644 --- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json +++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json @@ -1,7 +1,20 @@ { "Version": 1, "WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\", - "Documents": [], + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + }, + { + "AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", + "RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" + } + ], "DocumentGroupContainers": [ { "Orientation": 0, @@ -9,11 +22,46 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": -1, + "SelectedChildIndex": 1, "Children": [ { "$type": "Bookmark", "Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "AiRibbon.cs", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs", + "RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs", + "RelativeToolTip": "WordAddIn\\AiRibbon.cs", + "ViewState": "AgIAACMAAAAAAAAAAAAowD8AAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:05:10.056Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:05:02.111Z" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs", + "RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs", + "ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-01-24T10:04:59.465Z" } ] } diff --git a/WordAddIn/WordAddIn/AiRibbon.cs b/WordAddIn/WordAddIn/AiRibbon.cs index 1c273b0..dab926e 100644 --- a/WordAddIn/WordAddIn/AiRibbon.cs +++ b/WordAddIn/WordAddIn/AiRibbon.cs @@ -16,21 +16,44 @@ using Newtonsoft.Json; namespace WordAddIn { + /// + /// Word 插件的主入口类,负责处理 Ribbon 功能区的交互逻辑。 + /// 实现了 IRibbonExtensibility 接口,用于自定义 Word 的功能区界面。 + /// 包含了所有 Ribbon 按钮的点击事件处理方法。 + /// [ComVisible(true)] public class AiRibbon : Office.IRibbonExtensibility { + // Ribbon UI 实例引用,用于刷新界面等操作 private Office.IRibbonUI ribbon; - private AiService _aiService = new AiService(); - private static bool _isLoggedIn = false; // 登录状态 (静态变量,确保整个应用程序生命周期内有效) - private string _documentContext = null; // 存储文档的全文分析上下文 - private string _referenceContext = null; // 存储外部参考文档内容 + + // AI 服务实例,封装了所有与后端 API 的通信逻辑 + private AiService _aiService = new AiService(); + + // 登录状态标识 (静态变量,确保在插件生命周期内保持状态) + private static bool _isLoggedIn = false; + + // 存储文档的全文分析上下文 (通过"通读全文"功能生成),用于辅助 AI 理解文档背景 + private string _documentContext = null; + + // 存储外部参考文档内容 (通过"补充背景"功能导入),用于辅助 AI 进行写作 + private string _referenceContext = null; + /// + /// 默认构造函数 + /// public AiRibbon() { } #region IRibbonExtensibility 成员 + /// + /// 加载自定义 Ribbon XML 界面定义。 + /// 该方法由 VSTO 运行时调用。 + /// + /// Ribbon ID + /// 包含 Ribbon 定义的 XML 字符串 public string GetCustomUI(string ribbonID) { return GetResourceText("WordAddIn.AiRibbon.xml"); @@ -40,11 +63,22 @@ namespace WordAddIn #region Ribbon 回调 + /// + /// Ribbon 加载完成时的回调方法。 + /// 保存 Ribbon UI 实例引用,以便后续可以使用它来刷新 Ribbon 控件的状态。 + /// + /// Ribbon UI 实例 public void Ribbon_Load(Office.IRibbonUI ribbonUI) { this.ribbon = ribbonUI; } + /// + /// 获取组合的上下文信息,包含全文背景和外部参考资料。 + /// 将 _documentContext (全文背景) 和 _referenceContext (外部参考) 拼接成一个字符串。 + /// 用于发送给 AI 以提供更准确的生成结果,确保生成内容与文档上下文和参考资料保持一致。 + /// + /// 组合后的上下文描述字符串 private string GetCombinedContext() { StringBuilder sb = new StringBuilder(); @@ -63,13 +97,20 @@ namespace WordAddIn return sb.ToString(); } + /// + /// 【补充背景】按钮点击事件处理。 + /// 允许用户导入外部文档 (txt, doc, docx) 作为背景资料。 + /// 读取的文件内容将存储在 _referenceContext 中,供后续 AI 生成任务使用。 + /// + /// 触发事件的 Ribbon 控件 public void OnImportReferenceClick(Office.IRibbonControl control) { + // 检查用户是否已登录,未登录则弹出登录框 if (!EnsureLoggedIn()) return; using (OpenFileDialog openFileDialog = new OpenFileDialog()) { - openFileDialog.Multiselect = true; + openFileDialog.Multiselect = true; // 允许选择多个文件 openFileDialog.Title = "补充背景 - 导入文档"; openFileDialog.Filter = "文档文件|*.docx;*.doc;*.txt|Word 文档|*.docx;*.doc|文本文件|*.txt|所有文件|*.*"; @@ -90,6 +131,7 @@ namespace WordAddIn } else if (ext == ".docx" || ext == ".doc") { + // 使用 Word Interop 打开并读取文档内容 dynamic wordApp = Globals.ThisAddIn.Application; dynamic doc = wordApp.Documents.Open(FileName: file, Visible: false, ReadOnly: true, AddToRecentFiles: false); content = doc.Content.Text; @@ -119,6 +161,12 @@ namespace WordAddIn } } + /// + /// 【通读全文】按钮点击事件处理。 + /// 读取当前文档的全部文本内容,调用 AI 进行深度语义分析。 + /// 生成的文档画像(包括摘要、风格、术语等)将存储在 _documentContext 中。 + /// + /// 触发事件的 Ribbon 控件 public async void OnReadFullClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -145,7 +193,7 @@ namespace WordAddIn fullText = fullText.Substring(0, 20000); } - // 1. 语义分析 + // 调用 AI 服务进行文档分析 _documentContext = await Task.Run(() => _aiService.AnalyzeDocument(fullText)); if (!string.IsNullOrEmpty(_documentContext)) @@ -167,6 +215,14 @@ namespace WordAddIn } } + /// + /// 【润色加工】按钮点击事件处理。 + /// 功能描述: + /// 1. 获取选中文本(或剪贴板内容)。 + /// 2. 调用 AI 润色接口,优化语言流畅度和专业性。 + /// 3. 将润色后的文本替换回文档。 + /// + /// 触发事件的 Ribbon 控件 public async void OnPolishClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -189,12 +245,7 @@ namespace WordAddIn string clipboardText = (string)data.GetData(DataFormats.Text); if (!string.IsNullOrWhiteSpace(clipboardText)) { - // 询问用户是否要润色剪贴板内容 - // 简单起见,如果没选中文字,默认就是处理剪贴板(类似扩写逻辑) - // 但为了避免误操作,我们可以提示一下? - // 扩写逻辑是直接把剪贴板当 originalText,生成后插入光标处。 - // 润色通常是“替换”原文。如果是剪贴板,那就是“插入润色后的内容”。 - // 逻辑一致。 + // 剪贴板有内容,逻辑继续 } else { @@ -231,11 +282,12 @@ namespace WordAddIn loading.Show(); loading.Refresh(); - // 在后台线程运行 AI 请求 + // 3. 在后台线程运行 AI 请求,传入原文和组合上下文 string polishedText = await Task.Run(() => _aiService.PolishText(originalText, GetCombinedContext())); if (!string.IsNullOrEmpty(polishedText)) { + // 4. 使用智能插入替换原文 SmartInsert(selection, polishedText); } } @@ -253,6 +305,14 @@ namespace WordAddIn } } + /// + /// 【深度升华】按钮点击事件处理。 + /// 功能描述: + /// 1. 获取选中文本。 + /// 2. 调用 AI 升华接口,挖掘思想深度,提升价值。 + /// 3. 将升华后的文本插入文档。 + /// + /// 触发事件的 Ribbon 控件 public async void OnElevateClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -262,7 +322,7 @@ namespace WordAddIn { var selection = Globals.ThisAddIn.Application.Selection; - // 1. 判断是否有选中文本 + // 1. 判断是否有选中文本或剪贴板内容 bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal || (!string.IsNullOrEmpty(selection.Text) && selection.Text.Length > 1); @@ -302,16 +362,16 @@ namespace WordAddIn return; } - // 显示加载窗体 loading = new LoadingForm("AI 正在深度升华中,挖掘核心价值..."); loading.Show(); loading.Refresh(); - // 在后台线程运行 AI 请求 + // 2. 调用 AI 服务进行深度升华 string elevatedText = await Task.Run(() => _aiService.ElevateText(originalText, GetCombinedContext())); if (!string.IsNullOrEmpty(elevatedText)) { + // 3. 智能插入结果 SmartInsert(selection, elevatedText); } } @@ -329,6 +389,15 @@ namespace WordAddIn } } + /// + /// 【智能扩写】按钮点击事件处理。 + /// 功能描述: + /// 1. 获取用户选中的文本(或剪贴板内容)。 + /// 2. 弹出选项窗体,允许用户输入自定义扩写提示词。 + /// 3. 调用 AI 扩写接口,传入原文、用户提示和全文上下文。 + /// 4. 将 AI 生成的扩写内容替换原有选区,或插入到光标位置。 + /// + /// 触发事件的 Ribbon 控件 public async void OnExpandClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -339,6 +408,7 @@ namespace WordAddIn var selection = Globals.ThisAddIn.Application.Selection; // 1. 判断是否有选中文本 + // wdSelectionNormal 表示有选区,否则检查 Text 属性 bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal || (!string.IsNullOrEmpty(selection.Text) && selection.Text.Length > 1); @@ -349,46 +419,7 @@ namespace WordAddIn if (data != null && data.GetDataPresent(DataFormats.Text)) { string clipboardText = (string)data.GetData(DataFormats.Text); - if (!string.IsNullOrWhiteSpace(clipboardText)) - { - // 将剪贴板内容插入文档,并选中它作为待扩写内容 - selection.Range.Text = clipboardText; - // 重新获取 selection,确保包含刚插入的文本 - // 注意:赋值 Range.Text 后,selection 通常会变成插入文本后的光标,或者选中插入文本 - // 我们显式选中刚才插入的范围比较稳妥 - // 但简化处理,直接让用户确认是否扩写刚才粘贴的内容 - // 这里 selection.Text = clipboardText 会替换光标或选区 - // 此时 selection 可能位于文本末尾。 - // 为了扩写,我们需要选中这段新文本。 - // 简单起见,我们直接把 clipboardText 作为 originalText 传给 AI, - // 但回写时需要知道覆盖哪里。 - // 最好的方式是:先插入,让 Selection 包含这段文字。 - - // 实际上 Word 的 Selection.Text = "xxx" 后,Selection 会变成折叠状态在文字后面。 - // 所以我们需要倒推回去选中它。 - // 或者,我们可以直接记录 Range。 - - // 修正策略:直接用 clipboardText 发给 AI, - // 然后生成完后,直接在这个位置插入(Selection 还在原处或刚插入文本的后面)。 - // 但原来的逻辑是 selection.Text = expandedText (替换)。 - // 如果我刚才没插入,现在插入 expandedText 也是对的。 - // 唯一的问题是:加载动画期间,用户看不到原文。 - // 但这也许是好事?直接变身。 - // 让我们采用“直接替换”逻辑,不先插入原文。 - - // 但等等,如果是“扩写”,通常是基于原文。 - // 如果不插入,用户就没法在扩写前对原文做微调(虽然现在的流程也不支持微调)。 - // 既然用户点击了按钮,就是想一步到位。 - - // 所以:如果没选中,就假装选中了剪贴板内容。 - // originalText = clipboardText - // 后面 selection.Text = expandedText 会直接在光标处插入扩写后的内容。 - // 完美。 - - // 唯一例外:如果 selection 是光标,selection.Text = expandedText 会插入。 - // 此时 originalText = clipboardText. - } - else + if (string.IsNullOrWhiteSpace(clipboardText)) { MessageBox.Show("剪贴板中内容为空,请先选择一段需要扩写的文字,或者复制文字到剪贴板。"); return; @@ -401,10 +432,6 @@ namespace WordAddIn } } - // 此时 selection.Text 可能为空(如果是剪贴板模式), - // 所以我们不能再依赖 selection.Text.Trim() 作为 originalText - // 我们需要一个变量来持有它。 - string originalText; if (hasSelection) { @@ -412,7 +439,7 @@ namespace WordAddIn } else { - // 再次获取,确保逻辑严谨(虽然上面已经 check 过了) + // 使用剪贴板内容 IDataObject data = Clipboard.GetDataObject(); originalText = (string)data.GetData(DataFormats.Text); } @@ -423,7 +450,7 @@ namespace WordAddIn return; } - // 弹出选项设置窗体 + // 3. 弹出选项设置窗体,让用户输入自定义扩写提示词 string customPrompt = null; using (var optionsForm = new ExpandOptionsForm()) { @@ -434,17 +461,17 @@ namespace WordAddIn customPrompt = optionsForm.SelectedPrompt; } - // 显示加载窗体 loading = new LoadingForm("AI 正在扩写中,请稍候..."); loading.Show(); - loading.Refresh(); // 强制重绘 + loading.Refresh(); - // 在后台线程运行 AI 请求,防止阻塞 UI + // 4. 在后台线程运行 AI 请求,防止阻塞 UI + // 传入 GetCombinedContext() 以利用文档背景和参考资料 string expandedText = await Task.Run(() => _aiService.ExpandText(originalText, customPrompt, GetCombinedContext())); if (!string.IsNullOrEmpty(expandedText)) { - // 使用智能插入代替直接赋值 + // 5. 智能插入:尝试保持格式 SmartInsert(selection, expandedText); } } @@ -462,6 +489,15 @@ namespace WordAddIn } } + /// + /// 【素材写作】按钮点击事件处理。 + /// 功能描述: + /// 1. 获取选中文本作为上下文(可选)。 + /// 2. 弹出素材写作配置窗体,允许用户输入文本素材或上传文档素材。 + /// 3. 解析上传的文件内容(支持 .txt, .doc, .docx)。 + /// 4. 调用 AI 接口,根据素材生成新的内容。 + /// + /// 触发事件的 Ribbon 控件 public async void OnReferenceGenerateClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -470,11 +506,12 @@ namespace WordAddIn try { var selection = Globals.ThisAddIn.Application.Selection; - string originalText = selection.Text; // 可以为空 + string originalText = selection.Text; // 可以为空,作为生成的上下文参考 string referenceContent = null; int wordLimit = 500; + // 1. 弹出素材写作配置窗体 using (var form = new ReferenceGeneratorForm()) { if (form.ShowDialog() != DialogResult.OK) @@ -484,8 +521,10 @@ namespace WordAddIn wordLimit = form.WordLimit; + // 2. 处理素材内容 if (form.IsFileMode) { + // 文件模式:读取文件内容 string path = form.SelectedFilePath; string ext = Path.GetExtension(path).ToLower(); @@ -495,10 +534,9 @@ namespace WordAddIn } else { - // 尝试用 Word 打开 + // 尝试用 Word Interop 打开读取 (针对 .doc, .docx) try { - // 使用 dynamic 避免繁琐的 ref object dynamic wordApp = Globals.ThisAddIn.Application; dynamic doc = wordApp.Documents.Open(FileName: path, Visible: false, ReadOnly: true, AddToRecentFiles: false); referenceContent = doc.Content.Text; @@ -513,6 +551,7 @@ namespace WordAddIn } else { + // 文本模式:直接获取输入的文本 referenceContent = form.ReferenceContent; } } @@ -527,11 +566,12 @@ namespace WordAddIn loading.Show(); loading.Refresh(); + // 3. 调用 AI 服务进行生成 + // 传入原文(上下文)、参考素材、字数限制、全文背景 string result = await Task.Run(() => _aiService.GenerateWithReference(originalText, referenceContent, wordLimit, GetCombinedContext())); if (!string.IsNullOrEmpty(result)) { - // 使用智能插入代替直接赋值 SmartInsert(selection, result); } } @@ -549,21 +589,18 @@ namespace WordAddIn } } + /// + /// 智能插入辅助方法。 + /// 使用 TypeText 方法插入文本,以保持当前光标处的格式。 + /// 如果 TypeText 失败,则回退到直接赋值 Text 属性。 + /// + /// Word 选区对象 + /// 要插入的文本 private void SmartInsert(Word.Selection selection, string text) { try { - // 简化逻辑:直接使用 TypeText 插入,让 Word 自动应用当前光标位置的格式 - // 这样可以最大程度保持与原文格式一致(兼容性最好) - // 同时也避免了手动应用样式带来的偏差 - - // 如果是覆盖选区,先检查是否需要清空(TypeText 会自动替换选区,但显式处理更清晰) - // selection.TypeText(text); // 这就足够了 - - // 2024-01-24 修正:用户反馈格式偏差大,且不希望 Markdown。 - // 因此我们不再进行 Markdown 解析,也不再强制应用 StyleProfile 中的样式。 - // 直接插入纯文本,让 Word 处理。 - + // 使用 TypeText 插入,让 Word 自动应用当前光标位置的格式 selection.TypeText(text); } catch (Exception ex) @@ -574,6 +611,15 @@ namespace WordAddIn } } + /// + /// 【格式美化】按钮点击事件处理。 + /// 功能描述: + /// 1. 读取当前文档全文。 + /// 2. 调用 AI 分析文档结构,识别各级标题。 + /// 3. 遍历文档段落,根据 AI 返回的标题列表,将匹配的段落设置为对应的标题样式 (Heading 1-5)。 + /// 4. 对非标题的正文段落应用首行缩进。 + /// + /// 触发事件的 Ribbon 控件 public async void OnFormatClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -584,13 +630,14 @@ namespace WordAddIn var doc = Globals.ThisAddIn.Application.ActiveDocument; string fullText = doc.Content.Text; + // 1. 基础校验:文档内容太少则不处理 if (string.IsNullOrWhiteSpace(fullText) || fullText.Length < 10) { MessageBox.Show("文档内容过少,无法进行格式美化。"); return; } - // 限制长度防止 Token 溢出 + // 2. 截断处理:限制长度防止 Token 溢出 (保留前 20000 字符进行结构分析) if (fullText.Length > 20000) { fullText = fullText.Substring(0, 20000); @@ -600,17 +647,18 @@ namespace WordAddIn loading.Show(); loading.Refresh(); - // 1. AI 分析结构 + // 3. AI 分析结构:识别标题及其层级 + // 这一步是关键,利用大模型理解语义来判断哪些行是标题,比正则更准确 var headings = await Task.Run(() => _aiService.AnalyzeStructure(fullText)); // 即使 headings 为空,我们也要继续处理正文缩进,所以这里不再直接 return if (headings == null) { headings = new List(); - // MessageBox.Show("未识别到有效的标题结构。"); // 可以不提示,静默处理 } - // 2. 应用样式 (在 UI 线程) + // 4. 应用样式 (在 UI 线程操作 Word 对象) + // 遍历所有段落,性能可能受文档长度影响,但对于一般文档可接受 int changeCount = 0; int indentCount = 0; foreach (Word.Paragraph p in doc.Paragraphs) @@ -618,13 +666,13 @@ namespace WordAddIn string pText = p.Range.Text.Trim(); if (string.IsNullOrWhiteSpace(pText)) continue; - // 查找匹配的标题 - // AI 返回的 text 可能不包含序号,或者有微小差异。 - // 使用 Contains 匹配比较宽容 + // 5. 查找匹配:检查当前段落文本是否在 AI 识别的标题列表中 + // 注意:这里使用 Contains 模糊匹配,因为 Word 段落可能包含额外的隐藏字符 var match = headings.FirstOrDefault(h => pText.Contains(h.text)); if (match != null) { + // 是标题:设置对应的 Word 内置标题样式 Word.WdBuiltinStyle styleEnum = Word.WdBuiltinStyle.wdStyleNormal; switch (match.level) { @@ -638,7 +686,7 @@ namespace WordAddIn p.set_Style(styleEnum); - // 标题通常不需要首行缩进(或者由样式决定),这里显式清除手动缩进,以防万一 + // 标题通常不需要首行缩进,强制清除 p.Format.CharacterUnitFirstLineIndent = 0; p.Format.FirstLineIndent = 0; @@ -646,7 +694,8 @@ namespace WordAddIn } else { - // 正文段落:设置首行缩进2字符 + // 6. 非标题(正文):统一设置首行缩进 2 字符 + // 让文档看起来更规范 p.Format.CharacterUnitFirstLineIndent = 2; indentCount++; } @@ -673,6 +722,15 @@ namespace WordAddIn } } + /// + /// 【配图生成】按钮点击事件处理。 + /// 功能描述: + /// 1. 获取用户选中的文字作为绘图描述 (Prompt)。 + /// 2. 弹出图片风格设置窗体,让用户选择风格(如写实、卡通、水墨等)和补充要求。 + /// 3. 调用 AI 绘图接口 (DALL-E 3 / Gemini Image),获取生成图片的 URL。 + /// 4. 下载图片并插入到 Word 文档中。 + /// + /// 触发事件的 Ribbon 控件 public async void OnImageClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; @@ -688,7 +746,7 @@ namespace WordAddIn return; } - // 弹出图片风格设置窗体 + // 1. 弹出图片风格设置窗体 string style = null; string additionalPrompt = null; using (var imgOptions = new ImageOptionsForm()) @@ -701,7 +759,7 @@ namespace WordAddIn additionalPrompt = imgOptions.AdditionalPrompt; } - // 组合 Prompt + // 2. 组合 Prompt:原文 + 补充要求 if (!string.IsNullOrWhiteSpace(additionalPrompt)) { prompt += $"\n补充要求: {additionalPrompt}"; @@ -711,11 +769,13 @@ namespace WordAddIn loading.Show(); loading.Refresh(); - // 传入 style 参数 + // 3. 调用 AI 服务生成图片 URL + // 注意:生图通常比较慢,需要耐心等待 string imageUrl = await Task.Run(() => _aiService.GenerateImageUrl(prompt, style)); if (!string.IsNullOrEmpty(imageUrl)) { + // 4. 插入图片 InsertImageFromUrl(imageUrl); } else @@ -725,7 +785,7 @@ namespace WordAddIn } catch (Exception ex) { - MessageBox.Show("图片生成失败: " + ex.Message); + MessageBox.Show("配图生成失败: " + ex.Message); } finally { @@ -737,138 +797,182 @@ namespace WordAddIn } } + /// + /// 【创建大纲】按钮点击事件处理。 + /// 功能描述: + /// 1. 弹出模态对话框让用户输入大纲主题。 + /// 2. 调用 AI 服务生成结构化的 JSON 大纲数据。 + /// 3. 解析 JSON 数据,遍历大纲节点。 + /// 4. 将大纲内容插入 Word 文档,并根据层级应用预设的字体、字号、对齐方式等样式。 + /// + /// 触发事件的 Ribbon 控件 (由 VSTO 运行时传入) + public async void OnCreateOutlineClick(Office.IRibbonControl control) + { + // 1. 安全检查:确保用户已登录,否则无法使用 AI 功能 + if (!EnsureLoggedIn()) return; + + string topic = null; + + // 2. 弹出“创建大纲”输入窗体 + using (var form = new CreateOutlineForm()) + { + if (form.ShowDialog() != DialogResult.OK) return; // 用户点击取消 + topic = form.Topic; + } + + if (string.IsNullOrWhiteSpace(topic)) return; + + // 3. 显示加载提示,提升用户体验 + LoadingForm loading = new LoadingForm("AI 正在构思大纲结构..."); + loading.Show(); + loading.Refresh(); // 强制刷新 UI,确保 loading 窗口立即显示 + + try + { + // 4. 异步调用 AI 服务生成大纲 + // 使用 Task.Run 将耗时操作放入后台线程,避免阻塞 Word UI 主线程 + List outline = await Task.Run(() => _aiService.GenerateOutline(topic)); + + if (outline == null || outline.Count == 0) + { + MessageBox.Show("未能生成大纲,请重试。"); + return; + } + + // 5. 获取大纲样式配置 + // 从用户设置中读取 JSON 字符串并反序列化,如果为空则使用默认样式 + string styleJson = Settings.Default.OutlineStyles; + OutlineStyleConfig styleConfig = string.IsNullOrEmpty(styleJson) + ? new OutlineStyleConfig() + : JsonConvert.DeserializeObject(styleJson); + + var doc = Globals.ThisAddIn.Application.ActiveDocument; + var selection = Globals.ThisAddIn.Application.Selection; + Word.Range insertRange = selection.Range; + insertRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd); + + // 6. 遍历生成的大纲项,逐行插入文档 + foreach (var item in outline) + { + // 在当前光标位置添加新段落 + Word.Paragraph p = doc.Content.Paragraphs.Add(insertRange); + p.Range.Text = item.Title + "\n"; + + // 7. 根据层级应用对应的样式 (字体、大小、加粗、对齐) + TextStyle ts = null; + switch (item.Level) + { + case 0: // 主标题 + ts = styleConfig.MainTitle; + p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); + break; + case 1: // 一级标题 + ts = styleConfig.Heading1; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); + break; + case 2: // 二级标题 + ts = styleConfig.Heading2; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); + break; + case 3: // 三级标题 + ts = styleConfig.Heading3; + p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); + break; + default: + ts = styleConfig.Heading3; + break; + } + + // 应用自定义样式属性 + if (ts != null) + { + p.Range.Font.Name = ts.FontName; + p.Range.Font.Size = ts.FontSize; + p.Range.Font.Bold = ts.IsBold ? 1 : 0; + p.Range.Font.Color = Word.WdColor.wdColorBlack; // 强制设为黑色,避免出现默认蓝色的情况 + + if (item.Level == 0) // 仅主标题应用自定义对齐方式 + { + p.Format.Alignment = (Word.WdParagraphAlignment)ts.Alignment; + } + } + + // 更新插入位置到当前段落之后,准备插入下一行 + insertRange = p.Range; + insertRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd); + } + } + catch (Exception ex) + { + // 异常处理:捕获并显示错误信息,防止插件崩溃 + MessageBox.Show("生成大纲失败: " + ex.Message); + } + finally + { + // 8. 无论成功与否,都要关闭 Loading 窗口 + loading.CloseSafe(); + loading.Dispose(); + } + } + + /// + /// 【大纲配置】按钮点击事件处理。 + /// 打开大纲样式配置窗口,允许用户自定义各级标题的字体、字号等。 + /// + /// 触发事件的 Ribbon 控件 public void OnOutlineSettingClick(Office.IRibbonControl control) { if (!EnsureLoggedIn()) return; + using (var form = new OutlineSettingsForm()) { form.ShowDialog(); } } - public async void OnCreateOutlineClick(Office.IRibbonControl control) + /// + /// 从 URL 插入图片到 Word 文档中。 + /// 下载图片并保存为临时文件,然后插入到当前光标位置。 + /// + /// 图片的 URL 地址 + private void InsertImageFromUrl(string imageUrl) { - if (!EnsureLoggedIn()) return; - - string topic = null; - using (var form = new CreateOutlineForm()) - { - if (form.ShowDialog() != DialogResult.OK) return; - topic = form.Topic; - } - - LoadingForm loading = null; try { - loading = new LoadingForm("AI 正在生成大纲,请稍候..."); - loading.Show(); - loading.Refresh(); - - var outline = await Task.Run(() => _aiService.GenerateOutline(topic)); - - if (outline == null || outline.Count == 0) + string tempPath = Path.GetTempFileName() + ".png"; + using (WebClient client = new WebClient()) { - MessageBox.Show("生成大纲失败,请重试。"); - return; + client.DownloadFile(imageUrl, tempPath); } - // Load config - OutlineStyleConfig config = null; - try - { - string json = Settings.Default.OutlineStyles; - if (!string.IsNullOrEmpty(json)) - { - config = JsonConvert.DeserializeObject(json); - } - } - catch { } - if (config == null) config = new OutlineStyleConfig(); - var selection = Globals.ThisAddIn.Application.Selection; + var inlineShape = selection.InlineShapes.AddPicture(FileName: tempPath, LinkToFile: false, SaveWithDocument: true); - // Move to end of selection to avoid overwriting if just cursor - selection.Collapse(Word.WdCollapseDirection.wdCollapseEnd); - - foreach (var item in outline) + // 简单的图片调整 + inlineShape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue; + if (inlineShape.Width > 400) { - // Type text - selection.TypeText(item.Title); - - // Get current paragraph - Word.Paragraph p = selection.Paragraphs[1]; - - // 1. Apply Built-in Style - try - { - switch (item.Level) - { - case 0: p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); break; - case 1: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); break; - case 2: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); break; - case 3: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); break; - default: p.set_Style(Word.WdBuiltinStyle.wdStyleNormal); break; - } - } - catch { } - - // 2. Apply Custom Config - TextStyle style = null; - switch (item.Level) - { - case 0: style = config.MainTitle; break; - case 1: style = config.Heading1; break; - case 2: style = config.Heading2; break; - case 3: style = config.Heading3; break; - default: style = config.Heading3; break; - } - - if (style != null) - { - p.Range.Font.Name = style.FontName; - p.Range.Font.Size = style.FontSize; - p.Range.Font.Bold = style.IsBold ? 1 : 0; - p.Range.Font.Color = Word.WdColor.wdColorBlack; // 强制设置为黑色 - - if (style.Alignment == 1) p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter; - else if (style.Alignment == 2) p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight; - else p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft; - } - - // New line for next item - selection.TypeParagraph(); + inlineShape.Width = 400; // 限制最大宽度 } } catch (Exception ex) { - MessageBox.Show("创建大纲失败: " + ex.Message); - } - finally - { - if (loading != null) - { - loading.CloseSafe(); - loading.Dispose(); - } + MessageBox.Show("插入图片失败: " + ex.Message); } } - public void OnSettingsClick(Office.IRibbonControl control) - { - MessageBox.Show("请在代码 AiService.cs 中配置您的 API Key。", "设置"); - } - - #endregion - - #region 帮助器 - + /// + /// 确保用户已登录。 + /// 检查 _isLoggedIn 状态,如果未登录则弹出登录窗口。 + /// + /// 如果已登录或登录成功返回 true,否则返回 false private bool EnsureLoggedIn() { if (_isLoggedIn) return true; - using (var loginForm = new LoginForm()) + using (LoginForm login = new LoginForm()) { - if (loginForm.ShowDialog() == DialogResult.OK) + if (login.ShowDialog() == DialogResult.OK) { _isLoggedIn = true; return true; @@ -877,58 +981,12 @@ namespace WordAddIn return false; } - private void InsertImageFromUrl(string imageUrl) - { - string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".png"); - try - { - using (WebClient client = new WebClient()) - { - client.DownloadFile(imageUrl, tempFile); - } - - var selection = Globals.ThisAddIn.Application.Selection; - // 移动到选区末尾插入,避免覆盖文字 - selection.Collapse(Word.WdCollapseDirection.wdCollapseEnd); - selection.TypeParagraph(); // 将图片嵌入文档 - var inlineShape = selection.InlineShapes.AddPicture(tempFile, LinkToFile: false, SaveWithDocument: true); - - // 自动调整图片大小为页面宽度的 80% (留出一点边距更美观) - try - { - var pageSetup = selection.PageSetup; - float pageWidth = pageSetup.PageWidth; - float leftMargin = pageSetup.LeftMargin; - float rightMargin = pageSetup.RightMargin; - - // 有效内容宽度 - float contentWidth = pageWidth - leftMargin - rightMargin; - - // 设置图片宽度 (如果图片本身小于页面宽度,也可以选择不放大,这里强制设为 100% 宽度) - inlineShape.Width = contentWidth; - - // 保持纵横比 - inlineShape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue; - } - catch - { - // 如果获取页面设置失败,给一个默认值 (500磅,约17.6cm,接近A4宽度) - inlineShape.Width = 500; - } - } - catch (Exception ex) - { - throw new Exception("图片下载或插入失败: " + ex.Message); - } - finally - { - if (File.Exists(tempFile)) - { - try { File.Delete(tempFile); } catch { } - } - } - } - + /// + /// 从嵌入资源中获取文本内容。 + /// 用于读取 Ribbon XML 定义文件。 + /// + /// 资源名称 + /// 资源文本内容 private static string GetResourceText(string resourceName) { Assembly asm = Assembly.GetExecutingAssembly(); @@ -951,4 +1009,4 @@ namespace WordAddIn #endregion } -} +} \ No newline at end of file diff --git a/WordAddIn/WordAddIn/AiService.cs b/WordAddIn/WordAddIn/AiService.cs index ac24edb..75d0a3b 100644 --- a/WordAddIn/WordAddIn/AiService.cs +++ b/WordAddIn/WordAddIn/AiService.cs @@ -11,36 +11,55 @@ using WordAddIn.Models; namespace WordAddIn { + /// + /// AI 服务核心类。 + /// 职责: + /// 1. 封装与后端大模型 API (如 Gemini, OpenAI) 的 HTTP 通信。 + /// 2. 构造 System Prompt 和 User Prompt,处理上下文注入。 + /// 3. 解析 API 返回的 JSON 数据,并处理异常。 + /// 4. 提供具体的业务接口:扩写、润色、升华、大纲生成、结构分析、配图生成。 + /// public class AiService { - // 兔子平台配置 + // 兔子平台配置 (兼容 OpenAI 接口格式) private const string ApiKey = "sk-FCwlaMANKdSlXlY7HkzZncSp0N5gecfpQdk0iR059Hfk4dQ1"; private const string BaseUrl = "https://api.tu-zi.com/v1"; // 模型配置 - private const string TextModel = "gemini-3-flash-preview"; - private const string ImageModel = "gemini-3-pro-image-preview"; + private const string TextModel = "gemini-3-flash-preview"; // 默认文本模型,速度快,成本低 + private const string ImageModel = "gemini-3-pro-image-preview"; // 默认生图模型,支持高质量绘图 private static readonly HttpClient _httpClient; static AiService() { - // 必须强制启用 TLS 1.2,否则无法连接现代 HTTPS 站点 + // 关键设置:必须强制启用 TLS 1.2,否则在 .NET Framework 4.7.2 下无法连接现代 HTTPS 站点 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; _httpClient = new HttpClient(); - _httpClient.Timeout = TimeSpan.FromSeconds(60); + _httpClient.Timeout = TimeSpan.FromSeconds(60); // 设置 60 秒超时,防止长文本生成时连接断开 _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}"); } + /// + /// 智能扩写:将简短的文本扩充为细节丰富、逻辑通顺的段落。 + /// + /// 需要扩写的原文 + /// 用户自定义的扩写要求(如“更幽默一点”、“强调数据支持”等),可选 + /// 文档全文上下文(摘要、风格等),用于保持文风一致,可选 + /// 扩写后的纯文本内容 public async Task ExpandText(string originalText, string customInstruction = null, string documentContext = null) { + // 构造 User 指令 string instruction = string.IsNullOrWhiteSpace(customInstruction) ? "请将以下内容扩写成一段正式、通顺的段落,保持原意但增加细节" : customInstruction; + // 构造 System Prompt + // 强制要求不使用 Markdown,方便直接插入 Word string systemPrompt = "你是一个专业的文档编辑助手。请直接输出扩写后的纯文本内容,不要使用任何Markdown标记(如#、**、- 等)。直接使用自然段落。"; + // 注入文档上下文(如果有) if (!string.IsNullOrWhiteSpace(documentContext)) { systemPrompt += $"\n\n【全文背景与排版规则】:你已通读全文,以下是文档的整体上下文信息和排版规则,请在扩写时保持与全文主题和风格的一致性,并参考排版规则生成对应的层级结构:\n{documentContext}"; @@ -55,6 +74,12 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 润色加工:优化文本的语言表达,提升流畅度和专业性。 + /// + /// 原文 + /// 文档全文上下文 + /// 润色后的文本 public async Task PolishText(string originalText, string documentContext = null) { string systemPrompt = "你是一个专业的文字编辑和润色专家。请对用户提供的文本进行深度润色和梳理。目标是:1. 提升语言的流畅度和专业性;2. 修正语法错误和用词不当;3. 优化逻辑结构,使表达更清晰;4. 保持原意不变。请直接输出润色后的内容,不要包含任何解释或客套话。请直接输出纯文本,不要使用任何Markdown标记。"; @@ -73,6 +98,12 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 深度升华:挖掘文本思想深度,提升价值和高度。 + /// + /// 原文 + /// 文档全文上下文 + /// 升华后的文本 public async Task ElevateText(string originalText, string documentContext = null) { string systemPrompt = "你是一个资深的领域专家和思想家。用户提供了一段文本,请你根据自己的广博知识和深刻理解,对其进行深度升华。目标是:1. 挖掘深度:透过现象看本质,补充更有见地的分析;2. 拓展广度:引入相关的行业背景、前沿趋势或跨学科视角;3. 提升价值:将普通的描述提升为具有指导意义或启发性的洞见;4. 高于原文:不要局限于原文的字面意思,要大胆进行思想层面的拔高。请直接输出升华后的纯文本内容,保持逻辑连贯,不要使用任何Markdown标记。"; @@ -91,6 +122,14 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 素材写作:根据参考资料生成新的内容。 + /// + /// 目标文本/上下文 + /// 参考资料内容 + /// 字数限制 + /// 文档全文上下文 + /// 生成的内容 public async Task GenerateWithReference(string originalText, string referenceContent, int wordLimit, string documentContext = null) { string systemPrompt = "你是一个专业的文档写作助手。请根据提供的参考资料,撰写或重写目标文本。请直接输出纯文本内容,不要使用任何Markdown标记。"; @@ -131,6 +170,16 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 生成大纲:根据主题生成结构化的 JSON 大纲。 + /// 功能描述: + /// 1. 构造 Prompt,要求 AI 返回特定格式的 JSON 数组 (OutlineItem)。 + /// 2. 调用 AI 接口。 + /// 3. 清理 AI 可能返回的 Markdown 代码块标记 (```json ... ```)。 + /// 4. 反序列化 JSON 为对象列表。 + /// + /// 大纲主题或简要描述 + /// 大纲项列表 (包含标题和层级) public async Task> GenerateOutline(string topic) { string systemPrompt = @"你是一个专业的文档大纲生成助手。请根据用户提供的主题或描述,生成一个结构化的文档大纲。 @@ -153,6 +202,7 @@ namespace WordAddIn string jsonResponse = await GetChatCompletion(messages); // 清理可能存在的 Markdown 代码块标记 + // 某些模型即使被要求不返回 Markdown,仍可能习惯性地加上 ```json jsonResponse = jsonResponse.Replace("```json", "").Replace("```", "").Trim(); try @@ -162,10 +212,21 @@ namespace WordAddIn catch (Exception ex) { Console.WriteLine("JSON Parsing Failed: " + ex.Message); + // 这里可以考虑返回一个空列表,或者抛出异常让上层处理 + // 目前选择返回空列表,由 UI 层提示失败 return new List(); } } + /// + /// 结构分析:分析文档文本,提取标题及其层级结构。 + /// 功能描述: + /// 1. 发送文档内容给 AI。 + /// 2. AI 识别出所有的标题行及其层级 (1-5)。 + /// 3. 返回 JSON 格式的标题列表,供格式美化功能使用。 + /// + /// 文档全文内容 (或截断后的部分内容) + /// 标题信息列表 (包含标题文本和层级) public async Task> AnalyzeStructure(string text) { string systemPrompt = @"你是一个文档格式专家。请分析用户提供的文档内容,识别出所有的标题及其对应的层级(1级、2级、3级...)。 @@ -202,6 +263,11 @@ namespace WordAddIn public int level { get; set; } } + /// + /// 通读全文:分析文档全文,生成摘要和风格画像。 + /// + /// 全文内容 + /// 文档画像描述 public async Task AnalyzeDocument(string fullText) { var messages = new List @@ -213,6 +279,9 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 格式化文本(保留接口,目前逻辑主要在 AnalyzeStructure 中实现)。 + /// public async Task FormatText(string originalText) { var messages = new List @@ -224,19 +293,30 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 生成图片 URL。 + /// 流程: + /// 1. 优化提示词:将用户简单的中文描述转换为详细的英文绘图 Prompt。 + /// 2. 调用生图 API (如 DALL-E 3 或 Gemini Image)。 + /// 3. 解析 API 响应,提取图片 URL。 + /// + /// 用户的原始描述 + /// 图片风格 (如"水墨画", "赛博朋克"等) + /// 生成的图片 URL (有效期通常较短) public async Task GenerateImageUrl(string prompt, string style = null) { try { // 1. 优化 Prompt:将用户输入转换为安全的英文绘图 Prompt,并注入风格 + // 这一步对于生成高质量图片至关重要,因为绘图模型对英文 Prompt 的理解通常更好 string optimizedPrompt = await OptimizePromptForImage(prompt, style); - // 使用匿名对象以获得最大灵活性 + // 使用匿名对象构造请求体,以获得最大灵活性 var requestBody = new { prompt = optimizedPrompt, model = ImageModel, - size = "1024x1024", + size = "1024x1024", // 标准分辨率 response_format = "url" // 显式请求 URL 格式 }; @@ -251,10 +331,10 @@ namespace WordAddIn throw new Exception($"API Error ({response.StatusCode}): {responseString}"); } - // 使用 JObject 动态解析 + // 使用 JObject 动态解析响应,以兼容不同厂商的 API 格式差异 var jsonResponse = JObject.Parse(responseString); - // 1. 尝试解析标准格式: data[0].url + // 1. 尝试解析 OpenAI 标准格式: data[0].url var data = jsonResponse["data"]; if (data != null) { @@ -268,8 +348,6 @@ namespace WordAddIn if (firstItem["revised_prompt"] != null && firstItem["revised_prompt"].ToString() == "NO_IMAGE") { // 尝试从其他字段找,或者这就是个错误 - // 如果是 NO_IMAGE,通常意味着内容安全拦截或生成失败 - // 但这里我们先假设它可能在别的字段 } // 某些 API 可能直接在数组里放 URL 字符串 @@ -282,7 +360,7 @@ namespace WordAddIn } } - // 3. 尝试解析根节点 url + // 3. 尝试解析根节点 url (部分非标准 API) if (jsonResponse["url"] != null) { return jsonResponse["url"].ToString(); @@ -296,6 +374,10 @@ namespace WordAddIn } } + /// + /// 优化图片生成提示词。 + /// 将用户的中文描述翻译成英文,并加入风格指令。 + /// private async Task OptimizePromptForImage(string originalPrompt, string style = null) { string styleInstruction = string.IsNullOrEmpty(style) ? "" : $"6. 图片风格必须严格遵循:{style}。"; @@ -309,6 +391,9 @@ namespace WordAddIn return await GetChatCompletion(messages); } + /// + /// 通用聊天补全请求方法。 + /// private async Task GetChatCompletion(List messages) { try diff --git a/WordAddIn/WordAddIn/ExpandOptionsForm.cs b/WordAddIn/WordAddIn/ExpandOptionsForm.cs index d149a13..2501bf4 100644 --- a/WordAddIn/WordAddIn/ExpandOptionsForm.cs +++ b/WordAddIn/WordAddIn/ExpandOptionsForm.cs @@ -5,8 +5,18 @@ using System.Windows.Forms; namespace WordAddIn { + /// + /// 智能扩写设置窗体。 + /// 功能描述: + /// 1. 提供多种预设的扩写模板(如"商务正式"、"细腻情感"等)。 + /// 2. 允许用户修改预设模板或输入完全自定义的扩写要求。 + /// 3. 返回用户的最终选择 (SelectedPrompt) 给调用方。 + /// public class ExpandOptionsForm : Form { + /// + /// 用户最终确认的扩写要求。 + /// public string SelectedPrompt { get; private set; } private ComboBox cmbTemplates; @@ -32,6 +42,9 @@ namespace WordAddIn InitializeTemplates(); } + /// + /// 初始化窗体组件。 + /// private void InitializeComponent() { this.lblTemplate = new System.Windows.Forms.Label(); @@ -124,6 +137,9 @@ namespace WordAddIn } + /// + /// 初始化模板下拉列表。 + /// private void InitializeTemplates() { foreach (var key in _templates.Keys) @@ -136,6 +152,10 @@ namespace WordAddIn } } + /// + /// 模板选择改变事件。 + /// 当用户选择不同模板时,自动填充对应的 Prompt 到文本框中。 + /// private void CmbTemplates_SelectedIndexChanged(object sender, EventArgs e) { if (cmbTemplates.SelectedItem != null && _templates.ContainsKey(cmbTemplates.SelectedItem.ToString())) @@ -144,6 +164,10 @@ namespace WordAddIn } } + /// + /// 确定按钮点击事件。 + /// 验证输入有效性,并保存结果。 + /// private void BtnOk_Click(object sender, EventArgs e) { SelectedPrompt = txtCustomPrompt.Text.Trim(); diff --git a/WordAddIn/WordAddIn/ImageOptionsForm.cs b/WordAddIn/WordAddIn/ImageOptionsForm.cs index 6d299a2..0f8a645 100644 --- a/WordAddIn/WordAddIn/ImageOptionsForm.cs +++ b/WordAddIn/WordAddIn/ImageOptionsForm.cs @@ -6,9 +6,23 @@ using WordAddIn.Properties; namespace WordAddIn { + /// + /// 生成配图设置窗体。 + /// 功能描述: + /// 1. 允许用户选择图片风格(如写实、卡通、3D渲染等)。 + /// 2. 允许用户输入额外的描述(如颜色、背景要求等)。 + /// 3. 支持保存上次选择的风格,提升用户体验。 + /// public class ImageOptionsForm : Form { + /// + /// 用户选择的图片风格。 + /// public string SelectedStyle { get; private set; } + + /// + /// 用户输入的补充描述。 + /// public string AdditionalPrompt { get; private set; } private ComboBox cmbStyles; @@ -36,6 +50,9 @@ namespace WordAddIn InitializeStyles(); } + /// + /// 初始化窗体组件。 + /// private void InitializeComponent() { this.lblStyle = new System.Windows.Forms.Label(); @@ -127,6 +144,9 @@ namespace WordAddIn } + /// + /// 初始化风格列表,并尝试加载上次保存的风格。 + /// private void InitializeStyles() { cmbStyles.Items.AddRange(_styles); @@ -143,6 +163,10 @@ namespace WordAddIn } } + /// + /// 确定按钮点击事件。 + /// 获取用户选择,并保存风格到 Settings。 + /// private void BtnOk_Click(object sender, EventArgs e) { SelectedStyle = cmbStyles.SelectedItem.ToString(); diff --git a/WordAddIn/WordAddIn/LoadingForm.cs b/WordAddIn/WordAddIn/LoadingForm.cs index 2b81631..a9ba344 100644 --- a/WordAddIn/WordAddIn/LoadingForm.cs +++ b/WordAddIn/WordAddIn/LoadingForm.cs @@ -5,6 +5,13 @@ using System.Windows.Forms; namespace WordAddIn { + /// + /// 加载动画窗体。 + /// 功能描述: + /// 1. 在执行耗时操作(如 AI 生成)时显示,阻断用户界面。 + /// 2. 使用 GDI+ 自定义绘制旋转动画 (Spinner)。 + /// 3. 支持跨线程安全关闭。 + /// public class LoadingForm : Form { private System.Windows.Forms.Label lblMessage; @@ -18,6 +25,10 @@ namespace WordAddIn private readonly Color _backgroundColor = Color.White; private readonly Color _borderColor = Color.FromArgb(100, 100, 100); + /// + /// 构造函数。 + /// + /// 加载时显示的提示文字 public LoadingForm(string message = "AI 正在思考中...") { InitializeComponent(); @@ -28,6 +39,9 @@ namespace WordAddIn this.UpdateStyles(); } + /// + /// 初始化窗体组件。 + /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); @@ -71,12 +85,18 @@ namespace WordAddIn } + /// + /// 动画定时器触发事件。更新旋转角度并重绘。 + /// private void AnimationTimer_Tick(object sender, EventArgs e) { _spinnerAngle = (_spinnerAngle + 10) % 360; this.Invalidate(); // 触发重绘 } + /// + /// 重写绘制事件,自定义绘制加载动画。 + /// protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); @@ -127,7 +147,10 @@ namespace WordAddIn g.ResetTransform(); } - // 允许在非 UI 线程关闭窗体 + /// + /// 线程安全的关闭方法。 + /// 允许从非 UI 线程调用。 + /// public void CloseSafe() { if (this.IsDisposed) return; @@ -143,7 +166,9 @@ namespace WordAddIn } } - // 允许拖动窗体 + /// + /// 支持鼠标拖动无边框窗体。 + /// protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); diff --git a/WordAddIn/WordAddIn/LoginForm.cs b/WordAddIn/WordAddIn/LoginForm.cs index 0845519..6ec18d2 100644 --- a/WordAddIn/WordAddIn/LoginForm.cs +++ b/WordAddIn/WordAddIn/LoginForm.cs @@ -4,6 +4,13 @@ using System.Windows.Forms; namespace WordAddIn { + /// + /// 用户登录窗体。 + /// 功能描述: + /// 1. 模拟用户登录验证过程。 + /// 2. 提供用户名和密码输入框。 + /// 3. 目前使用硬编码的凭证 (huanghai / 12345678) 进行验证,实际生产中应连接认证服务。 + /// public class LoginForm : Form { private TextBox txtUsername; @@ -19,6 +26,9 @@ namespace WordAddIn InitializeComponent(); } + /// + /// 初始化窗体组件。 + /// private void InitializeComponent() { this.lblTitle = new System.Windows.Forms.Label(); @@ -135,6 +145,10 @@ namespace WordAddIn } + /// + /// 登录按钮点击事件。 + /// 验证用户名和密码。 + /// private void BtnLogin_Click(object sender, EventArgs e) { string user = txtUsername.Text.Trim(); diff --git a/WordAddIn/WordAddIn/Models.cs b/WordAddIn/WordAddIn/Models.cs index aab1d77..73088ab 100644 --- a/WordAddIn/WordAddIn/Models.cs +++ b/WordAddIn/WordAddIn/Models.cs @@ -3,70 +3,208 @@ using System.Collections.Generic; namespace WordAddIn.Models { - // Chat Completion Models + // ========================================== + // Chat Completion API 模型定义 + // 用于与 OpenAI / Gemini 兼容的聊天接口交互 + // ========================================== + + /// + /// 聊天补全请求对象 + /// public class ChatCompletionRequest { + /// + /// 模型名称 (e.g., "gpt-3.5-turbo", "gemini-3-flash") + /// public string model { get; set; } = "gpt-3.5-turbo"; + + /// + /// 消息列表,包含 system, user, assistant 等角色的消息 + /// public List messages { get; set; } + + /// + /// 温度参数,控制生成的随机性 (0.0 - 1.0) + /// 较高值使输出更随机,较低值使输出更确定 + /// public double temperature { get; set; } = 0.7; } + /// + /// 聊天消息对象 + /// public class Message { + /// + /// 角色: system, user, assistant + /// public string role { get; set; } + + /// + /// 消息内容 + /// public string content { get; set; } } + /// + /// 聊天补全响应对象 + /// public class ChatCompletionResponse { + /// + /// 响应 ID + /// public string id { get; set; } + + /// + /// 生成的选择列表 (通常只取第一个) + /// public List choices { get; set; } } + /// + /// 生成结果选项 + /// public class Choice { + /// + /// 生成的消息内容 + /// public Message message { get; set; } } - // Image Generation Models + // ========================================== + // Image Generation API 模型定义 + // 用于与 DALL-E / Gemini Image 接口交互 + // ========================================== + + /// + /// 图片生成请求对象 + /// public class ImageGenerationRequest { + /// + /// 模型名称 + /// public string model { get; set; } = "dall-e-3"; + + /// + /// 绘图提示词 + /// public string prompt { get; set; } + + /// + /// 生成数量 (DALL-E 3 通常限制为 1) + /// public int n { get; set; } = 1; + + /// + /// 图片尺寸 (e.g., "1024x1024") + /// public string size { get; set; } = "1024x1024"; } + /// + /// 图片生成响应对象 + /// public class ImageGenerationResponse { + /// + /// 生成的数据列表 + /// public List data { get; set; } } + /// + /// 图片数据项 + /// public class ImageItem { + /// + /// 图片 URL + /// public string url { get; set; } } - // Outline Models + // ========================================== + // 业务逻辑模型定义 + // 用于大纲生成和样式配置 + // ========================================== + + /// + /// 大纲条目 + /// 用于接收 AI 生成的结构化大纲数据 + /// public class OutlineItem { + /// + /// 标题文本 + /// public string Title { get; set; } - public int Level { get; set; } // 0=MainTitle, 1=H1, 2=H2, 3=H3 + + /// + /// 标题层级 + /// 0: 主标题 + /// 1: 一级标题 + /// 2: 二级标题 + /// 3: 三级标题 + /// + public int Level { get; set; } } + /// + /// 大纲样式配置集合 + /// 存储各级标题的样式设置 + /// public class OutlineStyleConfig { - public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // Center + /// + /// 主标题样式 + /// + public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // 默认为居中 + + /// + /// 一级标题样式 + /// public TextStyle Heading1 { get; set; } = new TextStyle { FontSize = 16, IsBold = true, FontName = "黑体" }; + + /// + /// 二级标题样式 + /// public TextStyle Heading2 { get; set; } = new TextStyle { FontSize = 14, IsBold = true, FontName = "黑体" }; + + /// + /// 三级标题样式 + /// public TextStyle Heading3 { get; set; } = new TextStyle { FontSize = 12, IsBold = true, FontName = "宋体" }; } + /// + /// 文本样式定义 + /// public class TextStyle { + /// + /// 字体名称 + /// public string FontName { get; set; } = "宋体"; + + /// + /// 字号 (磅值) + /// public float FontSize { get; set; } = 12; + + /// + /// 是否加粗 + /// public bool IsBold { get; set; } = false; - public int Alignment { get; set; } = 0; // 0=Left, 1=Center, 2=Right + + /// + /// 对齐方式 + /// 0: 左对齐 + /// 1: 居中 + /// 2: 右对齐 + /// + public int Alignment { get; set; } = 0; } } diff --git a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs index b8ca892..1f386a0 100644 --- a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs +++ b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs @@ -5,6 +5,13 @@ using System.IO; namespace WordAddIn { + /// + /// 素材写作(参考生成)配置窗体。 + /// 功能描述: + /// 1. 允许用户提供参考素材,用于辅助 AI 生成内容。 + /// 2. 支持两种输入模式:上传文件 (Word/Txt) 或 直接输入文本。 + /// 3. 允许用户设置生成内容的字数上限。 + /// public class ReferenceGeneratorForm : Form { private TabControl tabControl; @@ -20,9 +27,24 @@ namespace WordAddIn private Label lblWordCount; private Label lblWordUnit; + /// + /// 获取直接输入的参考文本内容。 + /// public string ReferenceContent { get; private set; } + + /// + /// 获取用户设置的字数上限。 + /// public int WordLimit { get; private set; } + + /// + /// 指示是否选择了文件模式。 + /// public bool IsFileMode { get; private set; } + + /// + /// 获取用户选择的文件路径。 + /// public string SelectedFilePath { get; private set; } public ReferenceGeneratorForm() @@ -30,6 +52,9 @@ namespace WordAddIn InitializeComponent(); } + /// + /// 初始化窗体组件。 + /// private void InitializeComponent() { this.tabControl = new System.Windows.Forms.TabControl(); @@ -235,6 +260,10 @@ namespace WordAddIn } + /// + /// 浏览按钮点击事件。 + /// 打开文件选择对话框,选择参考文档。 + /// private void BtnBrowse_Click(object sender, EventArgs e) { using (OpenFileDialog ofd = new OpenFileDialog()) @@ -248,6 +277,10 @@ namespace WordAddIn } } + /// + /// 生成按钮点击事件。 + /// 验证输入有效性,并保存用户的选择。 + /// private void BtnGenerate_Click(object sender, EventArgs e) { WordLimit = (int)numWordCount.Value; diff --git a/WordAddIn/WordAddIn/ThisAddIn.cs b/WordAddIn/WordAddIn/ThisAddIn.cs index 7840883..cf97ac2 100644 --- a/WordAddIn/WordAddIn/ThisAddIn.cs +++ b/WordAddIn/WordAddIn/ThisAddIn.cs @@ -1,7 +1,13 @@ -namespace WordAddIn +namespace WordAddIn { public partial class ThisAddIn { + /// + /// 覆盖 CreateRibbonExtensibilityObject 方法。 + /// 返回我们自定义的 Ribbon 实例 (AiRibbon)。 + /// VSTO 在加载时会调用此方法来获取 Ribbon XML 和回调处理程序。 + /// + /// IRibbonExtensibility 接口实现 protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject() { return new AiRibbon(); @@ -9,10 +15,12 @@ private void ThisAddIn_Startup(object sender, System.EventArgs e) { + // 在此添加初始化代码 } private void ThisAddIn_Shutdown(object sender, System.EventArgs e) { + // 在此添加清理代码 } #region VSTO 生成的代码 diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll index ca6626f..bc85c99 100644 Binary files a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll and b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll differ diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest index 555d244..8c51ce4 100644 --- a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest +++ b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest @@ -166,14 +166,14 @@ - + - kaNoiBD64kA337KwwCyQswknaKN/Jm2xVBMG2u/zfjM= + 57KGd7UXAGzFPWql99dRDRDZXoe2StLuM7Uyd52MAcU= @@ -200,4 +200,4 @@ -gCkchTa6A61vV45e2JsTggjhXU+MDFhh5zWDEljDxY4=KT7wTPexe3IeF5T7gn7yChiy5gXySA8+kakdFMgJMYZy1G0xI5ojr1bKiB4m9l0kPeNjDMhD7NSSpUOhyyT57CsjriDL2JN6pzRIItHEfIyUz5kwUkQoPDpebaVMWeXxIy0fbrT691Z7c1AC2BSG3uVwq487YB1CqqzufpclDqo=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorrIvGDs7aJWd5U9eWWhzLMnDB7NI0COaBGFP9BeGNrj8=ExruAcyG+I6xR7ACNNi1O9NI+7HWanTyJDMNa4Dh6G61sVw1AOOkfrrKJlGEy43t/XpubZu90wviQu7/Fkf/02rrYxUjhmP6IyCiMQLuTkDPm01+ExgWzIxjufW0lOYEsr5KvXJsLMKKKx4qAYoz+hd3tIzB01lwKqvSwkXP1sY=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file +Q59Lub3/7zQPl9a5u6WlazdugvzYLbuMA2ScUeG2Kgc=e+fTQJmSYA2Ba8atHBYYQHQ3yumSSKoHkFWQhSnBCeAJ3fJdgAe1BT50EjnkKWP6bgL7txi2fDWiqXH7PomA+4D+t9Px5E2mRjSn6TocihjON9kFjatapE5bYrXX4rxZmsa7baDmtRolKjErgeei1cgkLG+00xgJkUDvKwgLaZM=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorBowY9B2/AdRMLaAmKgcs3z/FtyzUeliTzB6MSGgWv2w=m1b3Quuv79UlTgmBro71Cl0hJmhy+RrFxAwWuFPtCzZmP7iI7KNCfbMMwSWufjzNYW/VF9ShKMk5nWLC1Q7iujoTWIYyUuiIDawYYrTyK4gO3QgQjE06b5DoluCNz4gENwYt2WJvNGRewiLCIEnDZhpAm/BaMJkSbYAkD22ASIY=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb index cd59581..bcfbd05 100644 Binary files a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb and b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb differ diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto index fed3e92..751f28c 100644 --- a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto +++ b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto @@ -14,8 +14,8 @@ - PPmVr+YsqgTyziCPQl2d+8CBQOBV0DzcZLxUnUr5dGA= + kQ6lvB1J+RPqWmN3TV1FKCJZwSRY6p4lkcGyY6yVSYM= -gmWu531mSYE6e+478tWijNz+HJGL6Ub2q/oN99Yoq8g=S6nctbanxh4cDuBGhiBntmVFtWyc/iVYn8rSFOXQEh7EgUG7v9FMK2qtYi6up36d5NWKvuV+kzKs5uOuUIg1P/hBWLVaRLzQSUQC9vekWmdUcrYjuQGrN7eqfJSK/H9QA1W9pbJ8KEDHQB82v9YQGVLhCn5Q3AxGlAWmaKQETZM=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorBUiNKpXwHd6U/7xnyou3wQDJxj8V+19+IoADYwPgppY=a2Wc9bjZ7YYsiAqAYwyYkYVJWXxl68YxMoNuB4DYMbo9uBiuMK+fMHN4ko9mqesTW3u7cxiI+HCZgPrEfG7yJdHo67+qdoXFThYYCvD0kCRLPM2s44twfV4pFtiGn5NZtoltrmevUR6pS9VFUAiYtKTygKHsU7CjXfa3UfTUV3c=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file +nO4LOJBDcGxL8l8R9b9+yIX5TXJ4Ym9VOMcF/Sry+qw=ZXK46Htr5iGt1kgI+VOKE9t9CPf3Xfkp16PHJxaK38qkTVXu/oylz4UUYGk/Tf1JuEkuQq06zN6CYCma0rwL4Gyoo/5n09Xzg3zmriPEIS2cs8BPbXwH90FbQ0HDaPVM+dJ9A8c+KTH9h6zNm8aj/HQdBRamLUv1D2PBxk7NE1s=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\Administrator9TyNXNxi0FyDrOMgoJ3zDQERhOeW01TflRltAh+X1k8=DHwRL4z0MQqHXYXDBnMyS39H5LqOPVJmaNB+zmQ+YoMUdVQojZBKuqSvpw3WvvWLhmP5dAH92Q94xrTd0gGggHPmL2r5/puOuDzYIhrvSXhE5dVoUwv8GW5Lem+FjDcAYn6TFic2CEkvwOE9FKEfT92ijcaa4K8AEWPm8X64EKg=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q== \ No newline at end of file diff --git a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll index ca6626f..bc85c99 100644 Binary files a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll and b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll differ diff --git a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb index cd59581..bcfbd05 100644 Binary files a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb and b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb differ