Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Ink Canvas/Helpers/Converters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu
}
}

public class BooleanToInverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
}

public class RippleEffectTranslationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
Expand Down
2 changes: 2 additions & 0 deletions Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ internal void ImageBlackboard_MouseUp(object sender, MouseButtonEventArgs e)
}
}
}, TaskScheduler.FromCurrentSynchronizationContext());
StartChickenSoupAutoRotation();

@augmentcode augmentcode Bot Jul 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs:956 — StartChickenSoupAutoRotation() starts rotating using the new multi-source scheme logic, but the initial BlackBoardWaterMark.Text set just above still relies on legacy Settings.Appearance.ChickenSoupSource. This can make the first displayed tip not match what the user configured (e.g., custom schemes / enabled presets) until the first timer tick.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.


if (Settings.Canvas.UsingWhiteboard)
{
Expand Down Expand Up @@ -1016,6 +1017,7 @@ internal void ImageBlackboard_MouseUp(object sender, MouseButtonEventArgs e)

if (GetSelectionBGLeft() != 28) PenIcon_Click(null, null);

StopChickenSoupAutoRotation();
WaterMarkTime.Visibility = Visibility.Collapsed;
WaterMarkDate.Visibility = Visibility.Collapsed;
BlackBoardWaterMark.Visibility = Visibility.Collapsed;
Expand Down
115 changes: 99 additions & 16 deletions Ink Canvas/MainWindow_cs/MW_Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ private void ToggleSwitchEnableNibMode_Toggled(object sender, RoutedEventArgs e)

private static readonly Lazy<object> HitokotoHttpClient = new Lazy<object>(CreateHitokotoClient, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);

private DispatcherTimer _chickenSoupAutoRotationTimer;

/// <summary>
/// 创建用于获取一言(Hitokoto)数据的HttpClient
/// </summary>
Expand Down Expand Up @@ -136,8 +138,11 @@ private static object CreateHitokotoClient()
/// 根据当前外观设置更新白板水印的名言文本。
/// </summary>
/// <remarks>
/// 当配置为内置来源时(0:OSUPlayer、1:名言警句、2:高考俗语)从对应数组中随机选择一条并设置为水印文本;
/// 当配置为一言(3)时会异步请求 Hitokoto API 并在请求中显示占位提示,成功时将返回文本设为水印,失败时记录警告日志并设置可读的失败提示文本。此方法会修改 BlackBoardWaterMark.Text,并在发生异常时记录日志且设置合适的回退文本。
/// 汇总所有启用的来源(预设来源 + 自定义方案),从中随机选取一个:
/// 若选中预设为 osu/mottos/gaokao/phigros,从对应数组中随机选择一条;
/// 若选中预设为 hitokoto,则异步请求 Hitokoto API,并在请求中显示占位提示,成功时将返回文本设为水印,失败时记录警告日志并设置可读的失败提示文本;
/// 若选中的是自定义方案,则按行拆分其 Content 并随机选取一行。
/// 当启用列表为空时直接返回,不修改当前文本。
/// </remarks>
internal async Task UpdateChickenSoupTextAsync()
{
Expand All @@ -148,22 +153,40 @@ internal async Task UpdateChickenSoupTextAsync()
return;
}

if (Settings.Appearance.ChickenSoupSource == 0)
// 汇总所有启用的方案
var enabledSchemes = new List<TipsScheme>();

var enabledPresets = Settings.Appearance.EnabledPresetTipsSources;
foreach (var preset in ChickenSoup.GetPresetSchemes())
{
int randChickenSoupIndex = new Random().Next(ChickenSoup.OSUPlayerYuLu.Length);
BlackBoardWaterMark.Text = ChickenSoup.OSUPlayerYuLu[randChickenSoupIndex];
if (enabledPresets != null && enabledPresets.Contains(preset.PresetId))
{
enabledSchemes.Add(preset);
}
}
else if (Settings.Appearance.ChickenSoupSource == 1)

var customSchemes = Settings.Appearance.CustomTipsSchemes;
if (customSchemes != null)
{
int randChickenSoupIndex = new Random().Next(ChickenSoup.MingYanJingJu.Length);
BlackBoardWaterMark.Text = ChickenSoup.MingYanJingJu[randChickenSoupIndex];
foreach (var custom in customSchemes)
{
if (custom != null && custom.IsEnabled)
{
enabledSchemes.Add(custom);
}
}
}
else if (Settings.Appearance.ChickenSoupSource == 2)

if (enabledSchemes.Count == 0)
{
int randChickenSoupIndex = new Random().Next(ChickenSoup.GaoKaoPhrases.Length);
BlackBoardWaterMark.Text = ChickenSoup.GaoKaoPhrases[randChickenSoupIndex];
return;
}
else if (Settings.Appearance.ChickenSoupSource == 3)

var rnd = new Random();
var selected = enabledSchemes[rnd.Next(enabledSchemes.Count)];

// Hitokoto 预设走 HTTP API
if (selected.IsPreset && selected.PresetId == "hitokoto")
{
BlackBoardWaterMark.Text = Properties.MainWindowStrings.Main_Hitokoto_Loading;

Expand Down Expand Up @@ -215,17 +238,39 @@ internal async Task UpdateChickenSoupTextAsync()
LogHelper.WriteLogToFile($"一言 API 请求失败: {ex.Message}", LogHelper.LogType.Warning);
BlackBoardWaterMark.Text = Properties.MainWindowStrings.Main_Hitokoto_Unavailable;
}
return;
}
else if (Settings.Appearance.ChickenSoupSource == 4)

