From 4185e3d3328fb66be6fcd611aa5e49a78cd03053 Mon Sep 17 00:00:00 2001 From: jasonliu1199 <114725833+jasonliu1199@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:32:38 +0800 Subject: [PATCH 1/3] docs: clarify community maintenance scope --- .github/ISSUE_TEMPLATE/bug_report.yml | 38 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 24 ++++++++++++++ .github/pull_request_template.md | 11 +++++++ .gitignore | 1 - CHANGELOG.md | 23 +++++++++++++ MAINTAINERS.md | 15 +++++++++ README.md | 24 ++++++++++---- ROADMAP.md | 32 ++++++++++++++++++ 8 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md create mode 100644 CHANGELOG.md create mode 100644 MAINTAINERS.md create mode 100644 ROADMAP.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..9dab263 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,38 @@ +name: Bug report +description: Report a reproducible ToastFish problem. +title: "[Bug]: " +labels: ["bug"] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: What happened? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: List the exact steps that trigger the problem. + placeholder: | + 1. Open ToastFish + 2. Select ... + 3. Click ... + validations: + required: true + - type: input + id: version + attributes: + label: ToastFish version + placeholder: v3.0.1-community.1 + - type: input + id: windows + attributes: + label: Windows version + placeholder: Windows 11 23H2 + - type: textarea + id: logs + attributes: + label: Error details or screenshots + description: Paste exception text or attach screenshots if available. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..40cd412 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,24 @@ +name: Feature request +description: Suggest an improvement for ToastFish. +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What problem would this feature solve? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + description: What behavior do you expect? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives + description: Any workaround or alternative design you have considered? diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..613c2d1 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## Summary + +- + +## Verification + +- + +## Related Issues + +- diff --git a/.gitignore b/.gitignore index afbe906..e379847 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ obj/ bin/ token.txt .vs/ -.github/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4d396ea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes in this community fork are documented here. + +## v3.0.1-community.1 - Unreleased + +### Fixed + +- Ensure the `Log` directory exists before learning or import flows write logs. +- Harden Excel import against unreadable files, empty rows, empty cells, and null import results. +- Fix Japanese word progress lookup by quoting the selected book name in SQL. + +### Changed + +- Clarified community fork status in README. +- Added maintenance roadmap. +- Updated GitHub Actions workflow for current Windows runners. + +### Known Issues + +- This remains a Windows-only WPF/.NET Framework application. +- Some notification behavior depends on Windows notification settings. +- macOS/Linux support is not planned for the first maintenance cycle. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000..525dc16 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,15 @@ +# Maintainers + +This is a community-maintained fork of `Uahh/ToastFish`. + +## Maintenance Scope + +- Keep the Windows version buildable and usable. +- Prioritize crash fixes, import/export reliability, notification reliability, and release packaging. +- Preserve upstream attribution and the MIT License. + +## Project Boundaries + +- Do not present this fork as the official upstream project unless upstream grants maintainer access or transfers ownership. +- Keep changes small and reviewable. +- Prefer issue-linked fixes over broad rewrites. diff --git a/README.md b/README.md index 8e367fa..11f468f 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,22 @@
-# ToastFish +# ToastFish Community Edition #### 这是一个利用Windows通知栏背单词的软件 #### 可以让你在上班、上课等恶劣环境下安全隐蔽地背单词 ![License MIT](https://img.shields.io/badge/license-MIT-orange) -![GitHub release (latest by date)](https://img.shields.io/badge/release-v3.0-blue) -![GitHub issues](https://img.shields.io/github/issues/Uahh/ToastFish) -[![.NET Build & Test](https://github.com/Uahh/ToastFish/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/Uahh/ToastFish/actions/workflows/dotnet-desktop.yml) +![GitHub release (latest by date)](https://img.shields.io/badge/release-v3.0.1--community.1-blue)
+## 社区维护说明 + +这是 [Uahh/ToastFish](https://github.com/Uahh/ToastFish) 的社区维护版本,目标是让 ToastFish 在当前 Windows 环境中继续可用,优先修复启动、通知、词库导入、日志和发布流程问题。 + +本 fork 不是官方上游项目,除非原作者后续授权或转移维护权限。原项目采用 MIT License,本 fork 会保留原始授权与 attribution。 + ## 使用方法 ### 基本流程 1. 选择词库: @@ -44,7 +48,7 @@ ### 自定义内容 可以通过自定义Excel内容来让ToastFish推送所需要的内容。 -自定义Excel模板位于安装目录/Resources/自定义模板.xslx +自定义Excel模板位于安装目录/Resources/自定义模板.xlsx ![设置词数](https://github.com/Uahh/ToastFish/blob/main/Resources/Gif/导入自定义单词.gif) @@ -56,9 +60,15 @@ A: 可以在系统设置 -> 轻松使用 -> 显示 -> 通知显示的时间 里 Q: 使用英语发音功能时会闪退? A: 请在系统设置里下载英语语音包,重启软件即可解决。 + +Q: 点击开始后没有通知? +A: 请确认 Windows 通知未被勿扰模式、专注助手或应用通知权限拦截,并检查系统通知中心中是否已有 ToastFish 通知。 + +Q: 导入 Excel 时失败或无反应? +A: 请使用 `Resources/自定义模板.xlsx` 作为模板,并保留第一行的类型标记。社区版会优先修复空单元格和格式异常导致的崩溃。 Q: 有没有Win7或是Mac版本的开发计划? -A: 这个真没有,Win7和Mac没有Windows10的通知栏。 +A: 近期维护重点是 Windows 10/11。ToastFish 依赖 Windows 通知能力,暂不计划 macOS/Linux 版本。 Q: 没有我想要背的单词怎么办? A: 可以使用自定义功能自己构建单词列表,如果单词数量很多,可以联系作者帮忙添加。 @@ -84,6 +94,8 @@ git clone https://github.com/Uahh/ToastFish ``` 项目使用VS2019, .net环境为4.7.2. +社区版建议使用 Visual Studio 2019/2022 或 GitHub Actions 的 Windows runner 编译。 + ## 感谢 感谢@itorr为本软件提供的支持、建议和测试! diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..c80269d --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,32 @@ +# Roadmap + +This fork focuses on maintenance first. Large rewrites and platform ports are out of scope until the Windows version is stable again. + +## v3.0.1-community.1 + +- Keep the project buildable on current GitHub Actions Windows runners. +- Publish build artifacts for every main branch build. +- Fix crashes caused by missing runtime folders. +- Harden Excel import against empty rows, empty cells, and unreadable files. +- Fix known Japanese word review crash caused by an invalid SQL query. +- Update README troubleshooting for Windows notification and voice-pack issues. + +## v3.0.2-community + +- Triage Windows 10/11 launch failures. +- Improve notification reliability after notifications are collapsed into Action Center. +- Add clearer diagnostics for import and notification failures. +- Review thread lifecycle and reduce reliance on `Thread.Abort`. + +## Later + +- Export currently imported/custom word lists. +- Add a way to exclude mastered words. +- Add pause/resume for interrupted learning sessions. +- Add shortcut customization. + +## Non-Goals + +- No macOS or Linux port in the first maintenance cycle. +- No UI rewrite in the first maintenance cycle. +- No framework migration until reliability issues are under control. From d5a6109b2760b40b119c03df2d028901686bdb94 Mon Sep 17 00:00:00 2001 From: jasonliu1199 <114725833+jasonliu1199@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:32:45 +0800 Subject: [PATCH 2/3] ci: modernize Windows build workflow --- .github/workflows/dotnet-desktop.yml | 71 ++++++++-------------------- 1 file changed, 20 insertions(+), 51 deletions(-) diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index e3ddccb..4d29826 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -1,65 +1,34 @@ -name: .NET Core Desktop +name: Windows Build + on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] + workflow_dispatch: jobs: - build: - - strategy: - matrix: - configuration: [Debug, Release] - runs-on: windows-latest steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - # Install .NET Core - - name: Install .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 3.1.202 - - # Setup MSBuild.exe - - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.0.2 - - # Setup NuGet - - name: Setup NuGet - uses: nuget/setup-nuget@v1 - with: - nuget-api-key: ${{ secrets.NuGetAPIKey }} - nuget-version: '5.x' + - name: Checkout + uses: actions/checkout@v4 - # NuGet restore - - name: NuGet restore - run: nuget restore + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 - # Build - - name: Build the solution - run: msbuild /p:Configuration=$env:Configuration - env: - Configuration: ${{ matrix.configuration }} + - name: Setup NuGet + uses: nuget/setup-nuget@v2 - # Execute unit tests - - name: Execute unit tests - run: dotnet test -c $env:Configuration - env: - Configuration: ${{ matrix.configuration }} - - # Upload bin directory - - name: Upload bin directory - uses: actions/upload-artifact@main - if: ${{ success() }} - with: - name: ToastFish_Build - path: ./bin/Release/ + - name: Restore packages + run: nuget restore ToastFish.sln + - name: Build + run: msbuild ToastFish.sln /p:Configuration=Release /p:Platform="Any CPU" + - name: Upload Release build + uses: actions/upload-artifact@v4 + with: + name: ToastFish-Release + path: bin/Release/** From de06c5e4ab891d1cfc6c4155a166d11e0826eb8c Mon Sep 17 00:00:00 2001 From: jasonliu1199 <114725833+jasonliu1199@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:32:52 +0800 Subject: [PATCH 3/3] fix: harden import and learning startup flows --- Model/Log/CreateLog.cs | 128 +++++++++++++++++++++------------- Model/SqliteControl/Select.cs | 2 +- View/ToastFish.xaml.cs | 24 ++++--- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/Model/Log/CreateLog.cs b/Model/Log/CreateLog.cs index 58e9a9f..1048f58 100644 --- a/Model/Log/CreateLog.cs +++ b/Model/Log/CreateLog.cs @@ -72,7 +72,7 @@ public void OutputExcel(String Path, object ObjList, String Type) else { // 自定义 - FirstLine = new List { "自定义", "第一行", "第二行", "第三行" }; + FirstLine = new List { "自定义", "第一行", "第二行", "第三行", "第四行" }; } row = sheet.CreateRow(0); for (int i = 0; i < FirstLine.Count; i++) @@ -96,36 +96,45 @@ public object ImportExcel(string path) List WordList = new List(); List JpWordList = new List(); List CustWordList = new List(); - IWorkbook WorkBook; + IWorkbook WorkBook = null; try { - FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - - // Try to read WorkBook as XLSX: - try - { - WorkBook = new XSSFWorkbook(fs); - } - catch + using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { - WorkBook = null; - } + // Try to read WorkBook as XLSX: + try + { + WorkBook = new XSSFWorkbook(fs); + } + catch + { + WorkBook = null; + } - // If reading fails, try to read WorkBook as XLS: - if (WorkBook == null) - { - WorkBook = new HSSFWorkbook(fs); + // If reading fails, try to read WorkBook as XLS: + if (WorkBook == null) + { + fs.Position = 0; + WorkBook = new HSSFWorkbook(fs); + } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Excel read error", MessageBoxButton.OK, MessageBoxImage.Error); - return WordList; + return null; } ISheet Sheet = WorkBook.GetSheetAt(0); + if (Sheet == null) + { + MessageBox.Show("导入失败,Excel文件没有可读取的工作表"); + return null; + } + IRow FirstRow = Sheet.GetRow(0); - if(FirstRow.GetCell(0).ToString() == "英语") + string importType = GetCellValue(FirstRow, 0); + if(importType == "英语") { int RowCount = Sheet.LastRowNum; for (int i = 1; i <= RowCount; i++) @@ -134,58 +143,69 @@ public object ImportExcel(string path) Word TempWord = new Word(); if (Row == null) continue; - TempWord.headWord = Row.GetCell(1).ToString(); - TempWord.tranCN = Row.GetCell(2).ToString(); - TempWord.ukPhone = Row.GetCell(3).ToString(); - TempWord.usPhone = Row.GetCell(4).ToString(); - TempWord.pos = Row.GetCell(5).ToString(); - TempWord.phrase = Row.GetCell(6).ToString(); - TempWord.phraseCN = Row.GetCell(7).ToString(); - TempWord.sentence = Row.GetCell(8).ToString(); - TempWord.sentenceCN = Row.GetCell(9).ToString(); - TempWord.question = Row.GetCell(10).ToString(); - TempWord.explain = Row.GetCell(11).ToString(); - TempWord.choiceIndexOne = Row.GetCell(12).ToString(); - TempWord.choiceIndexTwo = Row.GetCell(13).ToString(); - TempWord.choiceIndexThree = Row.GetCell(14).ToString(); - TempWord.choiceIndexFour = Row.GetCell(15).ToString(); - TempWord.rightIndex = Row.GetCell(16).ToString(); + TempWord.headWord = GetCellValue(Row, 1); + TempWord.tranCN = GetCellValue(Row, 2); + TempWord.ukPhone = GetCellValue(Row, 3); + TempWord.usPhone = GetCellValue(Row, 4); + TempWord.pos = GetCellValue(Row, 5); + TempWord.phrase = GetCellValue(Row, 6); + TempWord.phraseCN = GetCellValue(Row, 7); + TempWord.sentence = GetCellValue(Row, 8); + TempWord.sentenceCN = GetCellValue(Row, 9); + TempWord.question = GetCellValue(Row, 10); + TempWord.explain = GetCellValue(Row, 11); + TempWord.choiceIndexOne = GetCellValue(Row, 12); + TempWord.choiceIndexTwo = GetCellValue(Row, 13); + TempWord.choiceIndexThree = GetCellValue(Row, 14); + TempWord.choiceIndexFour = GetCellValue(Row, 15); + TempWord.rightIndex = GetCellValue(Row, 16); + if (string.IsNullOrWhiteSpace(TempWord.headWord)) + continue; WordList.Add(TempWord); } return WordList; } - else if (FirstRow.GetCell(0).ToString() == "日语") + else if (importType == "日语") { int RowCount = Sheet.LastRowNum; - for (int i = 1; i < RowCount; i++) + for (int i = 1; i <= RowCount; i++) { IRow Row = Sheet.GetRow(i); JpWord TempWord = new JpWord(); if (Row == null) continue; - TempWord.headWord = Row.GetCell(1).ToString(); - TempWord.tranCN = Row.GetCell(2).ToString(); - TempWord.Phone = int.Parse(Row.GetCell(3).ToString()); - TempWord.hiragana = Row.GetCell(4).ToString(); - TempWord.pos = Row.GetCell(5).ToString(); + TempWord.headWord = GetCellValue(Row, 1); + TempWord.tranCN = GetCellValue(Row, 2); + int phone; + TempWord.Phone = int.TryParse(GetCellValue(Row, 3), out phone) ? phone : 0; + TempWord.hiragana = GetCellValue(Row, 4); + TempWord.pos = GetCellValue(Row, 5); + if (string.IsNullOrWhiteSpace(TempWord.headWord)) + continue; JpWordList.Add(TempWord); } return JpWordList; } - else if (FirstRow.GetCell(0).ToString() == "自定义") + else if (importType == "自定义") { int RowCount = Sheet.LastRowNum; - int CellCount = FirstRow.LastCellNum; - for (int i = 1; i < RowCount; i++) + for (int i = 1; i <= RowCount; i++) { IRow Row = Sheet.GetRow(i); CustomizeWord TempWord = new CustomizeWord(); if (Row == null) continue; - TempWord.firstLine = Row.GetCell(1).ToString(); - TempWord.secondLine = Row.GetCell(2).ToString(); - TempWord.thirdLine = Row.GetCell(3).ToString(); - TempWord.fourthLine = Row.GetCell(4).ToString(); + TempWord.firstLine = GetCellValue(Row, 1); + TempWord.secondLine = GetCellValue(Row, 2); + TempWord.thirdLine = GetCellValue(Row, 3); + TempWord.fourthLine = GetCellValue(Row, 4); + if (string.IsNullOrWhiteSpace(TempWord.firstLine) + && string.IsNullOrWhiteSpace(TempWord.secondLine) + && string.IsNullOrWhiteSpace(TempWord.thirdLine) + && string.IsNullOrWhiteSpace(TempWord.fourthLine)) + { + continue; + } CustWordList.Add(TempWord); } return CustWordList; @@ -196,5 +216,17 @@ public object ImportExcel(string path) return null; } } + + private string GetCellValue(IRow row, int cellIndex) + { + if (row == null) + return string.Empty; + + ICell cell = row.GetCell(cellIndex); + if (cell == null) + return string.Empty; + + return cell.ToString().Trim(); + } } } diff --git a/Model/SqliteControl/Select.cs b/Model/SqliteControl/Select.cs index 0d2a685..2ec2f2f 100644 --- a/Model/SqliteControl/Select.cs +++ b/Model/SqliteControl/Select.cs @@ -94,7 +94,7 @@ public void UpdateTableCount() public void UpdateCount() { BookCount Temp = new BookCount(); - CountList = DataBase.Query($"select * from Count where bookName = {TABLE_NAME}", Temp); + CountList = DataBase.Query($"select * from Count where bookName = '{TABLE_NAME}'", Temp); var CountArray = CountList.ToArray(); foreach (var OneCount in CountArray) { diff --git a/View/ToastFish.xaml.cs b/View/ToastFish.xaml.cs index 1fee98e..febd4f8 100644 --- a/View/ToastFish.xaml.cs +++ b/View/ToastFish.xaml.cs @@ -41,6 +41,7 @@ public partial class MainWindow : Window //HotKey _hotKey0, _hotKey1, _hotKey2, _hotKey3, _hotKey4; public MainWindow() { + EnsureLogDirectory(); Form_Load(); InitializeComponent(); DataContext = Vm; @@ -344,10 +345,7 @@ private void NotifyIconDoubleClick(object sender, EventArgs e) private void Begin_Click(object sender, EventArgs e) { - if (!System.IO.Directory.Exists("Log")) { - System.IO.Directory.CreateDirectory("Log"); - } - // System.IO.Directory.CreateDirectory("Log"); + EnsureLogDirectory(); var state = thread.ThreadState; @@ -415,6 +413,11 @@ private void ImportWords_Click(object sender, EventArgs e) WordType Words = new WordType(); Words.Number = Select.WORD_NUMBER; object lstObj = Log.ImportExcel(FileName); + if (lstObj == null) + { + System.Windows.Forms.MessageBox.Show("导入文件出错!"); + return; + } string typeObj = lstObj.ToString(); string typeWord= typeof(List).ToString(); string typeJpWord = typeof(List).ToString(); @@ -447,9 +450,7 @@ private void ImportWords_Click(object sender, EventArgs e) return; } - if (!Directory.Exists("Log")){ - System.IO.Directory.CreateDirectory("Log"); - } + EnsureLogDirectory(); var state = thread.ThreadState; @@ -712,6 +713,14 @@ private void ExitApp_Click(object sender, EventArgs e) Environment.Exit(0); } + private void EnsureLogDirectory() + { + if (!Directory.Exists("Log")) + { + Directory.CreateDirectory("Log"); + } + } + private void Start_Click(object sender, EventArgs e) { //StartWithWindows.SetMeStart(true); @@ -722,4 +731,3 @@ private void Start_Click(object sender, EventArgs e) #endregion } } -