dsh-mini 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/Service.cs ADDED
@@ -0,0 +1,412 @@
1
+ using System;
2
+ using System.Diagnostics;
3
+ using System.Drawing;
4
+ using System.IO;
5
+ using System.Runtime.InteropServices;
6
+ using System.Text;
7
+ using System.Windows.Forms;
8
+
9
+ namespace DshHud
10
+ {
11
+ /// <summary>
12
+ /// Flat ini-style settings shared by the window shell, the background service and the
13
+ /// host plugin. Deliberately not JSON: powershell 5.1, C# 5 and node all have to agree
14
+ /// on it without pulling in a parser.
15
+ /// </summary>
16
+ internal sealed class HudConfig
17
+ {
18
+ internal bool CopyShot;
19
+ internal bool Ask;
20
+ internal string HotkeyFull;
21
+ internal string HotkeyRegion;
22
+
23
+ internal HudConfig()
24
+ {
25
+ CopyShot = true;
26
+ Ask = true;
27
+ HotkeyFull = "Ctrl+Shift+1";
28
+ HotkeyRegion = "Ctrl+Shift+2";
29
+ }
30
+
31
+ internal static string Dir
32
+ {
33
+ get
34
+ {
35
+ string dir = Path.Combine(
36
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "dsh-hud");
37
+ Directory.CreateDirectory(dir);
38
+ return dir;
39
+ }
40
+ }
41
+
42
+ internal static string ConfigPath { get { return Path.Combine(Dir, "config.ini"); } }
43
+ internal static string QuotePath { get { return Path.Combine(Dir, "quote.txt"); } }
44
+ internal static string CmdPath { get { return Path.Combine(Dir, "cmd.txt"); } }
45
+
46
+ internal static HudConfig Load()
47
+ {
48
+ HudConfig cfg = new HudConfig();
49
+ try
50
+ {
51
+ if (!File.Exists(ConfigPath)) return cfg;
52
+ foreach (string raw in File.ReadAllLines(ConfigPath, Encoding.UTF8))
53
+ {
54
+ string line = raw.Trim();
55
+ if (line.Length == 0 || line.StartsWith("#")) continue;
56
+ int eq = line.IndexOf('=');
57
+ if (eq <= 0) continue;
58
+ string key = line.Substring(0, eq).Trim();
59
+ string val = line.Substring(eq + 1).Trim();
60
+ if (key == "copyShot") cfg.CopyShot = val != "0";
61
+ else if (key == "ask") cfg.Ask = val != "0";
62
+ else if (key == "hotkeyFull") cfg.HotkeyFull = val;
63
+ else if (key == "hotkeyRegion") cfg.HotkeyRegion = val;
64
+ }
65
+ }
66
+ catch
67
+ {
68
+ }
69
+ return cfg;
70
+ }
71
+
72
+ internal void Save()
73
+ {
74
+ try
75
+ {
76
+ StringBuilder sb = new StringBuilder();
77
+ sb.AppendLine("# DSH 小窗设置 / settings. The settings page writes this file too.");
78
+ sb.AppendLine("copyShot=" + (CopyShot ? "1" : "0"));
79
+ sb.AppendLine("ask=" + (Ask ? "1" : "0"));
80
+ sb.AppendLine("hotkeyFull=" + HotkeyFull);
81
+ sb.AppendLine("hotkeyRegion=" + HotkeyRegion);
82
+ File.WriteAllText(ConfigPath, sb.ToString(), Encoding.UTF8);
83
+ }
84
+ catch
85
+ {
86
+ }
87
+ }
88
+
89
+ internal static long Stamp()
90
+ {
91
+ try
92
+ {
93
+ if (!File.Exists(ConfigPath)) return 0;
94
+ return File.GetLastWriteTimeUtc(ConfigPath).Ticks;
95
+ }
96
+ catch
97
+ {
98
+ return 0;
99
+ }
100
+ }
101
+ }
102
+
103
+ /// <summary>
104
+ /// Hidden window that only exists to own global hotkeys (RegisterHotKey needs a hwnd
105
+ /// and a message loop) and to be a BeginInvoke target for the watcher.
106
+ /// </summary>
107
+ internal sealed class HotkeyHost : Form
108
+ {
109
+ internal event Action<string> HotkeyPressed;
110
+
111
+ private const int WM_HOTKEY = 0x0312;
112
+ private const int ID_FULL = 0x1001;
113
+ private const int ID_REGION = 0x1002;
114
+
115
+ internal HotkeyHost()
116
+ {
117
+ FormBorderStyle = FormBorderStyle.None;
118
+ ShowInTaskbar = false;
119
+ StartPosition = FormStartPosition.Manual;
120
+ Size = new Size(1, 1);
121
+ Location = new Point(-4000, -4000);
122
+ Text = "DSH 划词服务";
123
+ }
124
+
125
+ internal void Bind(HudConfig cfg)
126
+ {
127
+ try
128
+ {
129
+ UnregisterHotKey(Handle, ID_FULL);
130
+ UnregisterHotKey(Handle, ID_REGION);
131
+
132
+ uint mods, vk;
133
+ if (TryParse(cfg.HotkeyFull, out mods, out vk))
134
+ {
135
+ if (!RegisterHotKey(Handle, ID_FULL, mods, vk))
136
+ SelectionWatcher.Log("hotkey full busy: " + cfg.HotkeyFull);
137
+ }
138
+ if (TryParse(cfg.HotkeyRegion, out mods, out vk))
139
+ {
140
+ if (!RegisterHotKey(Handle, ID_REGION, mods, vk))
141
+ SelectionWatcher.Log("hotkey region busy: " + cfg.HotkeyRegion);
142
+ }
143
+ }
144
+ catch (Exception ex)
145
+ {
146
+ SelectionWatcher.Log("hotkey bind failed: " + ex.Message);
147
+ }
148
+ }
149
+
150
+ internal static bool TryParse(string spec, out uint mods, out uint vk)
151
+ {
152
+ mods = 0;
153
+ vk = 0;
154
+ if (string.IsNullOrEmpty(spec)) return false;
155
+
156
+ string[] parts = spec.Split('+');
157
+ for (int i = 0; i < parts.Length; i++)
158
+ {
159
+ string part = parts[i].Trim();
160
+ if (part.Length == 0) continue;
161
+ string low = part.ToLowerInvariant();
162
+
163
+ if (low == "ctrl" || low == "control") { mods |= 0x0002; continue; }
164
+ if (low == "alt") { mods |= 0x0001; continue; }
165
+ if (low == "shift") { mods |= 0x0004; continue; }
166
+ if (low == "win") { mods |= 0x0008; continue; }
167
+
168
+ if (low.Length == 1)
169
+ {
170
+ char c = char.ToUpperInvariant(low[0]);
171
+ if (c >= 'A' && c <= 'Z') { vk = (uint)c; continue; }
172
+ if (c >= '0' && c <= '9') { vk = (uint)c; continue; }
173
+ }
174
+ if (low.Length >= 2 && low[0] == 'f')
175
+ {
176
+ int n;
177
+ if (int.TryParse(low.Substring(1), out n) && n >= 1 && n <= 24)
178
+ {
179
+ vk = (uint)(0x70 + n - 1);
180
+ continue;
181
+ }
182
+ }
183
+ if (low == "printscreen" || low == "prtsc") { vk = 0x2C; continue; }
184
+ if (low == "space") { vk = 0x20; continue; }
185
+ if (low == "tab") { vk = 0x09; continue; }
186
+ if (low == "enter" || low == "return") { vk = 0x0D; continue; }
187
+ if (low == "esc" || low == "escape") { vk = 0x1B; continue; }
188
+ return false;
189
+ }
190
+ return vk != 0;
191
+ }
192
+
193
+ protected override void OnVisibleChanged(EventArgs e)
194
+ {
195
+ base.OnVisibleChanged(e);
196
+ if (!Visible)
197
+ {
198
+ // never let the helper window surface, even if someone calls Show()
199
+ Hide();
200
+ }
201
+ }
202
+
203
+ protected override void WndProc(ref Message m)
204
+ {
205
+ if (m.Msg == WM_HOTKEY)
206
+ {
207
+ int id = m.WParam.ToInt32();
208
+ Action<string> handler = HotkeyPressed;
209
+ if (handler != null) handler(id == ID_REGION ? "region" : "full");
210
+ return;
211
+ }
212
+ base.WndProc(ref m);
213
+ }
214
+
215
+ [DllImport("user32.dll", SetLastError = true)]
216
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
217
+
218
+ [DllImport("user32.dll", SetLastError = true)]
219
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
220
+ }
221
+
222
+ /// <summary>
223
+ /// Background process that keeps "问大肥鱼" alive without the window: the pill must
224
+ /// work even when the HUD was never opened. It also owns the global screenshot hotkeys
225
+ /// and hands work to the window shell over files (quote.txt / cmd.txt).
226
+ /// </summary>
227
+ internal sealed class SelectionService : ApplicationContext
228
+ {
229
+ private readonly HotkeyHost host;
230
+ private readonly SelectionWatcher watcher;
231
+ private readonly NotifyIcon tray;
232
+ private readonly ToolStripMenuItem askItem;
233
+ private readonly string exePath;
234
+ private readonly string url;
235
+ private HudConfig config;
236
+ private long configStamp;
237
+ private readonly System.Windows.Forms.Timer configTimer;
238
+
239
+ internal SelectionService(string url)
240
+ {
241
+ this.url = url;
242
+ exePath = Application.ExecutablePath;
243
+ config = HudConfig.Load();
244
+ configStamp = HudConfig.Stamp();
245
+
246
+ host = new HotkeyHost();
247
+ IntPtr forceHandle = host.Handle; // create it now: hotkeys/message loop need it
248
+ host.HotkeyPressed += OnHotkey;
249
+
250
+ watcher = new SelectionWatcher(host);
251
+ watcher.QuoteRequested += DeliverQuote;
252
+ watcher.Enabled = config.Ask;
253
+
254
+ tray = new NotifyIcon();
255
+ tray.Icon = SystemIcons.Application;
256
+ tray.Text = "DSH 划词服务";
257
+ tray.Visible = true;
258
+ tray.DoubleClick += delegate { OpenHud(); };
259
+
260
+ ContextMenuStrip menu = new ContextMenuStrip();
261
+ menu.Items.Add("打开小窗", null, delegate { OpenHud(); });
262
+ askItem = new ToolStripMenuItem("划词问大肥鱼");
263
+ askItem.CheckOnClick = true;
264
+ askItem.Checked = config.Ask;
265
+ askItem.CheckedChanged += delegate
266
+ {
267
+ config.Ask = askItem.Checked;
268
+ config.Save();
269
+ configStamp = HudConfig.Stamp();
270
+ watcher.Enabled = config.Ask;
271
+ };
272
+ menu.Items.Add(askItem);
273
+ menu.Items.Add(new ToolStripSeparator());
274
+ menu.Items.Add("退出", null, delegate { ExitThread(); });
275
+ tray.ContextMenuStrip = menu;
276
+
277
+ host.Bind(config);
278
+ SelectionWatcher.Log("service started url=" + (url == null ? "none" : "yes"));
279
+
280
+ configTimer = new System.Windows.Forms.Timer();
281
+ configTimer.Interval = 2500;
282
+ configTimer.Tick += OnConfigTick;
283
+ configTimer.Start();
284
+ }
285
+
286
+ /// <summary>Diagnostic hook: pillrect.txt holds "x,y,w,h" and previews the pill placement.</summary>
287
+ private void CheckPillRect()
288
+ {
289
+ try
290
+ {
291
+ string path = Path.Combine(HudConfig.Dir, "pillrect.txt");
292
+ if (!File.Exists(path)) return;
293
+ string raw = File.ReadAllText(path).Trim();
294
+ File.Delete(path);
295
+ string[] parts = raw.Split(',');
296
+ if (parts.Length != 4) return;
297
+ Rectangle rect = new Rectangle(int.Parse(parts[0]), int.Parse(parts[1]),
298
+ int.Parse(parts[2]), int.Parse(parts[3]));
299
+ watcher.DebugPillAt(rect);
300
+ }
301
+ catch (Exception ex)
302
+ {
303
+ SelectionWatcher.Log("pillrect failed: " + ex.Message);
304
+ }
305
+ }
306
+
307
+ private void OnConfigTick(object sender, EventArgs e)
308
+ {
309
+ CheckPillRect();
310
+ long stamp = HudConfig.Stamp();
311
+ if (stamp == configStamp) return;
312
+ configStamp = stamp;
313
+ config = HudConfig.Load();
314
+ watcher.Enabled = config.Ask;
315
+ askItem.Checked = config.Ask;
316
+ host.Bind(config);
317
+ SelectionWatcher.Log("config reloaded: hotkeyFull=" + config.HotkeyFull +
318
+ " hotkeyRegion=" + config.HotkeyRegion);
319
+ }
320
+
321
+ private void OnHotkey(string kind)
322
+ {
323
+ try
324
+ {
325
+ SelectionWatcher.Log("hotkey " + kind);
326
+ File.WriteAllText(HudConfig.CmdPath, kind, Encoding.UTF8);
327
+ OpenHud();
328
+ }
329
+ catch (Exception ex)
330
+ {
331
+ SelectionWatcher.Log("hotkey failed: " + ex.Message);
332
+ }
333
+ }
334
+
335
+ /// <summary>The pill was clicked: park the text, then bring the window up.</summary>
336
+ private void DeliverQuote(string text)
337
+ {
338
+ try
339
+ {
340
+ File.WriteAllText(HudConfig.QuotePath, text, Encoding.UTF8);
341
+ SelectionWatcher.Log("quote saved " + text.Length + " chars");
342
+ }
343
+ catch (Exception ex)
344
+ {
345
+ SelectionWatcher.Log("quote save failed: " + ex.Message);
346
+ }
347
+ OpenHud();
348
+ }
349
+
350
+ private void OpenHud()
351
+ {
352
+ try
353
+ {
354
+ IntPtr hwnd = FindHudWindow();
355
+ if (hwnd != IntPtr.Zero)
356
+ {
357
+ Native.ShowWindow(hwnd, Native.SW_RESTORE);
358
+ Native.SetForegroundWindow(hwnd);
359
+ return;
360
+ }
361
+ if (string.IsNullOrEmpty(url))
362
+ {
363
+ SelectionWatcher.Log("spawn skipped: no url");
364
+ return;
365
+ }
366
+ Process.Start(exePath, "--url \"" + url + "\" --title \"DSH 小窗\"");
367
+ }
368
+ catch (Exception ex)
369
+ {
370
+ SelectionWatcher.Log("spawn failed: " + ex.Message);
371
+ }
372
+ }
373
+
374
+ private static IntPtr FindHudWindow()
375
+ {
376
+ try
377
+ {
378
+ string name = Path.GetFileNameWithoutExtension(Application.ExecutablePath);
379
+ foreach (Process p in Process.GetProcessesByName(name))
380
+ {
381
+ try
382
+ {
383
+ if (p.MainWindowHandle != IntPtr.Zero) return p.MainWindowHandle;
384
+ }
385
+ catch
386
+ {
387
+ }
388
+ finally
389
+ {
390
+ p.Dispose();
391
+ }
392
+ }
393
+ }
394
+ catch
395
+ {
396
+ }
397
+ return IntPtr.Zero;
398
+ }
399
+
400
+ protected override void Dispose(bool disposing)
401
+ {
402
+ if (disposing)
403
+ {
404
+ try { configTimer.Stop(); configTimer.Dispose(); } catch { }
405
+ try { if (watcher != null) watcher.Dispose(); } catch { }
406
+ try { if (tray != null) { tray.Visible = false; tray.Dispose(); } } catch { }
407
+ try { if (host != null) host.Dispose(); } catch { }
408
+ }
409
+ base.Dispose(disposing);
410
+ }
411
+ }
412
+ }
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
3
+ <assemblyIdentity version="1.0.0.0" name="DshHud.app" />
4
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
5
+ <windowsSettings>
6
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
7
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
8
+ <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
9
+ </windowsSettings>
10
+ </application>
11
+ <dependency>
12
+ <dependentAssembly>
13
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0"
14
+ processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
15
+ </dependentAssembly>
16
+ </dependency>
17
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
18
+ <application>
19
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
20
+ </application>
21
+ </compatibility>
22
+ </assembly>
Binary file
@@ -0,0 +1,112 @@
1
+ param(
2
+ [switch]$Clean
3
+ )
4
+
5
+ $ErrorActionPreference = 'Stop'
6
+
7
+ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
8
+ $vendor = Join-Path $here 'vendor'
9
+ $bin = Join-Path $here 'bin'
10
+ New-Item -ItemType Directory -Force -Path $vendor, $bin | Out-Null
11
+
12
+ if ($Clean) {
13
+ Get-ChildItem $bin -File -ErrorAction SilentlyContinue | Remove-Item -Force
14
+ }
15
+
16
+ $csc = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe'
17
+ if (-not (Test-Path $csc)) { throw "csc.exe not found at $csc" }
18
+
19
+ # --- vendor: WebView2 assemblies ---
20
+ # Order: app\vendor (committed) -> %TEMP%\wv2 (cache) -> NuGet download.
21
+ $wv2Version = '1.0.4314.35'
22
+ $wv2root = Join-Path $env:TEMP 'wv2'
23
+ $deps = @(
24
+ @{ src = "$wv2root\lib\net462\Microsoft.Web.WebView2.Core.dll"; dst = 'Microsoft.Web.WebView2.Core.dll' },
25
+ @{ src = "$wv2root\lib\net462\Microsoft.Web.WebView2.WinForms.dll"; dst = 'Microsoft.Web.WebView2.WinForms.dll' },
26
+ @{ src = "$wv2root\runtimes\win-x64\native\WebView2Loader.dll"; dst = 'WebView2Loader.dll' }
27
+ )
28
+
29
+ function Test-Cache {
30
+ foreach ($d in $deps) {
31
+ if (-not (Test-Path $d.src)) { return $false }
32
+ }
33
+ return $true
34
+ }
35
+
36
+ $missing = @()
37
+ foreach ($d in $deps) {
38
+ if ($Clean -or -not (Test-Path (Join-Path $vendor $d.dst))) { $missing += $d }
39
+ }
40
+
41
+ if ($missing.Count -gt 0 -and -not (Test-Cache)) {
42
+ Write-Output 'WebView2 assemblies not cached - downloading from NuGet...'
43
+ $pkg = Join-Path $env:TEMP ("webview2-" + $wv2Version + ".zip")
44
+ $url = 'https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/' + $wv2Version
45
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
46
+ Invoke-WebRequest -Uri $url -OutFile $pkg -UseBasicParsing
47
+ if (Test-Path $wv2root) { Remove-Item $wv2root -Recurse -Force }
48
+ New-Item -ItemType Directory -Force -Path $wv2root | Out-Null
49
+ Expand-Archive -Path $pkg -DestinationPath $wv2root -Force
50
+ Remove-Item $pkg -Force
51
+ if (-not (Test-Cache)) { throw "WebView2 package did not contain the expected files under $wv2root" }
52
+ }
53
+
54
+ foreach ($d in $missing) {
55
+ Copy-Item $d.src (Join-Path $vendor $d.dst) -Force
56
+ }
57
+
58
+ # --- generate the page-script constant from hud-page.js ---
59
+ $jsPath = Join-Path $here 'hud-page.js'
60
+ $js = [System.IO.File]::ReadAllText($jsPath, [System.Text.Encoding]::UTF8)
61
+ $escaped = $js.Replace('"', '""')
62
+ $gen = "namespace DshHud`r`n{`r`n internal static class HudPage`r`n {`r`n internal const string Script = @`"$escaped`";`r`n }`r`n}`r`n"
63
+ [System.IO.File]::WriteAllText((Join-Path $here 'hud-page.g.cs'), $gen, (New-Object System.Text.UTF8Encoding($true)))
64
+
65
+ # --- compile ---
66
+ # Built from code points on purpose: if this file is ever saved without a BOM,
67
+ # powershell 5.1 reads it as GBK and a literal '小窗' would become mojibake.
68
+ $exeBase = 'DSH' + [char]0x5C0F + [char]0x7A97
69
+ $exe = Join-Path $bin ($exeBase + '.exe')
70
+ $refs = @(
71
+ 'System.dll',
72
+ 'System.Core.dll',
73
+ 'System.Drawing.dll',
74
+ 'System.Windows.Forms.dll',
75
+ 'System.Xml.dll'
76
+ )
77
+
78
+ $cscArgs = @('/nologo', '/target:winexe', '/platform:x64', '/optimize+', '/langversion:5')
79
+ $cscArgs += ('/out:' + $exe)
80
+ $cscArgs += ('/win32manifest:' + (Join-Path $here 'app.manifest'))
81
+ foreach ($r in $refs) { $cscArgs += ('/r:' + $r) }
82
+ $cscArgs += ('/r:' + (Join-Path $vendor 'Microsoft.Web.WebView2.Core.dll'))
83
+ $cscArgs += ('/r:' + (Join-Path $vendor 'Microsoft.Web.WebView2.WinForms.dll'))
84
+ # UIA gives us the selection rectangle (and a passive read that needs no Ctrl+C).
85
+ $uiaRoot = 'C:\Windows\Microsoft.NET\assembly\GAC_MSIL'
86
+ $uiaClient = Join-Path $uiaRoot 'UIAutomationClient\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationClient.dll'
87
+ $uiaTypes = Join-Path $uiaRoot 'UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll'
88
+ foreach ($uia in @($uiaClient, $uiaTypes)) {
89
+ if (-not (Test-Path $uia)) { throw "UIA assembly missing: $uia" }
90
+ $cscArgs += ('/r:' + $uia)
91
+ }
92
+ # TextPattern.GetBoundingRectangles returns System.Windows.Rect, so WindowsBase is needed.
93
+ $windowsBase = Join-Path $uiaRoot 'WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll'
94
+ if (-not (Test-Path $windowsBase)) { throw "WindowsBase missing: $windowsBase" }
95
+ $cscArgs += ('/r:' + $windowsBase)
96
+ $cscArgs += (Join-Path $here 'HudWindow.cs')
97
+ $cscArgs += (Join-Path $here 'SelectionWatcher.cs')
98
+ $cscArgs += (Join-Path $here 'Service.cs')
99
+ $cscArgs += (Join-Path $here 'RegionCapture.cs')
100
+ $cscArgs += (Join-Path $here 'hud-page.g.cs')
101
+
102
+ & $csc @cscArgs
103
+ if ($LASTEXITCODE -ne 0) { throw "compile failed with exit code $LASTEXITCODE" }
104
+
105
+ # --- stage runtime files next to the exe ---
106
+ Copy-Item (Join-Path $vendor 'Microsoft.Web.WebView2.Core.dll') $bin -Force
107
+ Copy-Item (Join-Path $vendor 'Microsoft.Web.WebView2.WinForms.dll') $bin -Force
108
+ Copy-Item (Join-Path $vendor 'WebView2Loader.dll') $bin -Force
109
+
110
+ Write-Output ''
111
+ Write-Output 'build ok:'
112
+ Get-ChildItem $bin -File | ForEach-Object { ' ' + $_.Name + ' ' + $_.Length + ' bytes' }
package/app/dev.ps1 ADDED
@@ -0,0 +1,63 @@
1
+ param(
2
+ [switch]$Build,
3
+ [string]$Url = '',
4
+ [string]$Eval = '',
5
+ [int]$WaitSeconds = 9
6
+ )
7
+
8
+ $ErrorActionPreference = 'Continue'
9
+ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
10
+ $exe = Join-Path $here 'bin\DSH小窗.exe'
11
+ $stateDir = Join-Path $env:LOCALAPPDATA 'dsh-hud'
12
+
13
+ function Get-TokenUrl {
14
+ $body = '{"href":"http://127.0.0.1:58791/"}'
15
+ try {
16
+ $r = Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:58791/dsh-hud/open' `
17
+ -ContentType 'application/json' -Body $body -TimeoutSec 25
18
+ if ($r.url) { return $r.url }
19
+ } catch {
20
+ }
21
+ return ''
22
+ }
23
+
24
+ Get-Process -Name 'DSH小窗' -ErrorAction SilentlyContinue | Stop-Process -Force
25
+ Start-Sleep -Milliseconds 800
26
+
27
+ if ($Build) {
28
+ $blog = Join-Path $env:TEMP 'build-out.txt'
29
+ Start-Process -FilePath 'powershell.exe' `
30
+ -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', (Join-Path $here 'build-app.ps1')) `
31
+ -NoNewWindow -Wait -RedirectStandardOutput $blog -RedirectStandardError ($blog + '.err')
32
+ $lines = Get-Content $blog -Encoding Default
33
+ if ($lines -match 'error CS') {
34
+ $lines | Where-Object { $_ -match 'error CS' } | ForEach-Object { Write-Output $_ }
35
+ exit 1
36
+ }
37
+ Write-Output 'build ok'
38
+ }
39
+
40
+ $target = $Url
41
+ if ([string]::IsNullOrEmpty($target)) { $target = Get-TokenUrl }
42
+ if ([string]::IsNullOrEmpty($target)) {
43
+ Write-Output 'no url: pass -Url or have the dsh-hud-window plugin running'
44
+ exit 1
45
+ }
46
+
47
+ $p = Start-Process -FilePath $exe -ArgumentList @('--url', $target, '--title', 'DSH 小窗', '--debug') -PassThru
48
+ Write-Output ('started pid ' + $p.Id)
49
+ Start-Sleep -Seconds $WaitSeconds
50
+
51
+ if (-not [string]::IsNullOrEmpty($Eval)) {
52
+ $js = [System.IO.File]::ReadAllText($Eval, [System.Text.Encoding]::UTF8)
53
+ [System.IO.File]::WriteAllText((Join-Path $stateDir 'eval.js'), $js, [System.Text.UTF8Encoding]::new($false))
54
+ Start-Sleep -Seconds 3
55
+ $log = Join-Path $stateDir 'hud.log'
56
+ if (Test-Path $log) {
57
+ $t = [System.IO.File]::ReadAllText($log, [System.Text.Encoding]::UTF8)
58
+ Write-Output ('log length: ' + $t.Length)
59
+ if ($t.Length -gt 6000) { Write-Output $t.Substring(0, 6000) } else { Write-Output $t }
60
+ } else {
61
+ Write-Output 'no hud.log'
62
+ }
63
+ }