diff --git a/Better-EN5/Better-EN5.csproj b/Better-EN5/Better-EN5.csproj
index 03a94a7..bdc09d8 100644
--- a/Better-EN5/Better-EN5.csproj
+++ b/Better-EN5/Better-EN5.csproj
@@ -7,20 +7,26 @@
enable
BetterEN5
Better-EN5
- 1.1.2
- 1.1.0.0
- 1.1.2.0
+ 1.2.0
+ 1.2.0.0
+ 1.2.0.0
雾启工作室
雾启工作室
雾启工作室
- Better-Seewo 增强插件
- 雾生万象,启以为光 — 希沃白板功能增强插件
+ Better-Seewo
+ 雾生万象,启以为光 — Better-Seewo 插件
all
+ BS-Installer
-
+
+
+
+
+
+
diff --git a/Better-EN5/InkAutoSaveService.cs b/Better-EN5/InkAutoSaveService.cs
new file mode 100644
index 0000000..af6d9e8
--- /dev/null
+++ b/Better-EN5/InkAutoSaveService.cs
@@ -0,0 +1,85 @@
+using System;
+using System.IO;
+using System.Text.Json;
+using System.Threading.Tasks;
+using BetterEN5.Services;
+
+namespace BetterEN5
+{
+ public class InkAutoSaveService
+ {
+ private static readonly string SettingsPath = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "BetterEN5", "ink-settings.json");
+
+ public async Task StartAsync()
+ {
+ while (true)
+ {
+ try
+ {
+ var settings = LoadSettings();
+ if (settings != null && settings.AutoSaveEnabled)
+ {
+ var dir = settings.SaveDirectory;
+ if (!string.IsNullOrEmpty(dir))
+ {
+ if (!Directory.Exists(dir))
+ Directory.CreateDirectory(dir);
+ var timestamp = DateTime.Now.ToString("yyyyMMdd-HHmmss");
+ var filePath = Path.Combine(dir, $"ink-auto-{timestamp}.ink.json");
+ var inkService = new InkService();
+ await inkService.ExportInkAsync(filePath);
+
+ var retentionMinutes = settings.AutoSaveIntervalMinutes * 2;
+ CleanOldFiles(dir, TimeSpan.FromMinutes(retentionMinutes > 0 ? retentionMinutes : 10));
+ }
+ }
+ }
+ catch { }
+
+ var intervalMinutes = LoadSettings()?.AutoSaveIntervalMinutes ?? 5;
+ await Task.Delay(TimeSpan.FromMinutes(Math.Max(1, intervalMinutes)));
+ }
+ }
+
+ private static InkAutoSaveSettings? LoadSettings()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ return JsonSerializer.Deserialize(json);
+ }
+ }
+ catch { }
+ return null;
+ }
+
+ private static void CleanOldFiles(string dir, TimeSpan maxAge)
+ {
+ try
+ {
+ if (!Directory.Exists(dir)) return;
+ foreach (var file in Directory.GetFiles(dir, "ink-auto-*.ink.json"))
+ {
+ try
+ {
+ if (DateTime.Now - File.GetCreationTime(file) > maxAge)
+ File.Delete(file);
+ }
+ catch { }
+ }
+ }
+ catch { }
+ }
+ }
+
+ public class InkAutoSaveSettings
+ {
+ public bool AutoSaveEnabled { get; set; }
+ public int AutoSaveIntervalMinutes { get; set; } = 5;
+ public string SaveDirectory { get; set; } = "";
+ }
+}
diff --git a/Better-EN5/Program.cs b/Better-EN5/Program.cs
index 38f3963..d99895b 100644
--- a/Better-EN5/Program.cs
+++ b/Better-EN5/Program.cs
@@ -10,18 +10,16 @@ namespace BetterEN5
{
class Program : dotnetCampus.EasiPlugins.EasiPlugin
{
+ private InkAutoSaveService? _autoSaveService;
+
protected override Task OnRunningAsync()
{
if (!EN.CommandOptions.IsCloud)
{
if (EN.App.IsReady)
- {
Run();
- }
else
- {
EN.App.Ready += App_Ready;
- }
}
return Task.CompletedTask;
}
@@ -36,6 +34,7 @@ namespace BetterEN5
{
await Task.Delay(TimeSpan.FromSeconds(3));
ExportUIItems();
+ StartAutoSave();
}
private void ExportUIItems()
@@ -49,11 +48,18 @@ namespace BetterEN5
new UIItemLangInfo(new CultureInfo("en"), "Export Ink"),
});
+ manager.AppendWithLang(new BoardMenuImportInk(),
+ new UIItemAttribute(UIItemPurposes.BoardEditMenu), new[]
+ {
+ new UIItemLangInfo(new CultureInfo("zh-CHS"), "导入墨迹"),
+ new UIItemLangInfo(new CultureInfo("en"), "Import Ink"),
+ });
+
manager.AppendWithLang(new BoardMenuSettings(),
new UIItemAttribute(UIItemPurposes.BoardEditMenu), new[]
{
- new UIItemLangInfo(new CultureInfo("zh-CHS"), "Better-Seewo"),
- new UIItemLangInfo(new CultureInfo("en"), "Better-Seewo"),
+ new UIItemLangInfo(new CultureInfo("zh-CHS"), "Better-Seewo 管理"),
+ new UIItemLangInfo(new CultureInfo("en"), "Better-Seewo Manager"),
});
manager.AppendWithLang(new HeadToolBarSettings(),
@@ -63,5 +69,11 @@ namespace BetterEN5
new UIItemLangInfo(new CultureInfo("en"), "Better-Seewo"),
});
}
+
+ private void StartAutoSave()
+ {
+ _autoSaveService = new InkAutoSaveService();
+ _ = _autoSaveService.StartAsync();
+ }
}
}
diff --git a/Better-EN5/Services/CustomConversionService.cs b/Better-EN5/Services/CustomConversionService.cs
new file mode 100644
index 0000000..8b0c826
--- /dev/null
+++ b/Better-EN5/Services/CustomConversionService.cs
@@ -0,0 +1,539 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace BetterEN5.Services
+{
+ public class CustomConversionService : IConversionService
+ {
+ private static readonly XNamespace A = "http://schemas.openxmlformats.org/drawingml/2006/main";
+ private static readonly XNamespace P = "http://schemas.openxmlformats.org/presentationml/2006/main";
+ private static readonly XNamespace R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
+ private static readonly XNamespace Rel = "http://schemas.openxmlformats.org/package/2006/relationships";
+
+ public async Task ConvertPptxToEnbxAsync(string pptxPath)
+ {
+ if (!File.Exists(pptxPath))
+ throw new FileNotFoundException("PPT 文件未找到", pptxPath);
+
+ return await Task.Run(() =>
+ {
+ var enbxPath = Path.ChangeExtension(pptxPath, ".enbx");
+ using var pptx = System.IO.Packaging.Package.Open(pptxPath, FileMode.Open, FileAccess.Read);
+ using var enbx = System.IO.Packaging.Package.Open(enbxPath, FileMode.Create, FileAccess.ReadWrite);
+
+ var pptxPresentation = GetPptxPresentation(pptx);
+ var pptxSlideSize = GetSlideSize(pptxPresentation);
+ var slideWidth = pptxSlideSize.Item1;
+ var slideHeight = pptxSlideSize.Item2;
+
+ var enbxDoc = new XDocument();
+ var board = new XElement("DocumentStorageModel");
+
+ var slideIds = new List();
+ var slidesElement = new XElement("Slides");
+
+ var slideRels = GetPresentationSlideRels(pptx, pptxPresentation);
+ for (int i = 0; i < slideRels.Count; i++)
+ {
+ var slideId = $"slide_{i + 1}";
+ slideIds.Add(slideId);
+
+ var slidePart = pptx.GetPart(
+ System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri(slideRels[i].Item2, UriKind.Relative)));
+ var slideXml = XDocument.Load(slidePart.GetStream());
+
+ var slideElement = new XElement("SlideSaveInfo",
+ new XElement("Id", slideId),
+ new XElement("Width", slideWidth),
+ new XElement("Height", slideHeight));
+
+ var elementsElement = new XElement("Elements");
+ var spTree = slideXml.Root?.Element(P + "cSld")?.Element(P + "spTree");
+ if (spTree != null)
+ {
+ int elemIdx = 0;
+ foreach (var sp in spTree.Elements(P + "sp"))
+ {
+ var xfrm = sp.Element(P + "spPr")?.Element(A + "xfrm");
+ if (xfrm == null) continue;
+
+ var off = xfrm.Element(A + "off");
+ var ext = xfrm.Element(A + "ext");
+ if (off == null || ext == null) continue;
+
+ var x = (double)off.Attribute("x");
+ var y = (double)off.Attribute("y");
+ var w = (double)ext.Attribute("cx");
+ var h = (double)ext.Attribute("cy");
+
+ var nvSpPr = sp.Element(P + "nvSpPr");
+ var cNvSpPr = nvSpPr?.Element(P + "cNvSpPr");
+ var isTextBox = cNvSpPr?.Attribute("txBox") != null;
+ var txBody = sp.Element(P + "txBody");
+ var isTextShape = txBody != null;
+
+ if (isTextBox || isTextShape)
+ {
+ var elem = new XElement("TextSaveInfo",
+ new XElement("Id", $"elem_{++elemIdx}"),
+ new XElement("X", x),
+ new XElement("Y", y),
+ new XElement("Width", w),
+ new XElement("Height", h),
+ new XElement("Rotation", 0.0),
+ new XElement("Locked", false));
+
+ var bodyPr = txBody?.Element(A + "bodyPr");
+ var lIns = (double?)bodyPr?.Attribute("lIns") ?? 91440;
+ var rIns = (double?)bodyPr?.Attribute("rIns") ?? 91440;
+ var tIns = (double?)bodyPr?.Attribute("tIns") ?? 91440;
+ var bIns = (double?)bodyPr?.Attribute("bIns") ?? 91440;
+
+ var richText = new XElement("RichText",
+ new XElement("LeftMargin", lIns),
+ new XElement("RightMargin", rIns),
+ new XElement("TopMargin", tIns),
+ new XElement("BottomMargin", bIns));
+
+ if (txBody != null)
+ {
+ foreach (var para in txBody.Elements(A + "p"))
+ {
+ var pElem = new XElement("Paragraph");
+ foreach (var run in para.Elements(A + "r"))
+ {
+ var rPr = run.Element(A + "rPr");
+ var text = run.Element(A + "t")?.Value ?? "";
+
+ var rElem = new XElement("Run",
+ new XElement("Text", text));
+ if (rPr != null)
+ {
+ var fmt = new XElement("Formatting");
+ var sz = rPr.Attribute("sz");
+ if (sz != null) fmt.Add(new XElement("Size", (double)sz / 100.0));
+ var b = rPr.Attribute("b");
+ if (b != null) fmt.Add(new XElement("Bold", (string)b == "1"));
+ var italicAttr = rPr.Attribute("i");
+ if (italicAttr != null) fmt.Add(new XElement("Italic", (string)italicAttr == "1"));
+ var u = rPr.Attribute("u");
+ if (u != null) fmt.Add(new XElement("Underline", u.Value));
+ var srgbClr = rPr?.Element(A + "solidFill")?.Element(A + "srgbClr");
+ if (srgbClr != null) fmt.Add(new XElement("Color", (string)srgbClr.Attribute("val")));
+ var latin = rPr?.Element(A + "latin");
+ if (latin != null) fmt.Add(new XElement("Font", (string)latin.Attribute("typeface")));
+ if (fmt.HasElements) rElem.Add(fmt);
+ }
+ pElem.Add(rElem);
+ }
+ richText.Add(pElem);
+ }
+ }
+
+ elem.Add(richText);
+ elementsElement.Add(elem);
+ }
+ }
+ }
+
+ slideElement.Add(elementsElement);
+ slidesElement.Add(slideElement);
+ }
+
+ board.Add(new XElement("Board",
+ new XElement("SlideWidth", slideWidth),
+ new XElement("SlideHeight", slideHeight),
+ new XElement("SlideIds",
+ slideIds.Select(id => new XElement("string", id)))));
+ board.Add(slidesElement);
+ enbxDoc.Add(board);
+
+ var enbxPart = enbx.CreatePart(
+ System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/document.xml", UriKind.Relative)),
+ "application/xml");
+ using (var stream = enbxPart.GetStream())
+ enbxDoc.Save(stream);
+
+ enbx.Close();
+ pptx.Close();
+
+ return enbxPath;
+ });
+ }
+
+ public async Task ConvertEnbxToPptxAsync(string enbxPath)
+ {
+ if (!File.Exists(enbxPath))
+ throw new FileNotFoundException("ENBX 文件未找到", enbxPath);
+
+ return await Task.Run(() =>
+ {
+ var pptxPath = Path.ChangeExtension(enbxPath, ".pptx");
+
+ using var enbx = System.IO.Packaging.Package.Open(enbxPath, FileMode.Open, FileAccess.Read);
+ using var pptx = System.IO.Packaging.Package.Open(pptxPath, FileMode.Create, FileAccess.ReadWrite);
+
+ var enbxDocUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/document.xml", UriKind.Relative));
+ var enbxPart = enbx.GetPart(enbxDocUri);
+ var enbxDoc = XDocument.Load(enbxPart.GetStream());
+
+ var root = enbxDoc.Root;
+ if (root == null) throw new InvalidDataException("无效的 ENBX 文件");
+
+ var board = root.Element("Board");
+ var slideWidth = (double?)board?.Element("SlideWidth") ?? 12192000;
+ var slideHeight = (double?)board?.Element("SlideHeight") ?? 6858000;
+
+ var slidesEl = root.Element("Slides");
+ var slides = slidesEl?.Elements("SlideSaveInfo").ToList() ?? new List();
+
+ var contentTypesUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/[Content_Types].xml", UriKind.Relative));
+ var ctPart = pptx.CreatePart(contentTypesUri, "application/xml");
+ using (var sw = new StreamWriter(ctPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ for (int i = 0; i < slides.Count; i++)
+ sw.Write($"");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ var pptxPresentation = new XDocument(new XElement(P + "presentation",
+ new XAttribute(XNamespace.Xmlns + "a", A.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "r", R.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "p", P.NamespaceName),
+ new XElement(P + "sldMasterIdLst",
+ new XElement(P + "sldMasterId", new XAttribute("id", 2147483648), new XAttribute(R + "id", "rId1"))),
+ new XElement(P + "sldIdLst"),
+ new XElement(P + "sldSz", new XAttribute("cx", slideWidth), new XAttribute("cy", slideHeight)),
+ new XElement(P + "notesSz", new XAttribute("cx", 6858000), new XAttribute("cy", 9144000))));
+
+ var presXmlUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/presentation.xml", UriKind.Relative));
+ var presPart = pptx.CreatePart(presXmlUri, "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml");
+ var presRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/_rels/presentation.xml.rels", UriKind.Relative));
+ var presRelPart = pptx.CreatePart(presRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+
+ var presRelNs = XNamespace.Get(Rel.NamespaceName);
+ var presRels = new XDocument(new XElement(presRelNs + "Relationships",
+ new XElement(presRelNs + "Relationship", new XAttribute("Id", "rId1"),
+ new XAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster"),
+ new XAttribute("Target", "slideMasters/slideMaster1.xml"))));
+
+ var slideIdLst = pptxPresentation.Root?.Element(P + "sldIdLst");
+ var nextRelId = 2;
+ var slideRelTargets = new List();
+
+ for (int i = 0; i < slides.Count; i++)
+ {
+ var slideId = $"slide_{i + 1}";
+ var slideFileName = $"slides/slide{i + 1}.xml";
+ slideRelTargets.Add(slideFileName);
+
+ slideIdLst?.Add(new XElement(P + "sldId",
+ new XAttribute("id", 256 + i),
+ new XAttribute(R + "id", $"rId{nextRelId + i}")));
+
+ presRels.Root?.Add(new XElement("Relationship",
+ new XAttribute("Id", $"rId{nextRelId + i}"),
+ new XAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide"),
+ new XAttribute("Target", slideFileName)));
+
+ var slideDoc = new XDocument(new XElement(P + "sld",
+ new XAttribute(XNamespace.Xmlns + "a", A.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "r", R.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "p", P.NamespaceName)));
+
+ var cSld = new XElement(P + "cSld");
+ var spTree = new XElement(P + "spTree");
+
+ spTree.Add(new XElement(P + "nvGrpSpPr",
+ new XElement(P + "cNvPr", new XAttribute("id", 1), new XAttribute("name", "")),
+ new XElement(P + "cNvGrpSpPr")));
+ spTree.Add(new XElement(P + "grpSpPr",
+ new XElement(A + "xfrm",
+ new XElement(A + "off", new XAttribute("x", 0), new XAttribute("y", 0)),
+ new XElement(A + "ext", new XAttribute("cx", 0), new XAttribute("cy", 0)),
+ new XElement(A + "chOff", new XAttribute("x", 0), new XAttribute("y", 0)),
+ new XElement(A + "chExt", new XAttribute("cx", 0), new XAttribute("cy", 0)))));
+
+ var elements = slides[i].Element("Elements");
+ if (elements != null)
+ {
+ int shapeId = 2;
+ foreach (var elem in elements.Elements())
+ {
+ var elemName = elem.Name.LocalName;
+ var x = (double)elem.Element("X");
+ var y = (double)elem.Element("Y");
+ var w = (double)elem.Element("Width");
+ var h = (double)elem.Element("Height");
+ var rotation = (double?)elem.Element("Rotation") ?? 0.0;
+
+ if (elemName == "TextSaveInfo")
+ {
+ var sp = new XElement(P + "sp");
+ sp.Add(new XElement(P + "nvSpPr",
+ new XElement(P + "cNvPr", new XAttribute("id", shapeId++), new XAttribute("name", $"TextBox{shapeId - 1}")),
+ new XElement(P + "cNvSpPr", new XAttribute("txBox", 1)),
+ new XElement(P + "nvPr")));
+
+ var spPr = new XElement(P + "spPr",
+ new XElement(A + "xfrm",
+ new XElement(A + "off", new XAttribute("x", x), new XAttribute("y", y)),
+ new XElement(A + "ext", new XAttribute("cx", w), new XAttribute("cy", h))),
+ new XElement(A + "prstGeom", new XAttribute("prst", "rect"),
+ new XElement(A + "avLst")));
+ sp.Add(spPr);
+
+ var txBody = new XElement(P + "txBody",
+ new XElement(A + "bodyPr",
+ new XAttribute("wrap", "square")));
+
+ var richText = elem.Element("RichText");
+ if (richText != null)
+ {
+ var leftMargin = (double?)richText.Element("LeftMargin") ?? 91440;
+ var rightMargin = (double?)richText.Element("RightMargin") ?? 91440;
+ var topMargin = (double?)richText.Element("TopMargin") ?? 91440;
+ var bottomMargin = (double?)richText.Element("BottomMargin") ?? 91440;
+
+ var bodyPr = txBody.Element(A + "bodyPr");
+ if (bodyPr != null)
+ {
+ bodyPr.Add(new XAttribute("lIns", leftMargin));
+ bodyPr.Add(new XAttribute("rIns", rightMargin));
+ bodyPr.Add(new XAttribute("tIns", topMargin));
+ bodyPr.Add(new XAttribute("bIns", bottomMargin));
+ }
+
+ var paragraphs = richText.Elements("Paragraph");
+ if (!paragraphs.Any())
+ {
+ txBody.Add(new XElement(A + "p"));
+ }
+ else
+ {
+ foreach (var para in paragraphs)
+ {
+ var pElem = new XElement(A + "p");
+ var runs = para.Elements("Run");
+ if (!runs.Any())
+ {
+ pElem.Add(new XElement(A + "endParaRPr"));
+ }
+ else
+ {
+ foreach (var run in runs)
+ {
+ var text = (string)run.Element("Text") ?? "";
+ var rElem = new XElement(A + "r");
+ var fmt = run.Element("Formatting");
+ if (fmt != null)
+ {
+ var rPr = new XElement(A + "rPr");
+ var sz = fmt.Element("Size");
+ if (sz != null) rPr.Add(new XAttribute("sz", (double)sz * 100));
+ var bold = fmt.Element("Bold");
+ if (bold != null && (bool)bold) rPr.Add(new XAttribute("b", 1));
+ var italic = fmt.Element("Italic");
+ if (italic != null && (bool)italic) rPr.Add(new XAttribute("i", 1));
+ var underline = (string?)fmt.Element("Underline");
+ if (underline != null) rPr.Add(new XAttribute("u", underline));
+ var color = (string?)fmt.Element("Color");
+ if (color != null)
+ rPr.Add(new XElement(A + "solidFill", new XElement(A + "srgbClr", new XAttribute("val", color))));
+ var font = (string?)fmt.Element("Font");
+ if (font != null)
+ rPr.Add(new XElement(A + "latin", new XAttribute("typeface", font)));
+ if (rPr.HasAttributes || rPr.HasElements) rElem.Add(rPr);
+ }
+ rElem.Add(new XElement(A + "t", text));
+ pElem.Add(rElem);
+ }
+ }
+ txBody.Add(pElem);
+ }
+ }
+ }
+ else
+ {
+ txBody.Add(new XElement(A + "p"));
+ }
+
+ sp.Add(txBody);
+ spTree.Add(sp);
+ }
+ }
+ }
+
+ cSld.Add(spTree);
+ slideDoc.Root?.Add(cSld);
+
+ var slideUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri($"/ppt/{slideFileName}", UriKind.Relative));
+ var slidePart = pptx.CreatePart(slideUri, "application/vnd.openxmlformats-officedocument.presentationml.slide+xml");
+ using (var sw = new StreamWriter(slidePart.GetStream()))
+ slideDoc.Save(sw);
+
+ var slideRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri($"/ppt/slides/_rels/slide{i + 1}.xml.rels", UriKind.Relative));
+ var slideRelPart = pptx.CreatePart(slideRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(slideRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ var rootRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/_rels/.rels", UriKind.Relative));
+ var rootRelPart = pptx.CreatePart(rootRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(rootRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ using (var sw = new StreamWriter(presPart.GetStream()))
+ pptxPresentation.Save(sw);
+ using (var sw = new StreamWriter(presRelPart.GetStream()))
+ presRels.Save(sw);
+
+ CreateMinimalTheme(pptx);
+ CreateMinimalSlideMaster(pptx);
+ CreateMinimalSlideLayout(pptx);
+ CreatePresProps(pptx);
+ CreateViewProps(pptx);
+
+ enbx.Close();
+ pptx.Close();
+
+ return pptxPath;
+ });
+ }
+
+ public Task FixConversionErrorsAsync()
+ {
+ var en5Path = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Seewo", "EasiNote5");
+ var tempDir = Path.Combine(en5Path, "Temp", "Conversion");
+ if (Directory.Exists(tempDir))
+ {
+ try { Directory.Delete(tempDir, true); } catch { }
+ }
+ return Task.CompletedTask;
+ }
+
+ private static Tuple GetSlideSize(XDocument presentation)
+ {
+ var sldSz = presentation.Root?.Element(P + "sldSz");
+ if (sldSz == null) return Tuple.Create(12192000.0, 6858000.0);
+ return Tuple.Create(
+ (double)sldSz.Attribute("cx"),
+ (double)sldSz.Attribute("cy"));
+ }
+
+ private static XDocument GetPptxPresentation(System.IO.Packaging.Package pptx)
+ {
+ var relsUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/_rels/.rels", UriKind.Relative));
+ var relsPart = pptx.GetPart(relsUri);
+ var rels = XDocument.Load(relsPart.GetStream());
+ var officeRel = rels.Root?.Elements("Relationship")
+ .FirstOrDefault(r => (string)r.Attribute("Type") ==
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument");
+ if (officeRel == null) throw new InvalidDataException("未找到 Office Document 关系");
+ var presUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri((string)officeRel.Attribute("Target"), UriKind.Relative));
+ var presPart = pptx.GetPart(presUri);
+ return XDocument.Load(presPart.GetStream());
+ }
+
+ private static List> GetPresentationSlideRels(System.IO.Packaging.Package pptx, XDocument presentation)
+ {
+ var relsUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/_rels/presentation.xml.rels", UriKind.Relative));
+ var relsPart = pptx.GetPart(relsUri);
+ var rels = XDocument.Load(relsPart.GetStream());
+ var slideType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
+ return rels.Root?.Elements("Relationship")
+ .Where(r => (string)r.Attribute("Type") == slideType)
+ .Select(r => Tuple.Create((string)r.Attribute("Id"), (string)r.Attribute("Target")))
+ .ToList() ?? new List>();
+ }
+
+ private static void CreateMinimalTheme(System.IO.Packaging.Package pptx)
+ {
+ var themeUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/theme/theme1.xml", UriKind.Relative));
+ var themePart = pptx.CreatePart(themeUri, "application/vnd.openxmlformats-officedocument.theme+xml");
+ using (var sw = new StreamWriter(themePart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreateMinimalSlideMaster(System.IO.Packaging.Package pptx)
+ {
+ var masterUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideMasters/slideMaster1.xml", UriKind.Relative));
+ var masterPart = pptx.CreatePart(masterUri, "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml");
+ using (var sw = new StreamWriter(masterPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ var masterRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideMasters/_rels/slideMaster1.xml.rels", UriKind.Relative));
+ var masterRelPart = pptx.CreatePart(masterRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(masterRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreateMinimalSlideLayout(System.IO.Packaging.Package pptx)
+ {
+ var layoutUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideLayouts/slideLayout1.xml", UriKind.Relative));
+ var layoutPart = pptx.CreatePart(layoutUri, "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml");
+ using (var sw = new StreamWriter(layoutPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreatePresProps(System.IO.Packaging.Package pptx)
+ {
+ var uri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/presProps.xml", UriKind.Relative));
+ var part = pptx.CreatePart(uri, "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml");
+ using (var sw = new StreamWriter(part.GetStream()))
+ sw.Write("");
+ }
+
+ private static void CreateViewProps(System.IO.Packaging.Package pptx)
+ {
+ var uri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/viewProps.xml", UriKind.Relative));
+ var part = pptx.CreatePart(uri, "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml");
+ using (var sw = new StreamWriter(part.GetStream()))
+ sw.Write("");
+ }
+ }
+}
diff --git a/Better-EN5/Services/PatchService.cs b/Better-EN5/Services/PatchService.cs
index e95228d..2fda154 100644
--- a/Better-EN5/Services/PatchService.cs
+++ b/Better-EN5/Services/PatchService.cs
@@ -46,7 +46,7 @@ namespace BetterEN5.Services
var manifest = new PatchManifest
{
InstallTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
- Version = "1.1.2"
+ Version = "1.2.0"
};
BackupFile(manifest,
diff --git a/Better-EN5/UI/BetterSeewoMainWindow.xaml b/Better-EN5/UI/BetterSeewoMainWindow.xaml
deleted file mode 100644
index 02544ba..0000000
--- a/Better-EN5/UI/BetterSeewoMainWindow.xaml
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
- #0F0F1A
- #1A1A2E
- #60B0FF
- #D0D0E0
- #707090
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- • 安装补丁:备份希沃白板关键配置文件,然后应用增强补丁。
- • 卸载补丁:从备份文件(.bak)还原原始配置,清除注册表修改。
- • 安装后请重启希沃白板使补丁生效。
- • 补丁不修改希沃白板主程序文件,仅修改配置和注册表。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Better-EN5/UI/BetterSeewoMainWindow.xaml.cs b/Better-EN5/UI/BetterSeewoMainWindow.xaml.cs
deleted file mode 100644
index e83f5a7..0000000
--- a/Better-EN5/UI/BetterSeewoMainWindow.xaml.cs
+++ /dev/null
@@ -1,243 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using Microsoft.Win32;
-using BetterEN5.Services;
-
-namespace BetterEN5.UI
-{
- public partial class BetterSeewoMainWindow : Window
- {
- private readonly InkService _inkService = new();
- private readonly ActivationService _activationService = new();
- private readonly PatchService _patchService = new();
-
- public BetterSeewoMainWindow()
- {
- InitializeComponent();
- Loaded += OnLoaded;
- }
-
- private async void OnLoaded(object sender, RoutedEventArgs e)
- {
- await RefreshInstallStatusAsync();
- }
-
- private void NavChanged(object sender, RoutedEventArgs e)
- {
- if (sender == NavInstall) ShowPanel(PanelInstall);
- else if (sender == NavInk) ShowPanel(PanelInk);
- else if (sender == NavConvert) ShowPanel(PanelConvert);
- else if (sender == NavTouch) ShowPanel(PanelTouch);
- else if (sender == NavActivate) ShowPanel(PanelActivate);
- }
-
- private void ShowPanel(StackPanel panel)
- {
- foreach (var p in new[] { PanelInstall, PanelInk, PanelConvert, PanelTouch, PanelActivate })
- p.Visibility = p == panel ? Visibility.Visible : Visibility.Collapsed;
- }
-
- private async Task RefreshInstallStatusAsync()
- {
- var installed = await Task.Run(() => _patchService.IsInstalled());
- InstallBtn.IsEnabled = !installed;
- UninstallBtn.IsEnabled = installed;
- InstallStatusText.Text = installed
- ? "✅ 补丁已安装"
- : "⚠️ 补丁未安装";
- if (installed)
- {
- var m = await Task.Run(() => _patchService.GetManifest());
- PatchDetailText.Text = $"版本: {m.Version} | 安装时间: {m.InstallTime} | 备份文件: {m.Backups.Count} 个 | 注册表项: {m.RegistryKeys.Count} 个";
- }
- else
- {
- PatchDetailText.Text = "尚未安装补丁。点击「安装补丁」将备份关键配置并应用增强补丁。";
- }
- }
-
- private async void InstallPatch_Click(object s, RoutedEventArgs e)
- {
- var progress = new Progress(msg =>
- {
- Dispatcher.Invoke(() => PatchDetailText.Text = msg);
- });
- InstallBtn.IsEnabled = false;
- UninstallBtn.IsEnabled = false;
- InstallStatusText.Text = "⏳ 正在安装...";
- try
- {
- await _patchService.InstallAsync(progress);
- InstallStatusText.Text = "✅ 补丁已安装";
- UninstallBtn.IsEnabled = true;
- }
- catch (Exception ex)
- {
- InstallStatusText.Text = "❌ 安装失败";
- PatchDetailText.Text = ex.Message;
- InstallBtn.IsEnabled = true;
- }
- }
-
- private async void UninstallPatch_Click(object s, RoutedEventArgs e)
- {
- var result = MessageBox.Show(this,
- "确定要卸载 Better-Seewo 补丁吗?\n将还原备份文件并清除注册表修改。",
- "卸载确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
- if (result != MessageBoxResult.Yes) return;
-
- var progress = new Progress(msg =>
- {
- Dispatcher.Invoke(() => PatchDetailText.Text = msg);
- });
- InstallBtn.IsEnabled = false;
- UninstallBtn.IsEnabled = false;
- InstallStatusText.Text = "⏳ 正在卸载...";
- try
- {
- await _patchService.UninstallAsync(progress);
- InstallStatusText.Text = "✅ 补丁已卸载";
- InstallBtn.IsEnabled = true;
- PatchDetailText.Text = "卸载完成。建议重启希沃白板。";
- }
- catch (Exception ex)
- {
- InstallStatusText.Text = "❌ 卸载失败";
- PatchDetailText.Text = ex.Message;
- UninstallBtn.IsEnabled = true;
- }
- }
-
- private void Minimize_Click(object s, RoutedEventArgs e) => WindowState = WindowState.Minimized;
- private void Maximize_Click(object s, RoutedEventArgs e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
- private void Close_Click(object s, RoutedEventArgs e) => Close();
-
- private async void ExportInk_Click(object s, RoutedEventArgs e)
- {
- var dialog = new SaveFileDialog { Title = "导出墨迹", Filter = "墨迹文件|*.ink.json|JSON|*.json", DefaultExt = ".ink.json" };
- if (dialog.ShowDialog() == true)
- {
- await ProgressDialog.Show(this, "导出墨迹", async p =>
- {
- p.UpdateStatus("正在导出墨迹...", "读取课件笔迹数据");
- await _inkService.ExportInkAsync(dialog.FileName);
- p.UpdateStatus("导出完成", $"已保存至 {Path.GetFileName(dialog.FileName)}");
- });
- InkStatus.Text = $"已导出: {Path.GetFileName(dialog.FileName)}";
- }
- }
-
- private async void ImportInk_Click(object s, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog { Title = "导入墨迹", Filter = "墨迹文件|*.ink.json|JSON|*.json" };
- if (dialog.ShowDialog() == true)
- {
- await ProgressDialog.Show(this, "导入墨迹", async p =>
- {
- p.UpdateStatus("正在导入墨迹...", "写入课件笔迹");
- await _inkService.ImportInkAsync(dialog.FileName);
- });
- InkStatus.Text = "墨迹已导入";
- }
- }
-
- private async void ConvertPptx_Click(object s, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog { Title = "选择 PPT", Filter = "PowerPoint|*.pptx" };
- if (dialog.ShowDialog() == true)
- {
- await ProgressDialog.Show(this, "转换文件", async p =>
- {
- p.UpdateStatus("正在转换 PPT → ENBX...", "调用转换器");
- var result = await new ConversionService().ConvertPptxToEnbxAsync(dialog.FileName);
- p.UpdateStatus("转换完成", Path.GetFileName(result));
- });
- }
- }
-
- private async void ConvertEnbx_Click(object s, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog { Title = "选择 ENBX", Filter = "希沃课件|*.enbx" };
- if (dialog.ShowDialog() == true)
- {
- await ProgressDialog.Show(this, "转换文件", async p =>
- {
- p.UpdateStatus("正在转换 ENBX → PPT...", "调用转换器");
- var result = await new ConversionService().ConvertEnbxToPptxAsync(dialog.FileName);
- p.UpdateStatus("导出完成", Path.GetFileName(result));
- });
- }
- }
-
- private async void CheckTouch_Click(object s, RoutedEventArgs e)
- {
- var ok = await Task.Run(() => TouchFixService.IsTouchWorkingCorrectly());
- TouchStatus.Text = ok ? "✅ IWB 模式已启用" : "⚠️ 未启用 IWB 模式";
- }
-
- private async void ApplyTouchFix_Click(object s, RoutedEventArgs e)
- {
- await ProgressDialog.Show(this, "触摸修复", async p =>
- {
- p.UpdateStatus("正在写入注册表配置...", "ForceIWB");
- await Task.Run(() => TouchFixService.ApplyFixAsync());
- p.UpdateStatus("正在配置 IWB 模式...", "IWBConfig.json");
- await Task.Delay(300);
- p.UpdateStatus("完成", "请重启希沃白板生效");
- });
- TouchStatus.Text = "修复已应用,请重启";
- }
-
- private async void ActivatePro_Click(object s, RoutedEventArgs e)
- {
- var dlg = new ActivateDialog();
- dlg.Owner = this;
- if (dlg.ShowDialog() == true)
- {
- await ProgressDialog.Show(this, "激活", async p =>
- {
- p.UpdateStatus("正在验证激活码...", "");
- var ok = await _activationService.ActivateProfessionalAsync(dlg.LicenseKey);
- if (ok)
- {
- p.UpdateStatus("激活成功", "专业版已启用");
- ActivationStatusText.Text = "✅ 专业版已激活";
- }
- else
- {
- p.UpdateStatus("激活失败", "激活码无效");
- }
- });
- }
- }
-
- private async void LaunchInstaller_Click(object s, RoutedEventArgs e)
- {
- var ok = await _activationService.LaunchBen5InstallerAsync();
- if (!ok)
- MessageBox.Show(this, "未找到注册补丁安装程序", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
- }
-
- private async void CheckActivation_Click(object s, RoutedEventArgs e)
- {
- var info = await Task.Run(() => _activationService.GetCurrentStatus());
- ActivationStatusText.Text = info.IsProfessional
- ? $"✅ 专业版 · {info.LastActivationDate}"
- : "🔓 社区版";
- }
-
- public static void ShowWindow()
- {
- Application.Current.Dispatcher.Invoke(() =>
- {
- var w = Application.Current.Windows.OfType().FirstOrDefault();
- if (w != null) { w.Activate(); return; }
- new BetterSeewoMainWindow().Show();
- });
- }
- }
-}
diff --git a/Better-EN5/UI/BoardMenuImportInk.cs b/Better-EN5/UI/BoardMenuImportInk.cs
new file mode 100644
index 0000000..21a7d6c
--- /dev/null
+++ b/Better-EN5/UI/BoardMenuImportInk.cs
@@ -0,0 +1,30 @@
+using Microsoft.Win32;
+using Cvte.EasiNote;
+using Cvte.Windows.Input;
+using BetterEN5.Services;
+
+namespace BetterEN5.UI
+{
+ public class BoardMenuImportInk : BoardEditMenuItem
+ {
+ public BoardMenuImportInk()
+ {
+ SortHint = 997;
+ Command = new DelegateCommand(() =>
+ {
+ var dialog = new OpenFileDialog
+ {
+ Title = "导入墨迹",
+ Filter = "墨迹文件 (*.ink.json)|*.ink.json|JSON 文件 (*.json)|*.json",
+ DefaultExt = ".ink.json"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ var inkService = new InkService();
+ _ = inkService.ImportInkAsync(dialog.FileName);
+ }
+ });
+ Predicate = _ => true;
+ }
+ }
+}
diff --git a/Better-EN5/UI/BoardMenuSettings.cs b/Better-EN5/UI/BoardMenuSettings.cs
index be1c713..411926b 100644
--- a/Better-EN5/UI/BoardMenuSettings.cs
+++ b/Better-EN5/UI/BoardMenuSettings.cs
@@ -1,5 +1,3 @@
-using System.Linq;
-using System.Windows;
using Cvte.EasiNote;
using Cvte.Windows.Input;
@@ -12,9 +10,24 @@ namespace BetterEN5.UI
SortHint = 999;
Command = new DelegateCommand(() =>
{
- BetterSeewoMainWindow.ShowWindow();
+ LaunchManager();
});
Predicate = _ => true;
}
+
+ private static void LaunchManager()
+ {
+ var baseDir = System.AppDomain.CurrentDomain.BaseDirectory;
+ var mgrPath = System.IO.Path.Combine(baseDir, "Better-Seewo.Manager.exe");
+ if (System.IO.File.Exists(mgrPath))
+ {
+ var psi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = mgrPath,
+ UseShellExecute = true
+ };
+ System.Diagnostics.Process.Start(psi);
+ }
+ }
}
}
diff --git a/Better-EN5/UI/HeadToolBarSettings.cs b/Better-EN5/UI/HeadToolBarSettings.cs
index 4e6f3cf..d86b128 100644
--- a/Better-EN5/UI/HeadToolBarSettings.cs
+++ b/Better-EN5/UI/HeadToolBarSettings.cs
@@ -10,9 +10,24 @@ namespace BetterEN5.UI
SortHint = 999;
Command = new DelegateCommand(() =>
{
- BetterSeewoMainWindow.ShowWindow();
+ LaunchManager();
});
Predicate = _ => true;
}
+
+ private static void LaunchManager()
+ {
+ var baseDir = System.AppDomain.CurrentDomain.BaseDirectory;
+ var mgrPath = System.IO.Path.Combine(baseDir, "Better-Seewo.Manager.exe");
+ if (System.IO.File.Exists(mgrPath))
+ {
+ var psi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = mgrPath,
+ UseShellExecute = true
+ };
+ System.Diagnostics.Process.Start(psi);
+ }
+ }
}
}
diff --git a/Better-EN5/UI/ProgressDialog.xaml b/Better-EN5/UI/ProgressDialog.xaml
deleted file mode 100644
index 591bed2..0000000
--- a/Better-EN5/UI/ProgressDialog.xaml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Better-EN5/UI/ProgressDialog.xaml.cs b/Better-EN5/UI/ProgressDialog.xaml.cs
deleted file mode 100644
index 0013edb..0000000
--- a/Better-EN5/UI/ProgressDialog.xaml.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using System.Windows;
-
-namespace BetterEN5.UI
-{
- public partial class ProgressDialog : Window
- {
- public ProgressDialog()
- {
- InitializeComponent();
- Loaded += (s, e) =>
- {
- var desktop = System.Windows.SystemParameters.WorkArea;
- Left = (desktop.Width - Width) / 2;
- Top = (desktop.Height - Height) / 2;
- };
- }
-
- public void UpdateStatus(string text, string detail = "")
- {
- Dispatcher.Invoke(() =>
- {
- StatusText.Text = text;
- if (!string.IsNullOrEmpty(detail))
- DetailText.Text = detail;
- });
- }
-
- public async Task RunWithProgress(Func action)
- {
- try
- {
- await action(this);
- UpdateStatus("完成", "");
- await Task.Delay(500);
- DialogResult = true;
- }
- catch (Exception ex)
- {
- UpdateStatus($"失败: {ex.Message}", "");
- await Task.Delay(1500);
- DialogResult = false;
- }
- Close();
- }
-
- public static async Task Show(Window owner, string title, Func action)
- {
- var dialog = new ProgressDialog();
- dialog.Owner = owner;
- dialog.Title = title;
- dialog.Show();
- await dialog.RunWithProgress(action);
- return dialog.DialogResult ?? false;
- }
- }
-}
diff --git a/Better-EN5/UI/SettingsWindow.xaml b/Better-EN5/UI/SettingsWindow.xaml
deleted file mode 100644
index 2676507..0000000
--- a/Better-EN5/UI/SettingsWindow.xaml
+++ /dev/null
@@ -1,168 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 将当前课件中的所有墨迹(笔迹、标注)导出为 JSON 文件,方便备份和迁移。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 将 PowerPoint 文件(.pptx)转换为希沃白板课件格式(.enbx),
- 保留更多原始格式和排版。
-
-
-
-
-
-
-
-
- 将希沃白板课件(.enbx)导出为 PowerPoint 格式(.pptx)。
-
-
-
-
-
-
-
-
- 清除转换缓存和临时文件,修复可能的转换失败问题。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 正在检测触摸状态...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- • 部分学校电脑的触摸硬件本身没有故障,但希沃白板的触摸检测逻辑存在兼容性问题。
- • 本功能通过配置注册表和配置文件,强制启用触摸支持,跳过有问题的检测步骤。
- • 如果修复后触摸仍不工作,请尝试重启希沃白板。
- • 如遇到更严重的触摸问题,可使用"还原"功能恢复默认设置。
-
-
-
-
-
-
-
-
-
-
-
-
-
- Better-EN5 模块提供以下增强功能:
- • 墨迹(笔迹/标注)的独立导出和导入
- • 增强的 PPT 与 ENBX 文件转换
- • 触摸检测逻辑修复与错误抑制
- 项目地址:https://github.com/anomalyco/opencode
-
-
-
-
-
-
diff --git a/Better-EN5/UI/SettingsWindow.xaml.cs b/Better-EN5/UI/SettingsWindow.xaml.cs
deleted file mode 100644
index c9833db..0000000
--- a/Better-EN5/UI/SettingsWindow.xaml.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading.Tasks;
-using System.Windows;
-using Microsoft.Win32;
-using Cvte.EasiNote;
-using BetterEN5.Services;
-
-namespace BetterEN5.UI
-{
- public partial class SettingsWindow : Window
- {
- private readonly InkService _inkService = new();
- private readonly ConversionService _conversionService = new();
-
- public SettingsWindow()
- {
- InitializeComponent();
- Loaded += OnLoaded;
- }
-
- private async void OnLoaded(object sender, RoutedEventArgs e)
- {
- LoadSettings();
- await RefreshTouchStatusAsync();
- }
-
- private void LoadSettings()
- {
- var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- var settingsFile = Path.Combine(localAppData, "Seewo", "EasiNote5", "BetterEN5", "settings.json");
- if (File.Exists(settingsFile))
- {
- try
- {
- var json = File.ReadAllText(settingsFile);
- var settings = System.Text.Json.JsonSerializer.Deserialize(json);
- if (settings != null)
- {
- AutoSaveInkCheckBox.IsChecked = settings.AutoSaveInk;
- IncludeShapeInkCheckBox.IsChecked = settings.IncludeShapeInk;
- ForceIWBModeCheckBox.IsChecked = settings.ForceIWB;
- SkipTouchCheckCheckBox.IsChecked = settings.SkipTouchCheck;
- SuppressTouchErrorsCheckBox.IsChecked = settings.SuppressTouchErrors;
- EnableFallbackModeCheckBox.IsChecked = settings.EnableFallbackMode;
- }
- }
- catch { }
- }
- }
-
- private void SaveSettings()
- {
- var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- var dir = Path.Combine(localAppData, "Seewo", "EasiNote5", "BetterEN5");
- var settingsFile = Path.Combine(dir, "settings.json");
-
- if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
-
- var settings = new SettingsData
- {
- AutoSaveInk = AutoSaveInkCheckBox.IsChecked ?? false,
- IncludeShapeInk = IncludeShapeInkCheckBox.IsChecked ?? false,
- ForceIWB = ForceIWBModeCheckBox.IsChecked ?? true,
- SkipTouchCheck = SkipTouchCheckCheckBox.IsChecked ?? true,
- SuppressTouchErrors = SuppressTouchErrorsCheckBox.IsChecked ?? true,
- EnableFallbackMode = EnableFallbackModeCheckBox.IsChecked ?? true,
- };
-
- var json = System.Text.Json.JsonSerializer.Serialize(settings, new System.Text.Json.JsonSerializerOptions
- {
- WriteIndented = true
- });
- File.WriteAllText(settingsFile, json);
- }
-
- private async void ExportInk_Click(object sender, RoutedEventArgs e)
- {
- var dialog = new SaveFileDialog
- {
- Title = "导出墨迹",
- Filter = "墨迹文件|*.ink.json|JSON 文件|*.json|所有文件|*.*",
- DefaultExt = ".ink.json"
- };
-
- if (dialog.ShowDialog() == true)
- {
- try
- {
- await _inkService.ExportInkAsync(dialog.FileName);
- InkStatusText.Text = $"墨迹已导出至: {dialog.FileName}";
- InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- InkStatusText.Text = $"导出失败: {ex.Message}";
- InkStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
- }
-
- private async void ImportInk_Click(object sender, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog
- {
- Title = "导入墨迹",
- Filter = "墨迹文件|*.ink.json|JSON 文件|*.json|所有文件|*.*",
- DefaultExt = ".ink.json"
- };
-
- if (dialog.ShowDialog() == true)
- {
- try
- {
- await _inkService.ImportInkAsync(dialog.FileName);
- InkStatusText.Text = "墨迹已导入";
- InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- InkStatusText.Text = $"导入失败: {ex.Message}";
- InkStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
- }
-
- private void SaveInkSettings_Click(object sender, RoutedEventArgs e)
- {
- SaveSettings();
- InkStatusText.Text = "墨迹设置已保存";
- InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
-
- private async void ConvertPptx_Click(object sender, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog
- {
- Title = "选择 PowerPoint 文件",
- Filter = "PowerPoint 文件|*.pptx|所有文件|*.*"
- };
-
- if (dialog.ShowDialog() == true)
- {
- try
- {
- var result = await _conversionService.ConvertPptxToEnbxAsync(dialog.FileName);
- ConversionStatusText.Text = $"转换完成: {result}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- ConversionStatusText.Text = $"转换失败: {ex.Message}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
- }
-
- private async void ConvertEnbx_Click(object sender, RoutedEventArgs e)
- {
- var dialog = new OpenFileDialog
- {
- Title = "选择希沃白板课件",
- Filter = "希沃课件|*.enbx|所有文件|*.*"
- };
-
- if (dialog.ShowDialog() == true)
- {
- try
- {
- var result = await _conversionService.ConvertEnbxToPptxAsync(dialog.FileName);
- ConversionStatusText.Text = $"导出完成: {result}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- ConversionStatusText.Text = $"导出失败: {ex.Message}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
- }
-
- private async void ClearConversionCache_Click(object sender, RoutedEventArgs e)
- {
- try
- {
- await _conversionService.FixConversionErrorsAsync();
- ConversionStatusText.Text = "转换缓存已清理";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- ConversionStatusText.Text = $"清理失败: {ex.Message}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
-
- private async void ReRegisterConverter_Click(object sender, RoutedEventArgs e)
- {
- try
- {
- var converterPath = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
- "Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main",
- "EasiNote.OfficeDocumentConverter.exe");
-
- if (File.Exists(converterPath))
- {
- Process.Start(converterPath, "/register");
- await Task.Delay(1000);
- ConversionStatusText.Text = "转换器已重新注册";
- }
- else
- {
- ConversionStatusText.Text = "未找到转换器";
- }
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
- catch (Exception ex)
- {
- ConversionStatusText.Text = $"注册失败: {ex.Message}";
- ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
-
- private async void CheckTouch_Click(object sender, RoutedEventArgs e)
- {
- await RefreshTouchStatusAsync();
- }
-
- private async void ApplyTouchFix_Click(object sender, RoutedEventArgs e)
- {
- try
- {
- await TouchFixService.ApplyFixAsync();
- TouchStatusText.Text = "触摸修复已应用,请重启希沃白板生效";
- TouchStatusText.Foreground = System.Windows.Media.Brushes.Green;
- SaveSettings();
- }
- catch (Exception ex)
- {
- TouchStatusText.Text = $"修复失败: {ex.Message}";
- TouchStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
-
- private void SaveTouchSettings_Click(object sender, RoutedEventArgs e)
- {
- SaveSettings();
- TouchStatusText.Text = "触摸设置已保存";
- TouchStatusText.Foreground = System.Windows.Media.Brushes.Green;
- }
-
- private async Task RefreshTouchStatusAsync()
- {
- try
- {
- var isIwb = await Task.Run(() => TouchFixService.IsTouchWorkingCorrectly());
- TouchStatusText.Text = isIwb
- ? "触摸状态: 正常 (IWB 模式已启用)"
- : "触摸状态: 非 IWB 模式 (可能存在问题)";
- TouchStatusText.Foreground = isIwb
- ? System.Windows.Media.Brushes.Green
- : System.Windows.Media.Brushes.Orange;
- }
- catch (Exception ex)
- {
- TouchStatusText.Text = $"无法检测触摸状态: {ex.Message}";
- TouchStatusText.Foreground = System.Windows.Media.Brushes.Red;
- }
- }
-
- private class SettingsData
- {
- public bool AutoSaveInk { get; set; }
- public bool IncludeShapeInk { get; set; }
- public bool ForceIWB { get; set; } = true;
- public bool SkipTouchCheck { get; set; } = true;
- public bool SuppressTouchErrors { get; set; } = true;
- public bool EnableFallbackMode { get; set; } = true;
- }
- }
-}
diff --git a/Better-Seewo.sln b/Better-Seewo.sln
deleted file mode 100644
index bc82a2e..0000000
--- a/Better-Seewo.sln
+++ /dev/null
@@ -1,19 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Better-EN5", "Better-EN5\Better-EN5.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md
deleted file mode 100644
index aad8ea8..0000000
--- a/DEVELOPER_GUIDE.md
+++ /dev/null
@@ -1,296 +0,0 @@
-# Better-Seewo 完整开发指南
-
-**项目类型**: 希沃白板 5 插件 SDK(基于 dotnetCampus.EasiPlugin.Sdk v2.1.1-alpha.3 构建)
-**目标框架**: net6.0-windows (.NET 6.0, WPF + WinForms)
-**开发环境**: 希沃白板 5 (版本 5.2.2.653 ~ 5.3.0.0)
-
-## 前言
-
-希沃白板 5 插件 SDK 文档严重缺失,导致开发者几乎无法找到正确的菜单注册点。本项目通过对 `EasiNote.Api.dll` 的完全逆向工程分析,填补了所有开发空白。
-
-## 核心发现
-
-经过对 `EasiNote.Api.dll` (版本 5.2.4.9855) 的静态分析,我们发现了 **`Cvte.EasiNote.UIItemPurposes`** 类型,其中包含 **13 个可用的菜单常量**。这是本项目开发的所有菜单入口的基础。
-
-### 所有真实可用的常量
-
-| 常量名 | 实际字符串值 | 菜单层级 | 适用场景 |
-|---|---|---|---|
-| `ToolBar` | `EduBoard.V5.ToolBarItem` | 顶部通用工具栏 | 白板顶部的工具按钮区域(绘图工具) |
-| `FunctionBar` | `EduBoard.V5.FunctionBarItem` | 顶部功能栏 | 白板顶部显示的绘图工具栏(画笔、橡皮擦等) |
-| `BoardEditMenu` | `EduBoard.V5.BoardEditMenuItem` | 备课模式·右键菜单 | 课件上方的"板书"区域右键菜单 (备课场景) |
-| `BoardDisplayMenu` | `EduBoard.V5.BoardDisplayMenuItem` | 授课模式·右键菜单 | 课件上方的"板书"区域右键菜单 (授课场景) |
-| `ElementEditMenu` | `EduBoard.V5.ElementEditMenuItem` | 备课模式·元素右键菜单 | 备课时对单个板书元素的右键操作 |
-| `ElementDisplayMenu` | `EduBoard.V5.ElementDisplayMenuItem` | 授课模式·元素右键菜单 | 授课时对单个板书元素的右键操作 |
-| `HeadToolBar` | `EduBoard.V5.HeadToolBarItem` | 顶级工具栏 | 白板左上角的"Better-Seewo"入口按钮 |
-| `BrowserTitleBar` | `EduBoard.V5.ExtendedTitleBar.Browser` | 浏览器·标题栏 | 备课时浏览器区域顶部的标题栏区域 |
-| `CloudTitleBar` | `EduBoard.V5.ExtendedTitleBar.Cloud` | 云盘·标题栏 | 备课时云盘区域顶部的标题栏区域 |
-| `AllTitleBar` | `EduBoard.V5.ExtendedTitleBar.All` | 所有·标题栏 | 备课时所有标题栏区域的集合 |
-| `TabUniqueApplicationMenu` | `EduBoard.V5.ApplicationMenu.Shell` | 当前标签页·应用菜单 | 当前标签页左下角的汉堡菜单 (ApplicationMenu 类型) |
-| `TabGlobalApplicationMenu` | `EduBoard.V5.ApplicationMenu.All` | 全局·应用菜单 | 所有标签页左下角的汉堡菜单 (ApplicationMenu 类型) |
-| `MultiBoardProxyToolBar` | `EduBoard.V5.MultiBoardProxyToolBar.Shell` | 多板代理·工具栏 | 多板代理的工具栏区域 |
-
-**⚠️ 关键提示**:希沃 SDK **不包含** `ApplicationMenu` 或 `ExtendedTitleBar` 类型常量!实际使用对应的具体常量替代:
-- `ApplicationMenu` → `TabUniqueApplicationMenu` / `TabGlobalApplicationMenu`
-- `ExtendedTitleBar` → `BrowserTitleBar` / `CloudTitleBar` / `AllTitleBar`
-
-## 菜单层级设计
-
-每个菜单项都对应于 `IUIItem` 的一个具体子类,这些子类定义了特定的 UI 层级。
-
-```csharp
-// 菜单常量对应关系 (全部位于 Cvte.EasiNote 命名空间)
-UIItemPurposes.BoardEditMenu // → BoardEditMenuItem
-UIItemPurposes.HeadToolBar // → HeadToolBarItem
-UIItemPurposes.TabGlobalApplicationMenu // → ApplicationMenuItem
-UIItemPurposes.AllTitleBar // → ExtendedTitleBarItem
-```
-
-## 本项目核心组件
-
-### 1. BetterSeewoMainWindow
-**文件**:`Better-EN5/UI/BetterSeewoMainWindow.xaml.cs`
-**作用**:插件的主界面,采用 16:9 横版布局,包含五个功能模块
-
-### 2. 五大核心服务
-
-#### InkService (课件墨迹管理)
-- **功能**:导出/导入课件墨迹数据
-- **技术**:反射调用希沃白板 Remark API
-- **优势**:无需硬编译依赖,兼容所有版本的白板
-- **应用场景**:课件备份、墨迹恢复、跨设备同步
-
-#### ConversionService (文件转换)
-- **功能**:PPT ↔ ENBX 格式互转
-- **技术**:独立进程 (`EasiNote.OfficeDocumentConverter.exe`)
-- **优势**:避免平台兼容性问题,保持文件原始质量
-
-#### TouchFixService (触摸修复)
-- **功能**:为非触摸大屏设备启用 IWB 模式
-- **技术**:注册表 + 配置文件补丁 (`IWBConfig.json`, `TouchConfig.ini`)
-- **应用场景**:大屏模式、触摸屏校准、学校多点触控
-
-#### ActivationService (专业版激活)
-- **功能**:专业版激活码验证 + 注册
-- **技术**:注册表写入 + 一键安装补丁程序
-
-### 3. UI模块
-
-#### BoardMenuExportInk
-**文件**:`Better-EN5/UI/BoardMenuExportInk.cs`
-**用途**:备课模式·右键菜单,导出当前课件墨迹为 `.json` 文件
-
-#### BoardMenuSettings
-**文件**:`Better-EN5/UI/BoardMenuSettings.cs`
-**用途**:备课模式·右键菜单,打开 Better-Seewo 设置界面
-
-#### HeadToolBarSettings
-**文件**:`Better-EN5/UI/HeadToolBarSettings.cs`
-**用途**:通用顶级工具栏,快速打开 Better-Seewo 主界面
-
-## 快速上手
-
-### 1. 环境搭建
-```bash
-# 1. 安装 .NET 6.0 SDK
-# 2. 下载并安装希沃白板 5 (5.2.2.653 ~ 5.3.0.0)
-```
-
-### 2. 构建项目
-```bash
-dotnet build Better-EN5/Better-EN5.csproj -c Release
-```
-
-### 3. 安装插件
-#### 方式一:安装程序
-```powershell
-# 以管理员权限运行,自动安装到希沃白板指定目录
-.
-\\install.ps1
-```
-
-#### 方式二:手动部署
-```powershell
-# 将以下目录复制到:
-# %APPDATA%\Seewo\EasiNote5\Extensions\Better-EN5\
-Better-EN5\bin\Release\net6.0-windows\
-Better-EN5\manifest.coin
-```
-
-### 4. 使用说明
-1. **重启希沃白板 5**
-2. **找到入口**:
- - **备课模式**:右键菜单 → "Better-Seewo" 或顶部工具栏按钮
- - **授课模式**:左下角汉堡菜单 → "Better-Seewo" (待实现)
-3. **体验功能**:打开主界面,体验五大核心服务
-
-## 完整开发指南
-
-### 1. 创建新菜单项
-
-**步骤**:
-1. **选择目标常量**,确定基类
-2. **创建子类**,继承对应基类
-3. **实现功能成员**:`Command`、`Predicate`、`SortHint`
-4. **自动注册**:无需手动处理,`AppendWithLang` 自动完成
-
-**示例** (备课模式·右键菜单):
-```csharp
-// 1. 选择 BoardEditMenu 常量(备课模式·右键菜单)
-UIItemPurposes.BoardEditMenu // → BoardEditMenuItem
-
-// 2. 创建子类
-public class ExportInkMenuItem : BoardEditMenuItem
-{
- public ExportInkMenuItem()
- {
- SortHint = 100; // 菜单项排序权重
- Command = new DelegateCommand(ExportInk);
- Predicate = _ => true; // 总是显示
- }
-}
-
-// 3. 自动注册 (通过 AppendWithLang)
-manager.AppendWithLang(new ExportInkMenuItem(),
- new UIItemAttribute(UIItemPurposes.BoardEditMenu), // <-- 指向 BoardEditMenu 常量
- new[]
- {
- new UIItemLangInfo(CultureInfo.Chinese, "导出墨迹"),
- new UIItemLangInfo(CultureInfo.English, "Export Ink")
- });
-```
-
-### 2. 多语言支持
-
-系统中内置完整的多语言支持框架。只需在注册时提供对应语言的文本即可。
-
-```csharp
-new[]
-{
- new UIItemLangInfo(new CultureInfo("zh-CHS"), "功能名称"),
- new UIItemLangInfo(new CultureInfo("en"), "Feature Name"),
- new UIItemLangInfo(new CultureInfo("ja"), "機能名"),
-}
-```
-
-### 3. 所有常量对照表与应用场景
-
-| 菜单常量 | 所处层级 | 适用场景 | 示例 |
-|---|---|---|---|
-| `BoardEditMenu` | 备课模式·右键菜单 | 备课时课件上方的右键菜单 | 导出墨迹、设置、打印等 |
-| `BoardDisplayMenu` | 授课模式·右键菜单 | 授课时课件上方的右键菜单 | 授课控制、评阅等 |
-| `HeadToolBar` | 通用顶级工具栏 | 白板左上角的按钮 | Better-Seewo 入口 |
-| `TabUniqueApplicationMenu` | 当前标签页·汉堡菜单 | 当前标签页的左下角菜单 | 单页专用功能 |
-| `TabGlobalApplicationMenu` | 全局·汉堡菜单 | 所有标签页的左下角菜单 | 所有页面通用功能 |
-| `AllTitleBar` | 备课·所有标题栏 | 备课时所有标题栏区域 | 标题栏菜单 |
-| `BrowserTitleBar` | 备课·浏览器标题栏 | 备课时浏览器区域标题栏 | 浏览器相关操作 |
-| `CloudTitleBar` | 备课·云盘标题栏 | 备课时云盘区域标题栏 | 云盘相关操作 |
-
-## 技术要点
-
-### 1. 菜单注册机制
-
-```csharp
-// 底层方法 (不推荐直接使用)
-manager.Append(item, new UIItemAttribute(UIItemPurposes.BoardEditMenu));
-
-// 方便易用的封装 (推荐使用)
-manager.AppendWithLang(item, new UIItemAttribute(UIItemPurposes.BoardEditMenu), langInfos);
-```
-
-### 2. 大屏支持
-
-为非触摸大屏设备提供专属支持,通过注册表和配置文件补丁实现。
-
-### 3. 激活机制
-
-专业版激活码验证系统,支持在线验证和离线注册。
-
-### 4. 文件转换
-
-使用独立进程实现 PPT ↔ ENBX 互转,避免平台兼容性问题。
-
-### 5. 激活流程
-
-一键启动 BEN5 注册补丁安装程序,实现专业版激活。
-
-## 常见问题
-
-### Q: `ApplicationMenu` 和 `ExtendedTitleBar` 是否存在?
-**A**: 不存在。使用对应的具体常量替代:
-- `ApplicationMenu` → `TabUniqueApplicationMenu` / `TabGlobalApplicationMenu`
-- `ExtendedTitleBar` → `BrowserTitleBar` / `CloudTitleBar` / `AllTitleBar`
-
-### Q: 菜单项如何排序?
-**A**: 使用 `SortHint` 属性,数值越小排序越前。
-
-### Q: 如何控制菜单项显示条件?
-**A**: 使用 `Predicate` 属性,接收 `object` 参数,返回 `bool` 值。
-
-### Q: 多语言如何使用?
-**A**: 系统自动生成 Key `Lang.{前缀}.{菜单项类名}`,直接在 UI 中使用即可。
-
-## 构建与发布
-
-### 构建命令
-```bash
-# 调试版本
-dotnet build Better-EN5/Better-EN5.csproj -c Debug
-
-# 发布版本
-dotnet build Better-EN5/Better-EN5.csproj -c Release
-```
-
-### 发布文件
-```
-Better-EN5/bin/Release/net6.0-windows/
-├── Better-EN5.dll # 主插件
-├── BetterSeewoMainWindow.exe.config # 配置文件
-├── ... (所有依赖 DLL)
-└── BetterSeewoMainWindow.xaml # 主界面资源
-```
-
-## 许可证
-
-## 构建方法
-
-### 前置条件
-1. 已安装 **希沃白板 5**(版本 5.2.2.653 ~ 5.3.0.0)
-2. 已安装 **.NET SDK 6.0**(推荐 `C:\Program Files\dotnet`)
-3. 确保 `dotnet` 在 `PATH` 中,或使用完整路径
-
-### 构建 Release
-```powershell
-# 方式一:确保 dotnet 在 PATH
-set PATH=C:\Program Files\dotnet;%PATH%
-dotnet build -c Release Better-EN5
-
-# 方式二:使用完整路径
-& "C:\Program Files\dotnet\dotnet.exe" build -c Release Better-EN5
-```
-
-### 安装到希沃白板
-```powershell
-# 管理员身份运行
-.\scripts\install.ps1
-```
-
-### 构建输出
-| 文件 | 说明 |
-|------|------|
-| `Better-EN5\bin\Release\net6.0-windows\Better-EN5.dll` | 插件 DLL |
-| `Better-EN5\bin\Release\net6.0-windows\Better-EN5.exe` | 宿主程序 |
-| `bin\Release\Better-Seewo 增强插件.X.X.X.exe` | 安装包 |
-| `bin\Release\Better-Seewo 增强插件.X.X.X.zip` | 插件包 (.enp) |
-
-## 许可协议
-
-本项目仅供 **学习交流** 使用。
-
-## 致谢
-
-感谢 dotnetCampus.EasiPlugin.Sdk 框架的支持。
-
----
-
-**开发指南已根据对 `EasiNote.Api.dll` 的完整逆向工程成果生成**。所有常量和继承关系均已验证。
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index d744385..0000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- latest
- enable
- $(MSBuildThisFileDirectory)bin\$(Configuration)
- Better-Seewo
- Better-Seewo
- https://github.com/anomalyco/opencode
-
-
diff --git a/Manager/ActivateDialog.xaml b/Manager/ActivateDialog.xaml
new file mode 100644
index 0000000..2f0c0db
--- /dev/null
+++ b/Manager/ActivateDialog.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Manager/ActivateDialog.xaml.cs b/Manager/ActivateDialog.xaml.cs
new file mode 100644
index 0000000..d13b874
--- /dev/null
+++ b/Manager/ActivateDialog.xaml.cs
@@ -0,0 +1,27 @@
+using System.Windows;
+
+namespace BetterSeewo.Manager
+{
+ public partial class ActivateDialog : Window
+ {
+ public string LicenseKey { get; private set; } = "";
+
+ public ActivateDialog()
+ {
+ InitializeComponent();
+ }
+
+ private void OnKeyTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
+ {
+ ActivateButton.IsEnabled = KeyTextBox.Text.Trim().Length >= 4;
+ }
+
+ private void Activate_Click(object sender, RoutedEventArgs e)
+ {
+ LicenseKey = KeyTextBox.Text.Trim();
+ DialogResult = true;
+ Close();
+ }
+ }
+}
+
diff --git a/Manager/App.xaml b/Manager/App.xaml
new file mode 100644
index 0000000..9f1b6db
--- /dev/null
+++ b/Manager/App.xaml
@@ -0,0 +1,15 @@
+
+
+
+ #0F0F1A
+ #1A1A2E
+ #60B0FF
+ #D0D0E0
+ #707090
+
+
+
+
diff --git a/Manager/App.xaml.cs b/Manager/App.xaml.cs
new file mode 100644
index 0000000..d5154a5
--- /dev/null
+++ b/Manager/App.xaml.cs
@@ -0,0 +1,8 @@
+using System.Windows;
+
+namespace BetterSeewo.Manager
+{
+ public partial class App : Application
+ {
+ }
+}
diff --git a/Manager/MainWindow.xaml b/Manager/MainWindow.xaml
new file mode 100644
index 0000000..44c5899
--- /dev/null
+++ b/Manager/MainWindow.xaml
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Manager/MainWindow.xaml.cs b/Manager/MainWindow.xaml.cs
new file mode 100644
index 0000000..841347e
--- /dev/null
+++ b/Manager/MainWindow.xaml.cs
@@ -0,0 +1,307 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using BetterSeewo.Manager.Services;
+
+namespace BetterSeewo.Manager
+{
+ public partial class MainWindow : Window
+ {
+ private readonly PatchService _patchService = new();
+ private readonly InkService _inkService = new();
+ private readonly CustomConversionService _conversionService = new();
+ private readonly ActivationService _activationService = new();
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ Loaded += OnLoaded;
+ }
+
+ private async void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ await RefreshInstallStatusAsync();
+ LoadInkSettings();
+ }
+
+ private void TabChanged(object sender, RoutedEventArgs e)
+ {
+ if (sender == TabInstall) ShowPage(PageInstall);
+ else if (sender == TabInk) ShowPage(PageInk);
+ else if (sender == TabConvert) ShowPage(PageConvert);
+ else if (sender == TabTouch) ShowPage(PageTouch);
+ else if (sender == TabActivate) ShowPage(PageActivate);
+ }
+
+ private void ShowPage(StackPanel page)
+ {
+ foreach (var p in new[] { PageInstall, PageInk, PageConvert, PageTouch, PageActivate })
+ p.Visibility = p == page ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ private void Minimize_Click(object s, RoutedEventArgs e) => WindowState = WindowState.Minimized;
+ private void Maximize_Click(object s, RoutedEventArgs e) =>
+ WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
+ private void Close_Click(object s, RoutedEventArgs e) => Close();
+
+ // Install Management
+ private async Task RefreshInstallStatusAsync()
+ {
+ var installed = await Task.Run(() => _patchService.IsInstalled());
+ InstallBtn.IsEnabled = !installed;
+ UninstallBtn.IsEnabled = installed;
+ InstallStatusText.Text = installed ? "补丁已安装" : "补丁未安装";
+ if (installed)
+ {
+ var m = await Task.Run(() => _patchService.GetManifest());
+ PatchDetailText.Text = $"版本: {m.Version} | 安装时间: {m.InstallTime} | 备份: {m.Backups.Count} 个 | 注册表: {m.RegistryKeys.Count} 项";
+ }
+ else
+ PatchDetailText.Text = "尚未安装补丁。点击「安装补丁」将备份关键配置并应用增强。";
+ }
+
+ private async void InstallPatch_Click(object s, RoutedEventArgs e)
+ {
+ var progress = new Progress(msg => Dispatcher.Invoke(() => PatchDetailText.Text = msg));
+ InstallBtn.IsEnabled = false;
+ UninstallBtn.IsEnabled = false;
+ InstallStatusText.Text = "正在安装...";
+ try
+ {
+ await _patchService.InstallAsync(progress);
+ InstallStatusText.Text = "补丁已安装";
+ UninstallBtn.IsEnabled = true;
+ await CreateShortcutAsync();
+ }
+ catch (Exception ex)
+ {
+ InstallStatusText.Text = "安装失败";
+ PatchDetailText.Text = ex.Message;
+ InstallBtn.IsEnabled = true;
+ }
+ }
+
+ private async void UninstallPatch_Click(object s, RoutedEventArgs e)
+ {
+ var result = MessageBox.Show(this, "确定要卸载补丁吗?\n将还原备份文件并清除注册表修改。",
+ "卸载确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
+ if (result != MessageBoxResult.Yes) return;
+
+ var progress = new Progress(msg => Dispatcher.Invoke(() => PatchDetailText.Text = msg));
+ InstallBtn.IsEnabled = false;
+ UninstallBtn.IsEnabled = false;
+ InstallStatusText.Text = "正在卸载...";
+ try
+ {
+ await _patchService.UninstallAsync(progress);
+ InstallStatusText.Text = "补丁已卸载";
+ InstallBtn.IsEnabled = true;
+ PatchDetailText.Text = "卸载完成。建议重启希沃白板。";
+ }
+ catch (Exception ex)
+ {
+ InstallStatusText.Text = "卸载失败";
+ PatchDetailText.Text = ex.Message;
+ UninstallBtn.IsEnabled = true;
+ }
+ }
+
+ private async Task CreateShortcutAsync()
+ {
+ try
+ {
+ var ps1 = Path.GetTempFileName() + ".ps1";
+ var exePath = Environment.ProcessPath;
+ var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
+ var lnkPath = Path.Combine(desktop, "Better-Seewo Manager.lnk");
+ File.WriteAllText(ps1,
+ "$ws = New-Object -ComObject WScript.Shell\n" +
+ "$s = $ws.CreateShortcut(\"" + lnkPath.Replace("'", "''") + "\")\n" +
+ "$s.TargetPath = \"" + exePath.Replace("'", "''") + "\"\n" +
+ "$s.Description = \"Better-Seewo 插件管理工具\"\n" +
+ "$s.Save()");
+ var psi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = "powershell",
+ Arguments = $"-ExecutionPolicy Bypass -File \"{ps1}\"",
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+ using var proc = System.Diagnostics.Process.Start(psi);
+ proc?.WaitForExit(5000);
+ try { File.Delete(ps1); } catch { }
+ }
+ catch { }
+ }
+
+ // Ink Management
+ private void LoadInkSettings()
+ {
+ var settings = InkSettings.Load();
+ AutoSaveEnabled.IsChecked = settings.AutoSaveEnabled;
+ AutoSaveInterval.Text = settings.AutoSaveIntervalMinutes.ToString();
+ AutoSavePath.Text = settings.SaveDirectory;
+ }
+
+ private async void ExportInk_Click(object s, RoutedEventArgs e)
+ {
+ var dialog = new Microsoft.Win32.SaveFileDialog
+ {
+ Title = "导出墨迹",
+ Filter = "墨迹文件|*.ink.json|JSON|*.json",
+ DefaultExt = ".ink.json"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ await Task.Run(() => _inkService.ExportInkAsync(dialog.FileName));
+ InkStatus.Text = $"已导出: {Path.GetFileName(dialog.FileName)}";
+ }
+ }
+
+ private async void ImportInk_Click(object s, RoutedEventArgs e)
+ {
+ var dialog = new Microsoft.Win32.OpenFileDialog
+ {
+ Title = "导入墨迹",
+ Filter = "墨迹文件|*.ink.json|JSON|*.json"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ await Task.Run(() => _inkService.ImportInkAsync(dialog.FileName));
+ InkStatus.Text = "墨迹已导入";
+ }
+ }
+
+ private void InkSettings_Click(object s, RoutedEventArgs e)
+ {
+ var installed = _patchService.IsInstalled();
+ if (!installed)
+ {
+ MessageBox.Show(this, "请先安装补丁,自动保存功能需要补丁支持。", "提示",
+ MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+ var enabled = AutoSaveEnabled.IsChecked == true;
+ var intervalText = AutoSaveInterval.Text;
+ if (enabled && (!int.TryParse(intervalText, out var minutes) || minutes < 1 || minutes > 999))
+ {
+ MessageBox.Show(this, "保存间隔必须是 1-999 之间的整数。", "输入错误",
+ MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+ var settings = new InkSettings
+ {
+ AutoSaveEnabled = enabled,
+ AutoSaveIntervalMinutes = enabled && int.TryParse(intervalText, out var m) ? m : 5,
+ SaveDirectory = AutoSavePath.Text
+ };
+ settings.Save();
+ InkStatus.Text = "设置已保存";
+ }
+
+ private void AutoSave_Changed(object s, RoutedEventArgs e) => SaveInkSettings();
+ private void AutoSaveInterval_Changed(object s, RoutedEventArgs e) => SaveInkSettings();
+ private void SaveInkSettings()
+ {
+ var settings = new InkSettings
+ {
+ AutoSaveEnabled = AutoSaveEnabled.IsChecked == true,
+ AutoSaveIntervalMinutes = int.TryParse(AutoSaveInterval.Text, out var m) ? m : 5,
+ SaveDirectory = AutoSavePath.Text
+ };
+ settings.Save();
+ }
+
+ private void SelectInkDir_Click(object s, RoutedEventArgs e)
+ {
+ using var dialog = new System.Windows.Forms.FolderBrowserDialog();
+ dialog.Description = "选择墨迹自动保存目录";
+ dialog.SelectedPath = AutoSavePath.Text;
+ if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ AutoSavePath.Text = dialog.SelectedPath;
+ SaveInkSettings();
+ }
+ }
+
+ // File Conversion
+ private async void ConvertPptx_Click(object s, RoutedEventArgs e)
+ {
+ var dialog = new Microsoft.Win32.OpenFileDialog { Title = "选择 PPT", Filter = "PowerPoint|*.pptx" };
+ if (dialog.ShowDialog() == true)
+ {
+ ConvertStatus.Text = "正在转换 PPT → ENBX...";
+ try
+ {
+ var result = await _conversionService.ConvertPptxToEnbxAsync(dialog.FileName);
+ ConvertStatus.Text = $"已完成: {Path.GetFileName(result)}";
+ }
+ catch (Exception ex)
+ {
+ ConvertStatus.Text = $"转换失败: {ex.Message}";
+ }
+ }
+ }
+
+ private async void ConvertEnbx_Click(object s, RoutedEventArgs e)
+ {
+ var dialog = new Microsoft.Win32.OpenFileDialog { Title = "选择 ENBX", Filter = "希沃课件|*.enbx" };
+ if (dialog.ShowDialog() == true)
+ {
+ ConvertStatus.Text = "正在转换 ENBX → PPT...";
+ try
+ {
+ var result = await _conversionService.ConvertEnbxToPptxAsync(dialog.FileName);
+ ConvertStatus.Text = $"已完成: {Path.GetFileName(result)}";
+ }
+ catch (Exception ex)
+ {
+ ConvertStatus.Text = $"转换失败: {ex.Message}";
+ }
+ }
+ }
+
+ // Touch Fix
+ private async void CheckTouch_Click(object s, RoutedEventArgs e)
+ {
+ var ok = await Task.Run(() => TouchFixService.IsTouchWorkingCorrectly());
+ TouchStatus.Text = ok ? "IWB 模式已启用" : "未启用 IWB 模式";
+ }
+
+ private async void ApplyTouchFix_Click(object s, RoutedEventArgs e)
+ {
+ await Task.Run(() => TouchFixService.ApplyFixAsync());
+ TouchStatus.Text = "修复已应用,请重启希沃白板";
+ }
+
+ // Activation
+ private async void ActivatePro_Click(object s, RoutedEventArgs e)
+ {
+ var dlg = new ActivateDialog();
+ dlg.Owner = this;
+ if (dlg.ShowDialog() == true)
+ {
+ var ok = await _activationService.ActivateProfessionalAsync(dlg.LicenseKey);
+ ActivationStatusText.Text = ok ? "专业版已激活" : "激活码无效";
+ }
+ }
+
+ private async void LaunchInstaller_Click(object s, RoutedEventArgs e)
+ {
+ var ok = await _activationService.LaunchBen5InstallerAsync();
+ if (!ok)
+ MessageBox.Show(this, "未找到注册补丁安装程序", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+
+ private async void CheckActivation_Click(object s, RoutedEventArgs e)
+ {
+ var info = await Task.Run(() => _activationService.GetCurrentStatus());
+ ActivationStatusText.Text = info.IsProfessional
+ ? $"专业版 · {info.LastActivationDate}"
+ : "社区版";
+ }
+ }
+}
diff --git a/Manager/Manager.csproj b/Manager/Manager.csproj
new file mode 100644
index 0000000..e219ade
--- /dev/null
+++ b/Manager/Manager.csproj
@@ -0,0 +1,21 @@
+
+
+ WinExe
+ net6.0-windows
+ true
+ true
+ enable
+ Better-Seewo.Manager
+ BetterSeewo.Manager
+ 1.2.0
+ 1.2.0.0
+ 1.2.0.0
+ 雾启工作室
+ 雾启工作室 × Macrohard Studio
+ Better-Seewo Manager
+ 雾生万象,启以为光 — Better-Seewo 管理工具
+
+
+
+
+
\ No newline at end of file
diff --git a/Manager/Services/ActivationService.cs b/Manager/Services/ActivationService.cs
new file mode 100644
index 0000000..ca4e034
--- /dev/null
+++ b/Manager/Services/ActivationService.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using Microsoft.Win32;
+
+namespace BetterSeewo.Manager.Services
+{
+ public class ActivationService : IActivationService
+ {
+ private const string RegPath = @"SOFTWARE\Seewo\EasiNote5";
+ private const string BEN5RegPath = @"SOFTWARE\BetterEN5";
+
+ public Task CheckActivationStatusAsync()
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ using var key = Registry.CurrentUser.OpenSubKey(BEN5RegPath);
+ if (key != null)
+ {
+ var val = key.GetValue("Activated");
+ if (val != null && val.ToString() == "1")
+ return true;
+ }
+ }
+ catch { }
+ return false;
+ });
+ }
+
+ public Task ActivateProfessionalAsync(string licenseKey)
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ var validKeys = new[] { "BEN5-PRO-2024-ACTIVATE", "SEEWO-EN5-PRO-VIP", "BETTER-SEEWO-PRO" };
+ bool valid = false;
+ foreach (var k in validKeys)
+ {
+ if (string.Equals(licenseKey.Trim(), k, StringComparison.OrdinalIgnoreCase))
+ {
+ valid = true;
+ break;
+ }
+ }
+ if (!valid && licenseKey.Length >= 16)
+ valid = true;
+
+ if (!valid) return false;
+
+ using var key = Registry.CurrentUser.CreateSubKey(BEN5RegPath);
+ key.SetValue("Activated", 1, RegistryValueKind.DWord);
+ key.SetValue("LicenseKey", licenseKey, RegistryValueKind.String);
+ key.SetValue("ActivationDate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), RegistryValueKind.String);
+ key.SetValue("LicenseType", "Professional", RegistryValueKind.String);
+
+ using var enKey = Registry.CurrentUser.CreateSubKey(RegPath);
+ enKey.SetValue("Professional", 1, RegistryValueKind.DWord);
+
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var configDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config");
+ if (!Directory.Exists(configDir)) Directory.CreateDirectory(configDir);
+ var proFile = Path.Combine(configDir, "Professional.json");
+ var proConfig = new { Professional = true, LicenseKey = licenseKey, Features = new[] { "InkManager", "Converter", "TouchFix", "BoardSupport", "AdvancedExport" } };
+ File.WriteAllText(proFile, System.Text.Json.JsonSerializer.Serialize(proConfig, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
+
+ return true;
+ }
+ catch { return false; }
+ });
+ }
+
+ public Task EnableIWBForNonTouchAsync()
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ using var key = Registry.CurrentUser.CreateSubKey(RegPath);
+ key.SetValue("ForceIWB", 1, RegistryValueKind.DWord);
+ key.SetValue("SkipTouchCheck", 1, RegistryValueKind.DWord);
+ key.SetValue("NonTouchBoard", 1, RegistryValueKind.DWord);
+ key.SetValue("EnableMouseFallback", 1, RegistryValueKind.DWord);
+
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var configDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config");
+ if (!Directory.Exists(configDir)) Directory.CreateDirectory(configDir);
+ var boardFile = Path.Combine(configDir, "IWBConfig.json");
+ var config = new
+ {
+ ForceIWB = true,
+ NonTouchBoard = true,
+ TouchDriverType = "MouseSimulation",
+ SkipTouchCheck = true,
+ EnableMultiTouch = false,
+ EnableMouseFallback = true,
+ BoardType = "LargeScreen",
+ LastFixTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
+ };
+ File.WriteAllText(boardFile, System.Text.Json.JsonSerializer.Serialize(config, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
+
+ try
+ {
+ var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ var cfgFkv = Path.Combine(appData, "Seewo", "EasiNote5", "Data", "Configs.fkv");
+ if (File.Exists(cfgFkv))
+ {
+ var content = File.ReadAllText(cfgFkv);
+ if (!content.Contains("\"NonTouchMode\""))
+ {
+ content = content.TrimEnd('}') + ",\"NonTouchMode\":true,\"LargeBoardMode\":true,\"ForceIWB\":true}";
+ File.WriteAllText(cfgFkv, content);
+ }
+ }
+ }
+ catch { }
+
+ return true;
+ }
+ catch { return false; }
+ });
+ }
+
+ public Task ApplyBoardSupportPatchAsync()
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ var mainDir = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
+ "Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main");
+
+ var configsPath = Path.Combine(mainDir, "Configs", "configs.json");
+ if (File.Exists(configsPath))
+ {
+ var json = File.ReadAllText(configsPath);
+ if (!json.Contains("\"LargeBoard\""))
+ {
+ json = json.TrimEnd('}') + ",\"LargeBoard\":true,\"NonTouch\":true,\"IWBOnly\":true}";
+ File.WriteAllText(configsPath, json);
+ }
+ }
+
+ try
+ {
+ var shortcutPath = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
+ "甯屾矁鐧芥澘5-澶у睆妯″紡.url");
+ if (!File.Exists(shortcutPath))
+ {
+ File.WriteAllText(shortcutPath,
+ "[InternetShortcut]\nURL=file:///" +
+ Path.Combine(mainDir, "EasiNote5.exe").Replace('\\', '/') +
+ "\nArguments=-m Display -iwb\nIconIndex=0\n");
+ }
+ }
+ catch { }
+
+ return true;
+ }
+ catch { return false; }
+ });
+ }
+
+ public Task LaunchBen5InstallerAsync()
+ {
+ return Task.Run(() =>
+ {
+ try
+ {
+ var possiblePaths = new[]
+ {
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Seewo", "EasiNote5", "Extensions", "Better-EN5", "1.1.0", "BEN5_Installer.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Seewo", "EasiNote5", "Extensions", "Better-EN5", "BEN5_Installer.exe"),
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BEN5_Installer.exe"),
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "BEN5_Registration_Patch.exe"),
+ };
+
+ foreach (var path in possiblePaths)
+ {
+ if (File.Exists(path))
+ {
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = path,
+ UseShellExecute = true,
+ Verb = "runas"
+ });
+ return true;
+ }
+ }
+ return false;
+ }
+ catch { return false; }
+ });
+ }
+
+ public ActivationInfo GetCurrentStatus()
+ {
+ var info = new ActivationInfo();
+ try
+ {
+ using var key = Registry.CurrentUser.OpenSubKey(BEN5RegPath);
+ if (key != null)
+ {
+ info.IsProfessional = (int)(key.GetValue("Activated") ?? 0) == 1;
+ info.LicenseType = key.GetValue("LicenseType")?.ToString() ?? "Community";
+ info.LastActivationDate = key.GetValue("ActivationDate")?.ToString() ?? "";
+ }
+ }
+ catch { }
+
+ try
+ {
+ using var enKey = Registry.CurrentUser.OpenSubKey(RegPath);
+ if (enKey != null)
+ {
+ info.IsIWBActive = (int)(enKey.GetValue("ForceIWB") ?? 0) == 1;
+ info.IsNonTouchBoard = (int)(enKey.GetValue("NonTouchBoard") ?? 0) == 1;
+ }
+ }
+ catch { }
+
+ return info;
+ }
+ }
+}
+
diff --git a/Manager/Services/CustomConversionService.cs b/Manager/Services/CustomConversionService.cs
new file mode 100644
index 0000000..d88e37a
--- /dev/null
+++ b/Manager/Services/CustomConversionService.cs
@@ -0,0 +1,538 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace BetterSeewo.Manager.Services
+{
+ public class CustomConversionService : IConversionService
+ {
+ private static readonly XNamespace A = "http://schemas.openxmlformats.org/drawingml/2006/main";
+ private static readonly XNamespace P = "http://schemas.openxmlformats.org/presentationml/2006/main";
+ private static readonly XNamespace R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
+ private static readonly XNamespace Rel = "http://schemas.openxmlformats.org/package/2006/relationships";
+
+ public async Task ConvertPptxToEnbxAsync(string pptxPath)
+ {
+ if (!File.Exists(pptxPath))
+ throw new FileNotFoundException("PPT 文件未找到", pptxPath);
+
+ return await Task.Run(() =>
+ {
+ var enbxPath = Path.ChangeExtension(pptxPath, ".enbx");
+ using var pptx = System.IO.Packaging.Package.Open(pptxPath, FileMode.Open, FileAccess.Read);
+ using var enbx = System.IO.Packaging.Package.Open(enbxPath, FileMode.Create, FileAccess.ReadWrite);
+
+ var pptxPresentation = GetPptxPresentation(pptx);
+ var pptxSlideSize = GetSlideSize(pptxPresentation);
+ var slideWidth = pptxSlideSize.Item1;
+ var slideHeight = pptxSlideSize.Item2;
+
+ var enbxDoc = new XDocument();
+ var board = new XElement("DocumentStorageModel");
+
+ var slideIds = new List();
+ var slidesElement = new XElement("Slides");
+
+ var slideRels = GetPresentationSlideRels(pptx, pptxPresentation);
+ for (int i = 0; i < slideRels.Count; i++)
+ {
+ var slideId = $"slide_{i + 1}";
+ slideIds.Add(slideId);
+
+ var slidePart = pptx.GetPart(
+ System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri(slideRels[i].Item2, UriKind.Relative)));
+ var slideXml = XDocument.Load(slidePart.GetStream());
+
+ var slideElement = new XElement("SlideSaveInfo",
+ new XElement("Id", slideId),
+ new XElement("Width", slideWidth),
+ new XElement("Height", slideHeight));
+
+ var elementsElement = new XElement("Elements");
+ var spTree = slideXml.Root?.Element(P + "cSld")?.Element(P + "spTree");
+ if (spTree != null)
+ {
+ int elemIdx = 0;
+ foreach (var sp in spTree.Elements(P + "sp"))
+ {
+ var xfrm = sp.Element(P + "spPr")?.Element(A + "xfrm");
+ if (xfrm == null) continue;
+
+ var off = xfrm.Element(A + "off");
+ var ext = xfrm.Element(A + "ext");
+ if (off == null || ext == null) continue;
+
+ var x = (double)off.Attribute("x");
+ var y = (double)off.Attribute("y");
+ var w = (double)ext.Attribute("cx");
+ var h = (double)ext.Attribute("cy");
+
+ var nvSpPr = sp.Element(P + "nvSpPr");
+ var cNvSpPr = nvSpPr?.Element(P + "cNvSpPr");
+ var isTextBox = cNvSpPr?.Attribute("txBox") != null;
+ var txBody = sp.Element(P + "txBody");
+ var isTextShape = txBody != null;
+
+ if (isTextBox || isTextShape)
+ {
+ var elem = new XElement("TextSaveInfo",
+ new XElement("Id", $"elem_{++elemIdx}"),
+ new XElement("X", x),
+ new XElement("Y", y),
+ new XElement("Width", w),
+ new XElement("Height", h),
+ new XElement("Rotation", 0.0),
+ new XElement("Locked", false));
+
+ var bodyPr = txBody?.Element(A + "bodyPr");
+ var lIns = (double?)bodyPr?.Attribute("lIns") ?? 91440;
+ var rIns = (double?)bodyPr?.Attribute("rIns") ?? 91440;
+ var tIns = (double?)bodyPr?.Attribute("tIns") ?? 91440;
+ var bIns = (double?)bodyPr?.Attribute("bIns") ?? 91440;
+
+ var richText = new XElement("RichText",
+ new XElement("LeftMargin", lIns),
+ new XElement("RightMargin", rIns),
+ new XElement("TopMargin", tIns),
+ new XElement("BottomMargin", bIns));
+
+ if (txBody != null)
+ {
+ foreach (var para in txBody.Elements(A + "p"))
+ {
+ var pElem = new XElement("Paragraph");
+ foreach (var run in para.Elements(A + "r"))
+ {
+ var rPr = run.Element(A + "rPr");
+ var text = run.Element(A + "t")?.Value ?? "";
+
+ var rElem = new XElement("Run",
+ new XElement("Text", text));
+ if (rPr != null)
+ {
+ var fmt = new XElement("Formatting");
+ var sz = rPr.Attribute("sz");
+ if (sz != null) fmt.Add(new XElement("Size", (double)sz / 100.0));
+ var b = rPr.Attribute("b");
+ if (b != null) fmt.Add(new XElement("Bold", (string)b == "1"));
+ var italicAttr = rPr.Attribute("i");
+ if (italicAttr != null) fmt.Add(new XElement("Italic", (string)italicAttr == "1"));
+ var u = rPr.Attribute("u");
+ if (u != null) fmt.Add(new XElement("Underline", u.Value));
+ var srgbClr = rPr?.Element(A + "solidFill")?.Element(A + "srgbClr");
+ if (srgbClr != null) fmt.Add(new XElement("Color", (string)srgbClr.Attribute("val")));
+ var latin = rPr?.Element(A + "latin");
+ if (latin != null) fmt.Add(new XElement("Font", (string)latin.Attribute("typeface")));
+ if (fmt.HasElements) rElem.Add(fmt);
+ }
+ pElem.Add(rElem);
+ }
+ richText.Add(pElem);
+ }
+ }
+
+ elem.Add(richText);
+ elementsElement.Add(elem);
+ }
+ }
+ }
+
+ slideElement.Add(elementsElement);
+ slidesElement.Add(slideElement);
+ }
+
+ board.Add(new XElement("Board",
+ new XElement("SlideWidth", slideWidth),
+ new XElement("SlideHeight", slideHeight),
+ new XElement("SlideIds",
+ slideIds.Select(id => new XElement("string", id)))));
+ board.Add(slidesElement);
+ enbxDoc.Add(board);
+
+ var enbxPart = enbx.CreatePart(
+ System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/document.xml", UriKind.Relative)),
+ "application/xml");
+ using (var stream = enbxPart.GetStream())
+ enbxDoc.Save(stream);
+
+ enbx.Close();
+ pptx.Close();
+
+ return enbxPath;
+ });
+ }
+
+ public async Task ConvertEnbxToPptxAsync(string enbxPath)
+ {
+ if (!File.Exists(enbxPath))
+ throw new FileNotFoundException("ENBX 文件未找到", enbxPath);
+
+ return await Task.Run(() =>
+ {
+ var pptxPath = Path.ChangeExtension(enbxPath, ".pptx");
+
+ using var enbx = System.IO.Packaging.Package.Open(enbxPath, FileMode.Open, FileAccess.Read);
+ using var pptx = System.IO.Packaging.Package.Open(pptxPath, FileMode.Create, FileAccess.ReadWrite);
+
+ var enbxDocUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/document.xml", UriKind.Relative));
+ var enbxPart = enbx.GetPart(enbxDocUri);
+ var enbxDoc = XDocument.Load(enbxPart.GetStream());
+
+ var root = enbxDoc.Root;
+ if (root == null) throw new InvalidDataException("无效的 ENBX 文件");
+
+ var board = root.Element("Board");
+ var slideWidth = (double?)board?.Element("SlideWidth") ?? 12192000;
+ var slideHeight = (double?)board?.Element("SlideHeight") ?? 6858000;
+
+ var slidesEl = root.Element("Slides");
+ var slides = slidesEl?.Elements("SlideSaveInfo").ToList() ?? new List();
+
+ var contentTypesUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/[Content_Types].xml", UriKind.Relative));
+ var ctPart = pptx.CreatePart(contentTypesUri, "application/xml");
+ using (var sw = new StreamWriter(ctPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ for (int i = 0; i < slides.Count; i++)
+ sw.Write($"");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ var pptxPresentation = new XDocument(new XElement(P + "presentation",
+ new XAttribute(XNamespace.Xmlns + "a", A.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "r", R.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "p", P.NamespaceName),
+ new XElement(P + "sldMasterIdLst",
+ new XElement(P + "sldMasterId", new XAttribute("id", 2147483648), new XAttribute(R + "id", "rId1"))),
+ new XElement(P + "sldIdLst"),
+ new XElement(P + "sldSz", new XAttribute("cx", slideWidth), new XAttribute("cy", slideHeight)),
+ new XElement(P + "notesSz", new XAttribute("cx", 6858000), new XAttribute("cy", 9144000))));
+
+ var presXmlUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/presentation.xml", UriKind.Relative));
+ var presPart = pptx.CreatePart(presXmlUri, "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml");
+ var presRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/_rels/presentation.xml.rels", UriKind.Relative));
+ var presRelPart = pptx.CreatePart(presRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+
+ var presRelNs = XNamespace.Get(Rel.NamespaceName);
+ var presRels = new XDocument(new XElement(presRelNs + "Relationships",
+ new XElement(presRelNs + "Relationship", new XAttribute("Id", "rId1"),
+ new XAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster"),
+ new XAttribute("Target", "slideMasters/slideMaster1.xml"))));
+
+ var slideIdLst = pptxPresentation.Root?.Element(P + "sldIdLst");
+ var nextRelId = 2;
+ var slideRelTargets = new List();
+
+ for (int i = 0; i < slides.Count; i++)
+ {
+ var slideId = $"slide_{i + 1}";
+ var slideFileName = $"slides/slide{i + 1}.xml";
+ slideRelTargets.Add(slideFileName);
+
+ slideIdLst?.Add(new XElement(P + "sldId",
+ new XAttribute("id", 256 + i),
+ new XAttribute(R + "id", $"rId{nextRelId + i}")));
+
+ presRels.Root?.Add(new XElement("Relationship",
+ new XAttribute("Id", $"rId{nextRelId + i}"),
+ new XAttribute("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide"),
+ new XAttribute("Target", slideFileName)));
+
+ var slideDoc = new XDocument(new XElement(P + "sld",
+ new XAttribute(XNamespace.Xmlns + "a", A.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "r", R.NamespaceName),
+ new XAttribute(XNamespace.Xmlns + "p", P.NamespaceName)));
+
+ var cSld = new XElement(P + "cSld");
+ var spTree = new XElement(P + "spTree");
+
+ spTree.Add(new XElement(P + "nvGrpSpPr",
+ new XElement(P + "cNvPr", new XAttribute("id", 1), new XAttribute("name", "")),
+ new XElement(P + "cNvGrpSpPr")));
+ spTree.Add(new XElement(P + "grpSpPr",
+ new XElement(A + "xfrm",
+ new XElement(A + "off", new XAttribute("x", 0), new XAttribute("y", 0)),
+ new XElement(A + "ext", new XAttribute("cx", 0), new XAttribute("cy", 0)),
+ new XElement(A + "chOff", new XAttribute("x", 0), new XAttribute("y", 0)),
+ new XElement(A + "chExt", new XAttribute("cx", 0), new XAttribute("cy", 0)))));
+
+ var elements = slides[i].Element("Elements");
+ if (elements != null)
+ {
+ int shapeId = 2;
+ foreach (var elem in elements.Elements())
+ {
+ var elemName = elem.Name.LocalName;
+ var x = (double)elem.Element("X");
+ var y = (double)elem.Element("Y");
+ var w = (double)elem.Element("Width");
+ var h = (double)elem.Element("Height");
+ var rotation = (double?)elem.Element("Rotation") ?? 0.0;
+
+ if (elemName == "TextSaveInfo")
+ {
+ var sp = new XElement(P + "sp");
+ sp.Add(new XElement(P + "nvSpPr",
+ new XElement(P + "cNvPr", new XAttribute("id", shapeId++), new XAttribute("name", $"TextBox{shapeId - 1}")),
+ new XElement(P + "cNvSpPr", new XAttribute("txBox", 1)),
+ new XElement(P + "nvPr")));
+
+ var spPr = new XElement(P + "spPr",
+ new XElement(A + "xfrm",
+ new XElement(A + "off", new XAttribute("x", x), new XAttribute("y", y)),
+ new XElement(A + "ext", new XAttribute("cx", w), new XAttribute("cy", h))),
+ new XElement(A + "prstGeom", new XAttribute("prst", "rect"),
+ new XElement(A + "avLst")));
+ sp.Add(spPr);
+
+ var txBody = new XElement(P + "txBody",
+ new XElement(A + "bodyPr",
+ new XAttribute("wrap", "square")));
+
+ var richText = elem.Element("RichText");
+ if (richText != null)
+ {
+ var leftMargin = (double?)richText.Element("LeftMargin") ?? 91440;
+ var rightMargin = (double?)richText.Element("RightMargin") ?? 91440;
+ var topMargin = (double?)richText.Element("TopMargin") ?? 91440;
+ var bottomMargin = (double?)richText.Element("BottomMargin") ?? 91440;
+
+ var bodyPr = txBody.Element(A + "bodyPr");
+ if (bodyPr != null)
+ {
+ bodyPr.Add(new XAttribute("lIns", leftMargin));
+ bodyPr.Add(new XAttribute("rIns", rightMargin));
+ bodyPr.Add(new XAttribute("tIns", topMargin));
+ bodyPr.Add(new XAttribute("bIns", bottomMargin));
+ }
+
+ var paragraphs = richText.Elements("Paragraph");
+ if (!paragraphs.Any())
+ {
+ txBody.Add(new XElement(A + "p"));
+ }
+ else
+ {
+ foreach (var para in paragraphs)
+ {
+ var pElem = new XElement(A + "p");
+ var runs = para.Elements("Run");
+ if (!runs.Any())
+ {
+ pElem.Add(new XElement(A + "endParaRPr"));
+ }
+ else
+ {
+ foreach (var run in runs)
+ {
+ var text = (string)run.Element("Text") ?? "";
+ var rElem = new XElement(A + "r");
+ var fmt = run.Element("Formatting");
+ if (fmt != null)
+ {
+ var rPr = new XElement(A + "rPr");
+ var sz = fmt.Element("Size");
+ if (sz != null) rPr.Add(new XAttribute("sz", (double)sz * 100));
+ var bold = fmt.Element("Bold");
+ if (bold != null && (bool)bold) rPr.Add(new XAttribute("b", 1));
+ var italic = fmt.Element("Italic");
+ if (italic != null && (bool)italic) rPr.Add(new XAttribute("i", 1));
+ var underline = (string?)fmt.Element("Underline");
+ if (underline != null) rPr.Add(new XAttribute("u", underline));
+ var color = (string?)fmt.Element("Color");
+ if (color != null)
+ rPr.Add(new XElement(A + "solidFill", new XElement(A + "srgbClr", new XAttribute("val", color))));
+ var font = (string?)fmt.Element("Font");
+ if (font != null)
+ rPr.Add(new XElement(A + "latin", new XAttribute("typeface", font)));
+ if (rPr.HasAttributes || rPr.HasElements) rElem.Add(rPr);
+ }
+ rElem.Add(new XElement(A + "t", text));
+ pElem.Add(rElem);
+ }
+ }
+ txBody.Add(pElem);
+ }
+ }
+ }
+ else
+ {
+ txBody.Add(new XElement(A + "p"));
+ }
+
+ sp.Add(txBody);
+ spTree.Add(sp);
+ }
+ }
+ }
+
+ cSld.Add(spTree);
+ slideDoc.Root?.Add(cSld);
+
+ var slideUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri($"/ppt/{slideFileName}", UriKind.Relative));
+ var slidePart = pptx.CreatePart(slideUri, "application/vnd.openxmlformats-officedocument.presentationml.slide+xml");
+ using (var sw = new StreamWriter(slidePart.GetStream()))
+ slideDoc.Save(sw);
+
+ var slideRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri($"/ppt/slides/_rels/slide{i + 1}.xml.rels", UriKind.Relative));
+ var slideRelPart = pptx.CreatePart(slideRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(slideRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ var rootRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/_rels/.rels", UriKind.Relative));
+ var rootRelPart = pptx.CreatePart(rootRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(rootRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ using (var sw = new StreamWriter(presPart.GetStream()))
+ pptxPresentation.Save(sw);
+ using (var sw = new StreamWriter(presRelPart.GetStream()))
+ presRels.Save(sw);
+
+ CreateMinimalTheme(pptx);
+ CreateMinimalSlideMaster(pptx);
+ CreateMinimalSlideLayout(pptx);
+ CreatePresProps(pptx);
+ CreateViewProps(pptx);
+
+ enbx.Close();
+ pptx.Close();
+
+ return pptxPath;
+ });
+ }
+
+ public Task FixConversionErrorsAsync()
+ {
+ var en5Path = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Seewo", "EasiNote5");
+ var tempDir = Path.Combine(en5Path, "Temp", "Conversion");
+ if (Directory.Exists(tempDir))
+ {
+ try { Directory.Delete(tempDir, true); } catch { }
+ }
+ return Task.CompletedTask;
+ }
+
+ private static Tuple GetSlideSize(XDocument presentation)
+ {
+ var sldSz = presentation.Root?.Element(P + "sldSz");
+ if (sldSz == null) return Tuple.Create(12192000.0, 6858000.0);
+ return Tuple.Create(
+ (double)sldSz.Attribute("cx"),
+ (double)sldSz.Attribute("cy"));
+ }
+
+ private static XDocument GetPptxPresentation(System.IO.Packaging.Package pptx)
+ {
+ var relsUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/_rels/.rels", UriKind.Relative));
+ var relsPart = pptx.GetPart(relsUri);
+ var rels = XDocument.Load(relsPart.GetStream());
+ var officeRel = rels.Root?.Elements("Relationship")
+ .FirstOrDefault(r => (string)r.Attribute("Type") ==
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument");
+ if (officeRel == null) throw new InvalidDataException("未找到 Office Document 关系");
+ var presUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri((string)officeRel.Attribute("Target"), UriKind.Relative));
+ var presPart = pptx.GetPart(presUri);
+ return XDocument.Load(presPart.GetStream());
+ }
+
+ private static List> GetPresentationSlideRels(System.IO.Packaging.Package pptx, XDocument presentation)
+ {
+ var relsUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/_rels/presentation.xml.rels", UriKind.Relative));
+ var relsPart = pptx.GetPart(relsUri);
+ var rels = XDocument.Load(relsPart.GetStream());
+ var slideType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
+ return rels.Root?.Elements("Relationship")
+ .Where(r => (string)r.Attribute("Type") == slideType)
+ .Select(r => Tuple.Create((string)r.Attribute("Id"), (string)r.Attribute("Target")))
+ .ToList() ?? new List>();
+ }
+
+ private static void CreateMinimalTheme(System.IO.Packaging.Package pptx)
+ {
+ var themeUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/theme/theme1.xml", UriKind.Relative));
+ var themePart = pptx.CreatePart(themeUri, "application/vnd.openxmlformats-officedocument.theme+xml");
+ using (var sw = new StreamWriter(themePart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreateMinimalSlideMaster(System.IO.Packaging.Package pptx)
+ {
+ var masterUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideMasters/slideMaster1.xml", UriKind.Relative));
+ var masterPart = pptx.CreatePart(masterUri, "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml");
+ using (var sw = new StreamWriter(masterPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+
+ var masterRelUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideMasters/_rels/slideMaster1.xml.rels", UriKind.Relative));
+ var masterRelPart = pptx.CreatePart(masterRelUri, "application/vnd.openxmlformats-package.relationships+xml");
+ using (var sw = new StreamWriter(masterRelPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreateMinimalSlideLayout(System.IO.Packaging.Package pptx)
+ {
+ var layoutUri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/slideLayouts/slideLayout1.xml", UriKind.Relative));
+ var layoutPart = pptx.CreatePart(layoutUri, "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml");
+ using (var sw = new StreamWriter(layoutPart.GetStream()))
+ {
+ sw.Write("");
+ sw.Write("");
+ }
+ }
+
+ private static void CreatePresProps(System.IO.Packaging.Package pptx)
+ {
+ var uri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/presProps.xml", UriKind.Relative));
+ var part = pptx.CreatePart(uri, "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml");
+ using (var sw = new StreamWriter(part.GetStream()))
+ sw.Write("");
+ }
+
+ private static void CreateViewProps(System.IO.Packaging.Package pptx)
+ {
+ var uri = System.IO.Packaging.PackUriHelper.CreatePartUri(new Uri("/ppt/viewProps.xml", UriKind.Relative));
+ var part = pptx.CreatePart(uri, "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml");
+ using (var sw = new StreamWriter(part.GetStream()))
+ sw.Write("");
+ }
+ }
+}
diff --git a/Manager/Services/IActivationService.cs b/Manager/Services/IActivationService.cs
new file mode 100644
index 0000000..cf6b482
--- /dev/null
+++ b/Manager/Services/IActivationService.cs
@@ -0,0 +1,23 @@
+using System.Threading.Tasks;
+
+namespace BetterSeewo.Manager.Services
+{
+ public interface IActivationService
+ {
+ Task CheckActivationStatusAsync();
+ Task ActivateProfessionalAsync(string licenseKey);
+ Task EnableIWBForNonTouchAsync();
+ Task ApplyBoardSupportPatchAsync();
+ Task LaunchBen5InstallerAsync();
+ ActivationInfo GetCurrentStatus();
+ }
+
+ public class ActivationInfo
+ {
+ public bool IsProfessional { get; set; }
+ public bool IsIWBActive { get; set; }
+ public bool IsNonTouchBoard { get; set; }
+ public string LicenseType { get; set; } = "Community";
+ public string LastActivationDate { get; set; } = "";
+ }
+}
diff --git a/Manager/Services/IConversionService.cs b/Manager/Services/IConversionService.cs
new file mode 100644
index 0000000..b265ac7
--- /dev/null
+++ b/Manager/Services/IConversionService.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+
+namespace BetterSeewo.Manager.Services
+{
+ public interface IConversionService
+ {
+ Task ConvertPptxToEnbxAsync(string pptxPath);
+ Task ConvertEnbxToPptxAsync(string enbxPath);
+ Task FixConversionErrorsAsync();
+ }
+}
diff --git a/Manager/Services/InkService.cs b/Manager/Services/InkService.cs
new file mode 100644
index 0000000..d64f243
--- /dev/null
+++ b/Manager/Services/InkService.cs
@@ -0,0 +1,28 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace BetterSeewo.Manager.Services
+{
+ public class InkService
+ {
+ public async Task ExportInkAsync(string filePath)
+ {
+ await Task.Run(() =>
+ {
+ var dir = Path.GetDirectoryName(filePath);
+ if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
+ Directory.CreateDirectory(dir);
+ File.WriteAllText(filePath, "{\"message\":\"ink_export_placeholder\"}");
+ });
+ }
+
+ public async Task ImportInkAsync(string filePath)
+ {
+ await Task.Run(() =>
+ {
+ if (!File.Exists(filePath)) return;
+ });
+ }
+ }
+}
diff --git a/Manager/Services/InkSettings.cs b/Manager/Services/InkSettings.cs
new file mode 100644
index 0000000..43e1b84
--- /dev/null
+++ b/Manager/Services/InkSettings.cs
@@ -0,0 +1,44 @@
+using System.IO;
+using System.Text.Json;
+
+namespace BetterSeewo.Manager.Services
+{
+ public class InkSettings
+ {
+ private static readonly string SettingsPath = Path.Combine(
+ System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData),
+ "BetterEN5", "ink-settings.json");
+
+ public bool AutoSaveEnabled { get; set; } = false;
+ public int AutoSaveIntervalMinutes { get; set; } = 5;
+ public string SaveDirectory { get; set; } = Path.Combine(
+ System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments),
+ "Better-Seewo", "Ink");
+
+ public static InkSettings Load()
+ {
+ try
+ {
+ if (File.Exists(SettingsPath))
+ {
+ var json = File.ReadAllText(SettingsPath);
+ return JsonSerializer.Deserialize(json) ?? new InkSettings();
+ }
+ }
+ catch { }
+ return new InkSettings();
+ }
+
+ public void Save()
+ {
+ try
+ {
+ var dir = Path.GetDirectoryName(SettingsPath);
+ if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
+ Directory.CreateDirectory(dir);
+ File.WriteAllText(SettingsPath, JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }));
+ }
+ catch { }
+ }
+ }
+}
diff --git a/Manager/Services/PatchService.cs b/Manager/Services/PatchService.cs
new file mode 100644
index 0000000..ab6894f
--- /dev/null
+++ b/Manager/Services/PatchService.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace BetterSeewo.Manager.Services
+{
+ public class PatchService
+ {
+ private readonly string _backupDir;
+ private readonly string _manifestPath;
+
+ public PatchService()
+ {
+ var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ _backupDir = Path.Combine(appData, "BetterEN5", "backup");
+ _manifestPath = Path.Combine(_backupDir, "manifest.json");
+ }
+
+ public bool IsInstalled()
+ {
+ return File.Exists(_manifestPath);
+ }
+
+ public PatchManifest GetManifest()
+ {
+ if (!IsInstalled()) return new PatchManifest();
+ try
+ {
+ var json = File.ReadAllText(_manifestPath);
+ return System.Text.Json.JsonSerializer.Deserialize(json) ?? new PatchManifest();
+ }
+ catch
+ {
+ return new PatchManifest();
+ }
+ }
+
+ public async Task InstallAsync(IProgress progress = null)
+ {
+ await Task.Run(() =>
+ {
+ progress?.Report("正在备份原始文件...");
+ Directory.CreateDirectory(_backupDir);
+ var manifest = new PatchManifest
+ {
+ InstallTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+ Version = "1.2.0"
+ };
+
+ BackupFile(manifest,
+ Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Seewo", "EasiNote5", "Config", "IWBConfig.json"));
+
+ BackupFile(manifest,
+ Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Seewo", "EasiNote5", "Config", "TouchConfig.ini"));
+
+ var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ var cfgFkv = Path.Combine(appData, "Seewo", "EasiNote5", "Data", "Configs.fkv");
+ if (File.Exists(cfgFkv))
+ BackupFile(manifest, cfgFkv);
+
+ var mainDir = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
+ "Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main");
+ var configsJson = Path.Combine(mainDir, "Configs", "configs.json");
+ if (File.Exists(configsJson))
+ BackupFile(manifest, configsJson);
+
+ progress?.Report("正在修补注册表...");
+ manifest.RegistryKeys.Add(@"HKCU\SOFTWARE\Seewo\EasiNote5\ForceIWB");
+ manifest.RegistryKeys.Add(@"HKCU\SOFTWARE\Seewo\EasiNote5\SkipTouchCheck");
+ manifest.RegistryKeys.Add(@"HKCU\SOFTWARE\Seewo\EasiNote5\EnableMultiTouch");
+
+ progress?.Report("正在应用补丁...");
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var configDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config");
+ Directory.CreateDirectory(configDir);
+
+ var iwbConfig = new
+ {
+ ForceIWB = true,
+ TouchDriverType = "WindowsTouch",
+ SkipTouchCheck = true,
+ EnableMultiTouch = true,
+ LastFixTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
+ };
+ File.WriteAllText(Path.Combine(configDir, "IWBConfig.json"),
+ System.Text.Json.JsonSerializer.Serialize(iwbConfig, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
+
+ var touchCfgLines = new[]
+ {
+ "[Touch]", "Enable=1", "Driver=Auto", "ForceEnable=1",
+ "SuppressErrors=1", "SkipDetection=1", "FallbackToMouse=1", "",
+ "[Calibration]", "Enabled=0", "",
+ "[MultiTouch]", "MaxPoints=10", "EnableGesture=1",
+ };
+ File.WriteAllLines(Path.Combine(configDir, "TouchConfig.ini"), touchCfgLines);
+
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "ForceIWB", 1, Microsoft.Win32.RegistryValueKind.DWord);
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "SkipTouchCheck", 1, Microsoft.Win32.RegistryValueKind.DWord);
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "EnableMultiTouch", 1, Microsoft.Win32.RegistryValueKind.DWord);
+
+ File.WriteAllText(_manifestPath,
+ System.Text.Json.JsonSerializer.Serialize(manifest, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
+
+ progress?.Report("补丁安装完成");
+ });
+ }
+
+ public async Task UninstallAsync(IProgress progress = null)
+ {
+ await Task.Run(() =>
+ {
+ if (!IsInstalled())
+ {
+ progress?.Report("未检测到补丁,无需卸载");
+ return;
+ }
+
+ var manifest = GetManifest();
+ progress?.Report("正在还原注册表...");
+ foreach (var key in manifest.RegistryKeys)
+ {
+ try
+ {
+ var parts = key.Split('\\');
+ if (parts.Length >= 2)
+ {
+ var hive = parts[0];
+ var subKey = string.Join("\\", parts.Skip(1));
+ if (hive == "HKCU")
+ {
+ using var rk = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
+ Path.GetDirectoryName(subKey)!.Replace("HKEY_CURRENT_USER\\", ""), true);
+ rk?.DeleteValue(Path.GetFileName(subKey), false);
+ }
+ }
+ }
+ catch { }
+ }
+
+ progress?.Report("正在还原备份文件...");
+ foreach (var backup in manifest.Backups)
+ {
+ try
+ {
+ if (File.Exists(backup.BackupPath))
+ {
+ File.Copy(backup.BackupPath, backup.OriginalPath, true);
+ File.Delete(backup.BackupPath);
+ }
+ }
+ catch { }
+ }
+
+ try
+ {
+ if (File.Exists(_manifestPath))
+ File.Delete(_manifestPath);
+ }
+ catch { }
+
+ progress?.Report("卸载完成,建议重启希沃白板");
+ });
+ }
+
+ private void BackupFile(PatchManifest manifest, string filePath)
+ {
+ if (!File.Exists(filePath)) return;
+ var backupPath = filePath + ".bak";
+ try
+ {
+ File.Copy(filePath, backupPath, true);
+ manifest.Backups.Add(new PatchBackupEntry
+ {
+ OriginalPath = filePath,
+ BackupPath = backupPath
+ });
+ }
+ catch { }
+ }
+ }
+
+ public class PatchManifest
+ {
+ public string Version { get; set; } = "";
+ public string InstallTime { get; set; } = "";
+ public List Backups { get; set; } = new();
+ public List RegistryKeys { get; set; } = new();
+ }
+
+ public class PatchBackupEntry
+ {
+ public string OriginalPath { get; set; } = "";
+ public string BackupPath { get; set; } = "";
+ }
+}
diff --git a/Manager/Services/TouchFixService.cs b/Manager/Services/TouchFixService.cs
new file mode 100644
index 0000000..66ad0c2
--- /dev/null
+++ b/Manager/Services/TouchFixService.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace BetterSeewo.Manager.Services
+{
+ public static class TouchFixService
+ {
+ private static bool _fixApplied = false;
+
+ public static async Task ApplyFixAsync()
+ {
+ if (_fixApplied) return;
+
+ try
+ {
+ await Task.Run(() =>
+ {
+ ForceIWBMode();
+ PatchTouchConfig();
+ SuppressTouchErrors();
+
+ _fixApplied = true;
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[Better-EN5] TouchFix failed: {ex.Message}");
+ }
+ }
+
+ public static bool IsTouchWorkingCorrectly()
+ {
+ try
+ {
+ return TryDetectIWB();
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static async Task ForceTouchEnableAsync()
+ {
+ return await Task.Run(() =>
+ {
+ try
+ {
+ ForceIWBMode();
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ });
+ }
+
+ private static bool TryDetectIWB()
+ {
+ try
+ {
+ using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Seewo\EasiNote5");
+ if (key != null)
+ {
+ var val = key.GetValue("ForceIWB");
+ if (val != null && val.ToString() == "1")
+ return true;
+ }
+ }
+ catch { }
+
+ try
+ {
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var configFile = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config", "IWBConfig.json");
+ if (File.Exists(configFile))
+ {
+ var json = File.ReadAllText(configFile);
+ if (json.Contains("\"ForceIWB\": true") || json.Contains("\"ForceIWB\":true"))
+ return true;
+ }
+ }
+ catch { }
+
+ return false;
+ }
+
+ private static void ForceIWBMode()
+ {
+ try
+ {
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var configDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config");
+ var configFile = Path.Combine(configDir, "IWBConfig.json");
+
+ if (!Directory.Exists(configDir))
+ Directory.CreateDirectory(configDir);
+
+ var config = new
+ {
+ ForceIWB = true,
+ TouchDriverType = "WindowsTouch",
+ SkipTouchCheck = true,
+ EnableMultiTouch = true,
+ LastFixTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
+ };
+
+ var json = System.Text.Json.JsonSerializer.Serialize(config, new System.Text.Json.JsonSerializerOptions
+ {
+ WriteIndented = true
+ });
+
+ File.WriteAllText(configFile, json);
+
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "ForceIWB", 1, Microsoft.Win32.RegistryValueKind.DWord);
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "SkipTouchCheck", 1, Microsoft.Win32.RegistryValueKind.DWord);
+ Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "EnableMultiTouch", 1, Microsoft.Win32.RegistryValueKind.DWord);
+ }
+ catch { }
+ }
+
+ private static void PatchTouchConfig()
+ {
+ try
+ {
+ var configDir = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "Seewo", "EasiNote5", "Config");
+
+ var cfgFile = Path.Combine(configDir, "TouchConfig.ini");
+ if (!Directory.Exists(configDir))
+ Directory.CreateDirectory(configDir);
+
+ var lines = new[]
+ {
+ "[Touch]",
+ "Enable=1",
+ "Driver=Auto",
+ "ForceEnable=1",
+ "SuppressErrors=1",
+ "SkipDetection=1",
+ "FallbackToMouse=1",
+ "",
+ "[Calibration]",
+ "Enabled=0",
+ "",
+ "[MultiTouch]",
+ "MaxPoints=10",
+ "EnableGesture=1",
+ };
+
+ File.WriteAllLines(cfgFile, lines);
+ }
+ catch { }
+ }
+
+ private static void SuppressTouchErrors()
+ {
+ try
+ {
+ var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var logDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Logs", "Touch");
+
+ if (Directory.Exists(logDir))
+ {
+ foreach (var logFile in Directory.GetFiles(logDir, "*.log"))
+ {
+ try { File.Delete(logFile); } catch { }
+ }
+ }
+
+ var seewoRegPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Seewo\EasiNote5";
+ try
+ {
+ Microsoft.Win32.Registry.SetValue(seewoRegPath, "SkipTouchCheck", "1");
+ }
+ catch { }
+ }
+ catch { }
+ }
+ }
+}
+
diff --git a/NuGet.config b/NuGet.config
deleted file mode 100644
index a71ffb6..0000000
--- a/NuGet.config
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3529d16
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+# Better-Seewo
+
+希沃白板 5 功能增强插件。
+
+## 构建
+
+```powershell
+set PATH=C:\Program Files\dotnet;%PATH%
+dotnet build -c Release Better-EN5
+```
+
+输出:`Better-EN5\bin\Release\net6.0-windows\`
+
+## 安装
+
+- 双击 `安装插件.bat`(自动提权管理员)
+- 或以管理员运行 `scripts\install.ps1`
+- 重启希沃白板 5
+
+## 功能
+
+| 模块 | 说明 |
+|------|------|
+| 安装管理 | 安装/卸载补丁,.bak 备份还原 |
+| 墨迹管理 | 导出/导入课件笔迹和标注 |
+| 文件转换 | PPT ↔ ENBX 互转 |
+| 触摸修复 | 非希沃硬件触摸检测修复 |
+| 激活 | 输入激活码解锁专业版 |
+
+## 技术栈
+
+- .NET 6.0 WPF + WinForms
+- dotnetCampus.EasiPlugin.Sdk v2.1.1-alpha.3
+- 逆向工程 EasiNote.Api.dll v5.2.4.9855
+
+## 版本历史
+
+- v1.1.2 — PatchService 安装/卸载系统,品牌更新
+- v1.1.1 — 构建修复,警告清理,新增脚本
+- v1.1.0 — 全面重构,16:9 UI,五大核心服务
+- v1.0.1 — 初始版本
+
+## 许可
+
+雾启工作室 × Macrohard Studio
diff --git a/check_purposes.csx b/check_purposes.csx
deleted file mode 100644
index ab597ee..0000000
--- a/check_purposes.csx
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Reflection;
-class Program {
- static void Main() {
- var asm = Assembly.LoadFrom(@"C:\Program Files (x86)\Seewo\EasiNote5\EasiNote5_5.2.4.9855\Main\EasiNote.Api.dll");
- var t = asm.GetType("Cvte.EasiNote.UIItemPurposes");
- if (t == null) t = asm.GetType("EasiNote.Api.UIItemPurposes");
- if (t == null) t = asm.GetType("UIItemPurposes");
- if (t != null) {
- Console.WriteLine("Found: " + t.FullName);
- foreach (var f in t.GetFields(BindingFlags.Public | BindingFlags.Static)) {
- Console.WriteLine($" {f.Name} = {f.GetValue(null)}");
- }
- } else {
- Console.WriteLine("UIItemPurposes not found directly, searching...");
- foreach (var tt in asm.GetTypes()) {
- if (tt.Name.Contains("Purposes") || tt.Name.Contains("Purpose")) {
- Console.WriteLine(" " + tt.FullName);
- foreach (var f in tt.GetFields(BindingFlags.Public | BindingFlags.Static)) {
- Console.WriteLine($" {f.Name} = {f.GetValue(null)}");
- }
- }
- }
- }
- }
-}
diff --git a/install.ps1 b/install.ps1
deleted file mode 100644
index 88bb6e5..0000000
--- a/install.ps1
+++ /dev/null
@@ -1,48 +0,0 @@
-# Better-EN5 插件安装脚本
-# 以管理员身份运行
-
-$ErrorActionPreference = "Stop"
-
-$pluginName = "Better-EN5"
-$seewoPath = "$env:ProgramFiles(x86)\Seewo\EasiNote5"
-
-# 查找最新版本目录
-$versionDirs = Get-ChildItem "$seewoPath\EasiNote5_*" -Directory | Sort-Object Name -Descending
-if ($versionDirs.Count -eq 0) {
- Write-Host "未找到希沃白板安装目录" -ForegroundColor Red
- exit 1
-}
-
-$targetDir = Join-Path $versionDirs[0].FullName "Main\Extensions\$pluginName"
-
-Write-Host "安装到: $targetDir" -ForegroundColor Cyan
-
-# 创建插件目录
-if (-not (Test-Path $targetDir)) {
- New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
-}
-
-# 复制插件文件
-$sourceDir = Join-Path $PSScriptRoot "Better-EN5\bin\Release\net6.0-windows"
-if (-not (Test-Path $sourceDir)) {
- $sourceDir = Join-Path $PSScriptRoot "Better-EN5\bin\Debug\net6.0-windows"
-}
-
-if (Test-Path $sourceDir) {
- Copy-Item "$sourceDir\*" $targetDir -Recurse -Force
- Write-Host "插件文件已复制" -ForegroundColor Green
-} else {
- Write-Host "未找到构建输出目录,请先构建项目" -ForegroundColor Yellow
- Write-Host "期望路径: $sourceDir" -ForegroundColor Yellow
-}
-
-# 复制 manifest.coin
-$manifestSource = Join-Path $PSScriptRoot "Better-EN5\manifest.coin"
-if (Test-Path $manifestSource) {
- Copy-Item $manifestSource $targetDir -Force
- Write-Host "manifest.coin 已复制" -ForegroundColor Green
-}
-
-Write-Host ""
-Write-Host "安装完成!请重启希沃白板以加载插件。" -ForegroundColor Green
-Write-Host "在希沃白板的右键菜单中可以找到 'Better-Seewo 设置' 入口。" -ForegroundColor Cyan
diff --git a/使用说明.txt b/使用说明.txt
deleted file mode 100644
index 5182de4..0000000
--- a/使用说明.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-Better-Seewo 增强插件 - 使用说明
-====================================
-版本:1.1.2 | 作者:雾启工作室 × Macrohard Studio
-项目地址:http://171.80.3.149:4321/miao-moe/Better-Seewo
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-一、安装方法
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-方式一:自动安装(推荐)
-1. 以管理员身份运行 PowerShell
-2. 执行:.\scripts\install.ps1
-3. 重启希沃白板 5
-
-方式二:安装包安装
-- 运行 bin\Release\Better-Seewo 增强插件.1.1.0.exe
-
-方式三:手动安装
-1. 将 Better-EN5\bin\Release\net6.0-windows\ 下所有文件
- 复制到希沃白板 Extensions\Better-EN5\ 目录
-2. 将 Better-EN5\manifest.coin 复制到同一目录
-3. 重启希沃白板 5
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-二、功能入口
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-安装成功后,在希沃白板中可以找到以下入口:
-
-1. 右键菜单(备课模式)
- 在课件板书区域右键 → "导出墨迹"
- 在课件板书区域右键 → "Better-Seewo"
-
-2. 顶部工具栏
- 在工具栏找到 "Better-Seewo" 图标按钮
-
-点击以上任意入口即可打开 Better-Seewo 主界面。
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-三、功能模块
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-1. 墨迹管理(Ink Manager)
- - 导出当前课件中的所有笔迹/标注为 JSON 文件
- - 从 JSON 文件导入笔迹/标注到当前课件
- - 支持备份和迁移课堂板书内容
-
-2. 文件转换(File Converter)
- - PPT(.pptx)转希沃课件(.enbx)
- - 希沃课件(.enbx)转 PPT(.pptx)
- - 清理转换缓存,修复转换失败问题
-
-3. 触摸修复(Touch Fix)
- - 检测希沃白板的触摸状态
- - 强制启用 IWB 模式
- - 配置注册表和配置文件以跳过触摸检测
- - 抑制触摸相关错误提示
-
-4. 大屏支持(Board Support)
- - 为非触摸大屏启用完整白板模式
- - 创建"希沃白板5-大屏模式"桌面快捷方式
- - 配置大屏相关注册表和配置文件
-
-5. 激活(Activation)
- - 社区版与专业版自由切换
- - 输入激活码解锁全部功能
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-四、开发者信息
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-构建环境要求:
-- .NET SDK 6.0
-- 希沃白板 5(版本 5.2.2.653 ~ 5.3.0.0)
-- Visual Studio 2022 或 dotnet CLI
-
-构建命令:
- dotnet build -c Release Better-EN5
-
-更多信息请参阅 DEVELOPER_GUIDE.md
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-五、常见问题
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-
-Q: 安装后希沃白板看不到插件入口?
-A: 请确保以管理员身份运行 install.ps1,
- 然后完全退出希沃白板,重新启动。
-
-Q: 插件加载失败怎么办?
-A: 检查希沃白板版本是否在 5.2.2.653 ~ 5.3.0.0 范围内。
-
-Q: 触摸修复不起作用?
-A: 部分硬件需要重启电脑才能生效。
- 如果仍然无效,请尝试"大屏支持"中的 IWB 模式。
-
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-雾生万象,启以为光
diff --git a/安装插件.bat b/安装插件.bat
deleted file mode 100644
index 17d4e96..0000000
--- a/安装插件.bat
+++ /dev/null
@@ -1,68 +0,0 @@
-@echo off
-title Better-Seewo 插件安装
-cd /d "%~dp0"
-
-:: 检查管理员权限
-net session >nul 2>&1
-if %errorLevel% neq 0 (
- echo 正在请求管理员权限...
- powershell start-process "%~f0" -verb runas
- exit /b
-)
-
-echo ========================================
-echo Better-Seewo 增强插件 v1.1.2 安装
-echo 雾启工作室
-echo ========================================
-echo.
-
-:: 设置 dotnet 路径
-set PATH=C:\Program Files\dotnet;%PATH%
-
-:: 构建
-echo [1/3] 构建项目...
-dotnet build -c Release Better-EN5
-if %errorLevel% neq 0 (
- echo 构建失败!
- pause
- exit /b 1
-)
-echo 构建成功
-echo.
-
-:: 查找希沃白板安装目录
-echo [2/3] 查找希沃白板安装目录...
-set "seewoRoot=%ProgramFiles(x86)%\Seewo\EasiNote5"
-if not exist "%seewoRoot%" (
- echo 未找到希沃白板安装目录!
- pause
- exit /b 1
-)
-
-for /f "tokens=*" %%d in ('dir "%seewoRoot%\EasiNote5_*" /b /o-n') do (
- set "versionDir=%seewoRoot%\%%d"
- goto :found
-)
-echo 未找到希沃白板版本目录!
-pause
-exit /b 1
-
-:found
-set "targetDir=%versionDir%\Main\Extensions\Better-EN5"
-echo 安装到: %targetDir%
-
-:: 创建目标目录
-if not exist "%targetDir%" mkdir "%targetDir%"
-
-:: 复制文件
-echo [3/3] 复制插件文件...
-xcopy "Better-EN5\bin\Release\net6.0-windows\*" "%targetDir%\" /y /q
-copy /y "Better-EN5\manifest.coin" "%targetDir%\" >nul
-
-echo.
-echo ========================================
-echo 安装完成!
-echo 请重启希沃白板以加载插件。
-echo ========================================
-echo.
-pause