Skip to content

Commit 14bfd34

Browse files
authored
Merge pull request #8 from devohmycode/0.4.0
feat: clipboard history, taskbar widget & configurable modules
2 parents 0cd6b76 + b656e66 commit 14bfd34

15 files changed

Lines changed: 2457 additions & 36 deletions

‎ActionPanel.cs‎

Lines changed: 166 additions & 24 deletions
Large diffs are not rendered by default.

‎App.xaml.cs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
2020
_window = new MainWindow();
2121
_window.Activate();
2222

23-
if (AppSettings.LoadStartMinimized())
23+
// Only minimize at startup when launched via Windows auto-start
24+
if (AppSettings.LoadStartMinimized() && AppSettings.LoadStartWithWindows())
2425
{
25-
var appWindow = _window.AppWindow;
26-
appWindow.Hide();
26+
var uptime = TimeSpan.FromMilliseconds(Environment.TickCount64);
27+
if (uptime.TotalMinutes < 5)
28+
{
29+
var appWindow = _window.AppWindow;
30+
appWindow.Hide();
31+
}
2732
}
2833
}
2934

‎AppSettings.cs‎

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,151 @@ public static (int X, int Y, int W, int H)? LoadNotepadPosition()
666666
return null;
667667
}
668668

669+
public static void SaveWidgetModules(Dictionary<string, bool> modules)
670+
{
671+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetModules"] = modules });
672+
}
673+
674+
public static Dictionary<string, bool> LoadWidgetModules()
675+
{
676+
try
677+
{
678+
if (File.Exists(SettingsPath))
679+
{
680+
var json = File.ReadAllText(SettingsPath);
681+
using var doc = JsonDocument.Parse(json);
682+
if (doc.RootElement.TryGetProperty("widgetModules", out var prop))
683+
{
684+
var result = new Dictionary<string, bool>();
685+
foreach (var p in prop.EnumerateObject())
686+
result[p.Name] = p.Value.GetBoolean();
687+
return result;
688+
}
689+
}
690+
}
691+
catch { }
692+
// Default: clipboard and favorites enabled
693+
return new Dictionary<string, bool>
694+
{
695+
["clipboard"] = true,
696+
["notes"] = false,
697+
["favorites"] = true,
698+
["folders"] = false,
699+
["snippets"] = false,
700+
};
701+
}
702+
703+
public static void SaveWidgetEnabled(bool enabled)
704+
{
705+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetEnabled"] = enabled });
706+
}
707+
708+
public static bool LoadWidgetEnabled()
709+
{
710+
try
711+
{
712+
if (File.Exists(SettingsPath))
713+
{
714+
var json = File.ReadAllText(SettingsPath);
715+
using var doc = JsonDocument.Parse(json);
716+
if (doc.RootElement.TryGetProperty("widgetEnabled", out var prop))
717+
return prop.GetBoolean();
718+
}
719+
}
720+
catch { }
721+
return false;
722+
}
723+
724+
public static void SaveWidgetPosition(int x, int y)
725+
{
726+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetX"] = x, ["widgetY"] = y });
727+
}
728+
729+
public static void SaveWidgetSize(int w, int h)
730+
{
731+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetW"] = w, ["widgetH"] = h });
732+
}
733+
734+
public static (int W, int H)? LoadWidgetSize()
735+
{
736+
try
737+
{
738+
if (!File.Exists(SettingsPath)) return null;
739+
var json = File.ReadAllText(SettingsPath);
740+
using var doc = JsonDocument.Parse(json);
741+
if (doc.RootElement.TryGetProperty("widgetW", out var wProp) &&
742+
doc.RootElement.TryGetProperty("widgetH", out var hProp) &&
743+
wProp.TryGetInt32(out var w) &&
744+
hProp.TryGetInt32(out var h))
745+
{
746+
return (w, h);
747+
}
748+
}
749+
catch { }
750+
return null;
751+
}
752+
753+
public static void SaveWidgetOpacity(double opacity)
754+
{
755+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetOpacity"] = opacity });
756+
}
757+
758+
public static double LoadWidgetOpacity()
759+
{
760+
try
761+
{
762+
if (File.Exists(SettingsPath))
763+
{
764+
var json = File.ReadAllText(SettingsPath);
765+
using var doc = JsonDocument.Parse(json);
766+
if (doc.RootElement.TryGetProperty("widgetOpacity", out var prop))
767+
return prop.GetDouble();
768+
}
769+
}
770+
catch { }
771+
return 1.0;
772+
}
773+
774+
public static void SaveWidgetCompact(bool compact)
775+
{
776+
MergeAndSaveSettings(new Dictionary<string, object> { ["widgetCompact"] = compact });
777+
}
778+
779+
public static bool LoadWidgetCompact()
780+
{
781+
try
782+
{
783+
if (File.Exists(SettingsPath))
784+
{
785+
var json = File.ReadAllText(SettingsPath);
786+
using var doc = JsonDocument.Parse(json);
787+
if (doc.RootElement.TryGetProperty("widgetCompact", out var prop))
788+
return prop.GetBoolean();
789+
}
790+
}
791+
catch { }
792+
return false;
793+
}
794+
795+
public static (int X, int Y)? LoadWidgetPosition()
796+
{
797+
try
798+
{
799+
if (!File.Exists(SettingsPath)) return null;
800+
var json = File.ReadAllText(SettingsPath);
801+
using var doc = JsonDocument.Parse(json);
802+
if (doc.RootElement.TryGetProperty("widgetX", out var xProp) &&
803+
doc.RootElement.TryGetProperty("widgetY", out var yProp) &&
804+
xProp.TryGetInt32(out var x) &&
805+
yProp.TryGetInt32(out var y))
806+
{
807+
return (x, y);
808+
}
809+
}
810+
catch { }
811+
return null;
812+
}
813+
669814
public static void SaveMainWindowPosition(Windows.Graphics.PointInt32 position, Windows.Graphics.SizeInt32 size)
670815
{
671816
MergeAndSaveSettings(new Dictionary<string, object>

‎ClipboardHistoryManager.cs‎

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System.Text.Json;
2+
3+
namespace NoteUI;
4+
5+
public class ClipboardHistoryEntry
6+
{
7+
public string Id { get; set; } = Guid.NewGuid().ToString();
8+
public string ContentType { get; set; } = "text"; // "text" or "image"
9+
public string? TextContent { get; set; }
10+
public string? RtfContent { get; set; }
11+
public byte[]? ImageData { get; set; }
12+
public string? SourceExePath { get; set; }
13+
public string? SourceTitle { get; set; }
14+
public DateTime CapturedAt { get; set; } = DateTime.Now;
15+
public bool IsPinned { get; set; }
16+
17+
public string Preview => ContentType == "image"
18+
? $"[{Lang.T("clipboard_image")}]"
19+
: string.IsNullOrEmpty(TextContent)
20+
? ""
21+
: TextContent.Length > 150 ? TextContent[..150] : TextContent;
22+
}
23+
24+
public class ClipboardHistoryManager
25+
{
26+
private const int MaxEntries = 50;
27+
private readonly List<ClipboardHistoryEntry> _entries = [];
28+
private readonly string _savePath;
29+
30+
public IReadOnlyList<ClipboardHistoryEntry> Entries => _entries;
31+
32+
public ClipboardHistoryManager()
33+
{
34+
var dir = Path.Combine(
35+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
36+
"NoteUI");
37+
_savePath = Path.Combine(dir, "clipboard_history.json");
38+
}
39+
40+
public void Load()
41+
{
42+
try
43+
{
44+
if (!File.Exists(_savePath)) return;
45+
var json = File.ReadAllText(_savePath);
46+
var entries = JsonSerializer.Deserialize<List<ClipboardHistoryEntry>>(json);
47+
if (entries != null)
48+
{
49+
_entries.Clear();
50+
_entries.AddRange(entries);
51+
}
52+
}
53+
catch { }
54+
}
55+
56+
public void Save()
57+
{
58+
try
59+
{
60+
Directory.CreateDirectory(Path.GetDirectoryName(_savePath)!);
61+
var json = JsonSerializer.Serialize(_entries, new JsonSerializerOptions { WriteIndented = true });
62+
File.WriteAllText(_savePath, json);
63+
}
64+
catch { }
65+
}
66+
67+
public void AddEntry(ClipboardHistoryEntry entry)
68+
{
69+
_entries.Insert(0, entry);
70+
while (_entries.Count > MaxEntries)
71+
{
72+
var oldest = _entries.LastOrDefault(e => !e.IsPinned);
73+
if (oldest != null)
74+
_entries.Remove(oldest);
75+
else
76+
break;
77+
}
78+
Save();
79+
}
80+
81+
public void RemoveEntry(string id)
82+
{
83+
_entries.RemoveAll(e => e.Id == id);
84+
Save();
85+
}
86+
87+
public void TogglePin(string id)
88+
{
89+
var entry = _entries.FirstOrDefault(e => e.Id == id);
90+
if (entry != null)
91+
{
92+
entry.IsPinned = !entry.IsPinned;
93+
Save();
94+
}
95+
}
96+
97+
public List<ClipboardHistoryEntry> GetSorted()
98+
{
99+
return _entries
100+
.OrderByDescending(e => e.IsPinned)
101+
.ThenByDescending(e => e.CapturedAt)
102+
.ToList();
103+
}
104+
105+
public void Clear()
106+
{
107+
_entries.RemoveAll(e => !e.IsPinned);
108+
Save();
109+
}
110+
111+
public bool IsDuplicate(string? text)
112+
{
113+
if (string.IsNullOrEmpty(text)) return false;
114+
return _entries.Any(e =>
115+
e.ContentType == "text" &&
116+
string.Equals(e.TextContent, text, StringComparison.Ordinal));
117+
}
118+
119+
public static string GetRelativeTime(DateTime dt)
120+
{
121+
var diff = DateTime.Now - dt;
122+
123+
if (diff.TotalMinutes < 1)
124+
return Lang.T("clipboard_just_now");
125+
if (diff.TotalMinutes < 60)
126+
return Lang.T("clipboard_minutes_ago", (int)diff.TotalMinutes);
127+
if (diff.TotalHours < 24)
128+
return Lang.T("clipboard_hours_ago", (int)diff.TotalHours);
129+
if (diff.TotalDays < 2)
130+
return Lang.T("clipboard_yesterday");
131+
return Lang.T("clipboard_days_ago", (int)diff.TotalDays);
132+
}
133+
}

0 commit comments

Comments
 (0)