// 其它预设来源
if (selected.IsPreset && !string.IsNullOrEmpty(selected.PresetId))
{
int randChickenSoupIndex = new Random().Next(ChickenSoup.PhigrosTips.Length);
BlackBoardWaterMark.Text = ChickenSoup.PhigrosTips[randChickenSoupIndex];
var tips = ChickenSoup.GetTipsFromPreset(selected.PresetId);
if (tips != null && tips.Length > 0)
{
BlackBoardWaterMark.Text = tips[rnd.Next(tips.Length)];
}
return;
}

// 自定义方案
if (!selected.IsPreset)
{
if (string.IsNullOrWhiteSpace(selected.Content))
{
return;
}
var lines = selected.Content.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0)
{
return;
}
BlackBoardWaterMark.Text = lines[rnd.Next(lines.Length)];
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新白板名言时出错: {ex.Message}", LogHelper.LogType.Warning);
if (Settings.Appearance.ChickenSoupSource == 3 && BlackBoardWaterMark != null)
if (BlackBoardWaterMark != null)
{
try { BlackBoardWaterMark.Text = Properties.MainWindowStrings.Main_Hitokoto_Unavailable; } catch (Exception innerEx) { System.Diagnostics.Debug.WriteLine(innerEx); }
}
Expand Down Expand Up @@ -399,6 +444,44 @@ private static BitmapImage CreateBitmapImage(Uri uri, int decodePixelWidth = 0)
return image;
}

/// <summary>
/// 启动白板名言自动轮换计时器。
/// </summary>
internal void StartChickenSoupAutoRotation()
{
if (!Settings.Appearance.EnableChickenSoupInWhiteboardMode) return;
if (!Settings.Appearance.EnableChickenSoupAutoRotation) return;

if (_chickenSoupAutoRotationTimer == null)
{
_chickenSoupAutoRotationTimer = new DispatcherTimer();
_chickenSoupAutoRotationTimer.Tick += async (s, e) => await UpdateChickenSoupTextAsync();
}

_chickenSoupAutoRotationTimer.Interval = TimeSpan.FromSeconds(Settings.Appearance.ChickenSoupAutoRotationInterval);

@augmentcode augmentcode Bot Jul 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ink Canvas/MainWindow_cs/MW_Settings.cs:461 — _chickenSoupAutoRotationTimer.Interval is set directly from Settings.Appearance.ChickenSoupAutoRotationInterval without validation; if the value is 0/negative (e.g., from a manually edited config), TimeSpan.FromSeconds/DispatcherTimer.Interval can throw or result in pathological tick behavior. Consider clamping to a safe minimum before applying it.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

_chickenSoupAutoRotationTimer.Start();
}

/// <summary>
/// 停止白板名言自动轮换计时器。
/// </summary>
internal void StopChickenSoupAutoRotation()
{
if (_chickenSoupAutoRotationTimer != null)
{
_chickenSoupAutoRotationTimer.Stop();
}
}

/// <summary>
/// 重启白板名言自动轮换计时器。
/// </summary>
internal void RestartChickenSoupAutoRotation()
{
StopChickenSoupAutoRotation();
StartChickenSoupAutoRotation();
}

/// <summary>
/// 更新组合框中的自定义图标选项
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Ink_Canvas.Helpers;
using Ink_Canvas.Windows.SettingsViews.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OSVersionExtension;
Expand Down Expand Up @@ -148,6 +149,9 @@ private void LoadSettings(bool isStartup = false, bool skipAutoUpdateCheck = fal
LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error);
}

// Migrate legacy chicken soup source setting to new multi-source format
SettingsManager.MigrateChickenSoupSettings();

try
{
if (Settings?.Appearance != null)
Expand Down
4 changes: 4 additions & 0 deletions Ink Canvas/Properties/NavStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 49 additions & 1 deletion Ink Canvas/Properties/ThemeStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 73 additions & 1 deletion Ink Canvas/Properties/ThemeStrings.en-US.resx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
Expand Down Expand Up @@ -393,4 +393,76 @@
<data name="Theme_BoardMenuOpacity" xml:space="preserve">
<value>Whiteboard menu opacity</value>
</data>
<data name="Theme_Tips_GlobalSettings" xml:space="preserve">
<value>Global Settings</value>
</data>
<data name="Theme_Tips_GlobalSettingsHint" xml:space="preserve">
<value>Manage faith source display, sources and auto-rotation</value>
</data>
<data name="Theme_Tips_AutoRotation" xml:space="preserve">
<value>Auto-rotate Tips</value>
</data>
<data name="Theme_Tips_AutoRotationHint" xml:space="preserve">
<value>When enabled, tips in the whiteboard will automatically rotate at the set interval</value>
</data>
<data name="Theme_Tips_RotationInterval" xml:space="preserve">
<value>Rotation interval (seconds)</value>
</data>
<data name="Theme_Tips_RotationIntervalHint" xml:space="preserve">
<value>Must enter a positive integer >= 5</value>
</data>
<data name="Theme_Tips_Import" xml:space="preserve">
<value>Import</value>
</data>
<data name="Theme_Tips_Create" xml:space="preserve">
<value>New</value>
</data>
<data name="Theme_Tips_Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Theme_Tips_Export" xml:space="preserve">
<value>Export</value>
</data>
<data name="Theme_Tips_SelectTxtFile" xml:space="preserve">
<value>+ Select a txt file</value>
</data>
<data name="Theme_Tips_SchemeName" xml:space="preserve">
<value>Name</value>
</data>
<data name="Theme_Tips_SchemeContent" xml:space="preserve">
<value>Content</value>
</data>
<data name="Theme_Tips_CancelConfirm" xml:space="preserve">
<value>This cannot be undone after cancellation. Are you sure?</value>
</data>
<data name="Theme_Tips_Abandon" xml:space="preserve">
<value>Discard</value>
</data>
<data name="Theme_Tips_SelectSchemeFirst" xml:space="preserve">
<value>Please select a scheme from the list first</value>
</data>
<data name="Theme_Tips_PresetLocked" xml:space="preserve">
<value>Preset schemes cannot be modified</value>
</data>
<data name="Theme_Tips_NewDialogTitle" xml:space="preserve">
<value>New Scheme</value>
</data>
<data name="Theme_Tips_ImportDialogTitle" xml:space="preserve">
<value>Import Scheme</value>
</data>
<data name="Theme_Tips_EditDialogTitle" xml:space="preserve">
<value>Edit Scheme</value>
</data>
<data name="Theme_Tips_NameRequired" xml:space="preserve">
<value>Please enter a scheme name</value>
</data>
<data name="Theme_Tips_ContentRequired" xml:space="preserve">
<value>Please enter scheme content</value>
</data>
<data name="Theme_Tips_NameExists" xml:space="preserve">
<value>A scheme with this name already exists. Please use a different name.</value>
</data>
<data name="Theme_Tips_Title" xml:space="preserve">
<value>Tips</value>
</data>
</root>
Loading
Loading