@yufengtadian/freedom-cli 1.13.1 → 1.13.3

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.
Files changed (76) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +119 -15
  3. package/lib/agents.js +448 -0
  4. package/lib/build.js +189 -45
  5. package/lib/cli.js +258 -9
  6. package/lib/config.js +20 -1
  7. package/lib/desktop.js +133 -0
  8. package/lib/dev.js +149 -0
  9. package/lib/mcp.js +217 -0
  10. package/lib/release.js +90 -0
  11. package/lib/security.js +113 -54
  12. package/lib/shell.js +170 -63
  13. package/lib/toolchain.js +339 -0
  14. package/lib/tui.js +37 -1
  15. package/lib/utils.js +51 -0
  16. package/lib/verify.js +48 -12
  17. package/lib/webkit.js +45 -0
  18. package/package.json +3 -2
  19. package/shell/darwin-arm64/freedom-shell +0 -0
  20. package/shell/linux-x64/freedom-shell +0 -0
  21. package/shell/win-x64/freedom-shell.exe +0 -0
  22. package/skill/freedom/SKILL.md +159 -0
  23. package/templates/desktop/app.html +559 -0
  24. package/templates/desktop/backend/desktop.mjs +327 -0
  25. package/templates/desktop/freedom.config.js +32 -0
  26. package/templates/desktop/icon.ico +0 -0
  27. package/templates/go/go.mod +3 -0
  28. package/templates/go/pkg/freedom/anti_debug_linux.go +69 -0
  29. package/templates/go/pkg/freedom/anti_debug_other.go +5 -3
  30. package/templates/go/pkg/freedom/anti_debug_windows.go +238 -11
  31. package/templates/go/pkg/freedom/assets/freedom.js +3 -0
  32. package/templates/go/pkg/freedom/assets_embed.go +1 -0
  33. package/templates/go/pkg/freedom/backend_proc.go +5 -5
  34. package/templates/go/pkg/freedom/events_windows.go +5 -5
  35. package/templates/go/pkg/freedom/freedom.go +64 -10
  36. package/templates/go/pkg/freedom/msgwindow_windows.go +8 -8
  37. package/templates/go/pkg/freedom/resources.go +119 -14
  38. package/templates/go/pkg/freedom/securetemp.go +61 -0
  39. package/templates/go/pkg/freedom/securetemp_other.go +20 -0
  40. package/templates/go/pkg/freedom/securetemp_windows.go +25 -0
  41. package/templates/go/pkg/freedom/security.go +293 -64
  42. package/templates/go/pkg/freedom/shutdown.go +55 -0
  43. package/templates/go/pkg/freedom/shutdown_windows.go +47 -0
  44. package/templates/go/pkg/freedom/store.go +11 -3
  45. package/templates/go/pkg/freedom/syscap_windows.go +33 -33
  46. package/templates/go/pkg/freedom/sysint_windows.go +2 -2
  47. package/templates/go/pkg/freedom/tray_windows.go +18 -18
  48. package/templates/go/pkg/freedom/updater.go +3 -3
  49. package/templates/go/pkg/freedom/window_other.go +7 -1
  50. package/templates/go/pkg/freedom/window_windows.go +185 -17
  51. package/templates/go/third_party/webview_go/CHANGELOG.md +15 -0
  52. package/templates/go/third_party/webview_go/FREEDOM-PATCH.md +32 -0
  53. package/templates/go/third_party/webview_go/LICENSE +22 -0
  54. package/templates/go/third_party/webview_go/README.md +50 -0
  55. package/templates/go/third_party/webview_go/glue.c +36 -0
  56. package/templates/go/third_party/webview_go/go.mod +3 -0
  57. package/templates/go/third_party/webview_go/go.sum +0 -0
  58. package/templates/go/third_party/webview_go/libs/mswebview2/LICENSE +27 -0
  59. package/templates/go/third_party/webview_go/libs/mswebview2/include/WebView2.h +23568 -0
  60. package/templates/go/third_party/webview_go/libs/mswebview2/include/vendor.go +2 -0
  61. package/templates/go/third_party/webview_go/libs/mswebview2/vendor.go +2 -0
  62. package/templates/go/third_party/webview_go/libs/mswebview2/version.txt +1 -0
  63. package/templates/go/third_party/webview_go/libs/webview/LICENSE +22 -0
  64. package/templates/go/third_party/webview_go/libs/webview/include/vendor.go +2 -0
  65. package/templates/go/third_party/webview_go/libs/webview/include/webview.h +3598 -0
  66. package/templates/go/third_party/webview_go/libs/webview/vendor.go +2 -0
  67. package/templates/go/third_party/webview_go/libs/webview/version.txt +1 -0
  68. package/templates/go/third_party/webview_go/webkit2_40.go +21 -0
  69. package/templates/go/third_party/webview_go/webkit2_41.go +8 -0
  70. package/templates/go/third_party/webview_go/webview.cc +1 -0
  71. package/templates/go/third_party/webview_go/webview.go +332 -0
  72. package/templates/installer/app.nsi +43 -0
  73. package/templates/project/freedom.config.js +14 -0
  74. package/templates/project/freedom.d.ts +77 -0
  75. package/templates/project-minimal/freedom.config.js +14 -0
  76. package/templates/project-minimal/freedom.d.ts +77 -0
@@ -26,7 +26,7 @@ import (
26
26
  var comInitDone atomic.Bool
27
27
 
28
28
  var (
29
- ole32 = syscall.NewLazyDLL("ole32.dll")
29
+ ole32 = syscall.NewLazyDLL("ole32.dll")
30
30
  procCoCreateInstance = ole32.NewProc("CoCreateInstance")
31
31
  procCoTaskMemFree = ole32.NewProc("CoTaskMemFree")
32
32
  procCoInitializeEx = ole32.NewProc("CoInitializeEx")
@@ -102,6 +102,7 @@ func comVtable(ppv uintptr, index uint32) uintptr {
102
102
  }
103
103
 
104
104
  // comVtableImpl 是实际的 vtable 查找实现,通过单独函数隔离 vet 检查。
105
+ //
105
106
  //go:noinline
106
107
  func comVtableImpl(ppv uintptr, index uint32) uintptr {
107
108
  // 读取 vtable 指针:COM 对象首 8 字节是指针数组。
@@ -146,10 +147,10 @@ var (
146
147
 
147
148
  // ITaskbarList3 vtable 索引(0=QueryInterface,1=AddRef,2=Release)。
148
149
  const (
149
- tb3HrInit = 3
150
- tb3SetProgressValue = 8
151
- tb3SetProgressState = 9
152
- tb3SetOverlayIcon = 17
150
+ tb3HrInit = 3
151
+ tb3SetProgressValue = 8
152
+ tb3SetProgressState = 9
153
+ tb3SetOverlayIcon = 17
153
154
  )
154
155
 
155
156
  // TBPFLAG 任务栏进度条状态。
@@ -170,7 +171,7 @@ func taskbarList3() uintptr {
170
171
  hr, _, _ := procCoCreateInstance.Call(
171
172
  uintptr(unsafe.Pointer(&clsidTaskbarList)),
172
173
  0,
173
- 0x1 /*CLSCTX_INPROC_SERVER*/,
174
+ 0x1, /*CLSCTX_INPROC_SERVER*/
174
175
  uintptr(unsafe.Pointer(&iidTaskbarList3)),
175
176
  uintptr(unsafe.Pointer(&ppv)),
176
177
  )
@@ -265,11 +266,11 @@ const (
265
266
  dwmwaSystemBackdropType = 38
266
267
  dwmwaBorderColor = 34
267
268
 
268
- dwmsbtAuto = 0
269
- dwmsbtNone = 1
270
- dwmsbtMainWindow = 2 // Mica
271
- dwmsbtTransientWin = 3 // Acrylic
272
- dwmsbtTabbedWindow = 4
269
+ dwmsbtAuto = 0
270
+ dwmsbtNone = 1
271
+ dwmsbtMainWindow = 2 // Mica
272
+ dwmsbtTransientWin = 3 // Acrylic
273
+ dwmsbtTabbedWindow = 4
273
274
 
274
275
  dwmwcpDefault = 0
275
276
  dwmwcpDoNotRound = 1
@@ -401,9 +402,9 @@ func hiconFromPNG(pngData []byte) uintptr {
401
402
  }
402
403
 
403
404
  type iconInfo struct {
404
- fIcon int32
405
- xHotspot, yHotspot uint32
406
- hbmMask, hbmColor uintptr
405
+ fIcon int32
406
+ xHotspot, yHotspot uint32
407
+ hbmMask, hbmColor uintptr
407
408
  }
408
409
  ii := iconInfo{fIcon: 1, hbmMask: hbmMask, hbmColor: hbmColor}
409
410
  hicon, _, _ := procCreateIconIndirect.Call(uintptr(unsafe.Pointer(&ii)))
@@ -412,7 +413,6 @@ func hiconFromPNG(pngData []byte) uintptr {
412
413
  return hicon
413
414
  }
414
415
 
415
-
416
416
  // syscallMessageBox 调用系统消息框。返回按钮标识(ok/yes/no/cancel)。
417
417
  func syscallMessageBox(hwnd uintptr, title, message, buttons, icon string) (string, error) {
418
418
  var flags uintptr
@@ -465,33 +465,33 @@ var procMessageBoxW = user32win.NewProc("MessageBoxW")
465
465
 
466
466
  // IFileOpenDialog / IFileSaveDialog / IShellItem COM 常量。
467
467
  var (
468
- clsidFileOpenDialog = comGUIDFromString("{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}")
469
- clsidFileSaveDialog = comGUIDFromString("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
470
- iidIFileOpenDialog = comGUIDFromString("{D57C7288-D4AD-4768-BE02-9D969532D960}")
471
- iidIFileSaveDialog = comGUIDFromString("{84BCCD23-5FDE-4CDB-AEA4-AF64B83D78AB}")
472
- iidIShellItem = comGUIDFromString("{43826D1E-E718-42EE-BC55-A1E261C37BFE}")
468
+ clsidFileOpenDialog = comGUIDFromString("{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}")
469
+ clsidFileSaveDialog = comGUIDFromString("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
470
+ iidIFileOpenDialog = comGUIDFromString("{D57C7288-D4AD-4768-BE02-9D969532D960}")
471
+ iidIFileSaveDialog = comGUIDFromString("{84BCCD23-5FDE-4CDB-AEA4-AF64B83D78AB}")
472
+ iidIShellItem = comGUIDFromString("{43826D1E-E718-42EE-BC55-A1E261C37BFE}")
473
473
  )
474
474
 
475
475
  // IFileDialog vtable 索引(IModalWindow: 0-3, IFileDialog: 4-20)。
476
476
  const (
477
- ifdSetFileTypes = 4
477
+ ifdSetFileTypes = 4
478
478
  ifdSetFileTypeIndex = 5
479
- ifdSetOptions = 9
480
- ifdSetTitle = 17
481
- ifdSetFileName = 15
482
- ifdGetResult = 20
483
- ifdShow = 3 // IModalWindow::Show
479
+ ifdSetOptions = 9
480
+ ifdSetTitle = 17
481
+ ifdSetFileName = 15
482
+ ifdGetResult = 20
483
+ ifdShow = 3 // IModalWindow::Show
484
484
  )
485
485
 
486
486
  // IFOS 选项(IFileDialogOptions)。
487
487
  const (
488
- fosOverwritePrompt = 0x00000002
489
- fosStrictFileTypes = 0x00000004
490
- fosNoChangeDir = 0x00000008
491
- fosForceFileSystem = 0x00000040
492
- fosAllowMultiSelect = 0x00000200
493
- fosPathMustExist = 0x00000800
494
- fosFileMustExist = 0x00001000
488
+ fosOverwritePrompt = 0x00000002
489
+ fosStrictFileTypes = 0x00000004
490
+ fosNoChangeDir = 0x00000008
491
+ fosForceFileSystem = 0x00000040
492
+ fosAllowMultiSelect = 0x00000200
493
+ fosPathMustExist = 0x00000800
494
+ fosFileMustExist = 0x00001000
495
495
  fosDefaultNoMiniMode = 0x20000000
496
496
  )
497
497
 
@@ -34,7 +34,7 @@ var (
34
34
  "alt": modAlt, "option": modAlt,
35
35
  "ctrl": modControl, "control": modControl,
36
36
  "shift": modShift,
37
- "win": modWin, "cmd": modWin, "meta": modWin, "super": modWin,
37
+ "win": modWin, "cmd": modWin, "meta": modWin, "super": modWin,
38
38
  }
39
39
  hotkeyVKs = map[string]uint32{}
40
40
  )
@@ -230,7 +230,7 @@ const (
230
230
  keyRead = 0x101
231
231
  regSz = 1
232
232
 
233
- runKeyPath = `Software\Microsoft\Windows\CurrentVersion\Run`
233
+ runKeyPath = `Software\Microsoft\Windows\CurrentVersion\Run`
234
234
  classesBase = `Software\Classes`
235
235
  )
236
236
 
@@ -39,12 +39,12 @@ const (
39
39
  tpmRightButton = 0x00000002
40
40
  tpmLeftAlign = 0x00000000
41
41
 
42
- mfString = 0x00000000
43
- mfSeparator = 0x00000800
44
- mfChecked = 0x00000008
45
- mfGrayed = 0x00000001
46
- mfEnabled = 0x00000000
47
- mfPopup = 0x00000010
42
+ mfString = 0x00000000
43
+ mfSeparator = 0x00000800
44
+ mfChecked = 0x00000008
45
+ mfGrayed = 0x00000001
46
+ mfEnabled = 0x00000000
47
+ mfPopup = 0x00000010
48
48
  mfByPosition = 0x00000400
49
49
  )
50
50
 
@@ -52,19 +52,19 @@ var (
52
52
  shell32 = syscall.NewLazyDLL("shell32.dll")
53
53
  procShellNotifyIconW = shell32.NewProc("Shell_NotifyIconW")
54
54
 
55
- procRegisterClassExW = user32win.NewProc("RegisterClassExW")
56
- procCreateWindowExW = user32win.NewProc("CreateWindowExW")
57
- procDefWindowProcW = user32win.NewProc("DefWindowProcW")
58
- procCreatePopupMenu = user32win.NewProc("CreatePopupMenu")
59
- procCreateMenu = user32win.NewProc("CreateMenu")
60
- procAppendMenuW = user32win.NewProc("AppendMenuW")
61
- procInsertMenuW = user32win.NewProc("InsertMenuW")
62
- procDestroyMenu = user32win.NewProc("DestroyMenu")
63
- procTrackPopupMenu = user32win.NewProc("TrackPopupMenu")
64
- procGetCursorPos = user32win.NewProc("GetCursorPos")
55
+ procRegisterClassExW = user32win.NewProc("RegisterClassExW")
56
+ procCreateWindowExW = user32win.NewProc("CreateWindowExW")
57
+ procDefWindowProcW = user32win.NewProc("DefWindowProcW")
58
+ procCreatePopupMenu = user32win.NewProc("CreatePopupMenu")
59
+ procCreateMenu = user32win.NewProc("CreateMenu")
60
+ procAppendMenuW = user32win.NewProc("AppendMenuW")
61
+ procInsertMenuW = user32win.NewProc("InsertMenuW")
62
+ procDestroyMenu = user32win.NewProc("DestroyMenu")
63
+ procTrackPopupMenu = user32win.NewProc("TrackPopupMenu")
64
+ procGetCursorPos = user32win.NewProc("GetCursorPos")
65
65
  procSetForegroundWindow = user32win.NewProc("SetForegroundWindow")
66
- procSetMenu = user32win.NewProc("SetMenu")
67
- procDrawMenuBar = user32win.NewProc("DrawMenuBar")
66
+ procSetMenu = user32win.NewProc("SetMenu")
67
+ procDrawMenuBar = user32win.NewProc("DrawMenuBar")
68
68
  )
69
69
 
70
70
  // ---- 结构 ----
@@ -178,9 +178,9 @@ func (a *App) CheckUpdate(ctx context.Context) (*UpdateInfo, error) {
178
178
  if !updateURLAllowed(m.URL) {
179
179
  return nil, fmt.Errorf("freedom: artifact URL scheme rejected: %q", m.URL)
180
180
  }
181
- cmp, err := compareVersions(Version, m.Version)
181
+ cmp, err := compareVersions(a.appVersion(), m.Version)
182
182
  if err != nil {
183
- return nil, fmt.Errorf("freedom: current version %q uncomparable: %w", Version, err)
183
+ return nil, fmt.Errorf("freedom: current version %q uncomparable: %w", a.appVersion(), err)
184
184
  }
185
185
  a.upMu.Lock()
186
186
  if cmp >= 0 {
@@ -333,7 +333,7 @@ func (a *App) updateCheckAsync() interface{} {
333
333
  return
334
334
  }
335
335
  if info == nil {
336
- a.Emit("update.upToDate", map[string]string{"current": Version})
336
+ a.Emit("update.upToDate", map[string]string{"current": a.appVersion()})
337
337
  return
338
338
  }
339
339
  a.Emit("update.available", info)
@@ -9,10 +9,13 @@ import "fmt"
9
9
  // 前端仍可通过 window.freedom.window.isFrameless() 感知当前是否无边框。
10
10
  func (a *App) applyTitleBar() {}
11
11
 
12
+ // setWindowIcon 在 macOS / Linux 上为空实现:图标由应用包(.icns / .desktop)决定。
13
+ func (a *App) setWindowIcon() {}
14
+
12
15
  // installWindowEvents / uninstallWindowEvents 在非 Windows 平台为空实现
13
16
  // (窗口事件与关闭拦截依赖 WndProc 子类化,仅 Windows 提供)。
14
17
  func (a *App) installWindowEvents() {}
15
- func (a *App) uninstallWindowEvents() {}
18
+ func (a *App) uninstallWindowEvents() {}
16
19
 
17
20
  // windowControl 处理前端 window.freedom.window.* 请求(macOS / Linux 占位实现)。
18
21
  // 动作集与 window_windows.go 保持一致:查询类返回中性值,动作类静默 no-op,
@@ -23,6 +26,9 @@ func windowControl(hwnd uintptr, action string, mode TitleBarMode, paramsJSON st
23
26
  // 仅 frameless 返回 true(与 window_windows.go 语义一致);
24
27
  // hidden 模式回退原生标题栏,前端不应自绘按钮。
25
28
  return mode == TitleBarFrameless, nil
29
+ case "appIcon":
30
+ // macOS / Linux 暂不提供 exe 内嵌图标提取,前端隐藏标题栏图标。
31
+ return "", nil
26
32
  case "isMaximized", "isMinimized", "isVisible", "isFocused", "isFullscreen":
27
33
  if action == "isVisible" {
28
34
  return true, nil
@@ -3,8 +3,14 @@
3
3
  package freedom
4
4
 
5
5
  import (
6
+ "bytes"
7
+ "encoding/base64"
8
+ "encoding/binary"
6
9
  "encoding/json"
7
10
  "fmt"
11
+ "image"
12
+ "image/color"
13
+ "image/png"
8
14
  "syscall"
9
15
  "unsafe"
10
16
  )
@@ -34,6 +40,12 @@ var (
34
40
  procGetModuleHandle = kernel32.NewProc("GetModuleHandleW")
35
41
  procLoadImage = user32win.NewProc("LoadImageW")
36
42
  procDestroyIcon = user32win.NewProc("DestroyIcon")
43
+ // PE 资源读取(exeIconData 提取内嵌 RT_ICON 用)与标题栏图标设置。
44
+ procFindResource = kernel32.NewProc("FindResourceW")
45
+ procSizeofResource = kernel32.NewProc("SizeofResource")
46
+ procLoadResource = kernel32.NewProc("LoadResource")
47
+ procLockResource = kernel32.NewProc("LockResource")
48
+ procSendMessage = user32win.NewProc("SendMessageW")
37
49
 
38
50
  dwmapi = syscall.NewLazyDLL("dwmapi.dll")
39
51
  procDwmExtendFrameIntoArea = dwmapi.NewProc("DwmExtendFrameIntoClientArea")
@@ -44,8 +56,8 @@ var (
44
56
  procSetWindowSubclass = comctl32.NewProc("SetWindowSubclass")
45
57
  procDefSubclassProc = comctl32.NewProc("DefSubclassProc")
46
58
 
47
- procGetWindowRect = user32win.NewProc("GetWindowRect")
48
- procGetClientRect = user32win.NewProc("GetClientRect")
59
+ procGetWindowRect = user32win.NewProc("GetWindowRect")
60
+ procGetClientRect = user32win.NewProc("GetClientRect")
49
61
  procBringWindowToTop = user32win.NewProc("BringWindowToTop")
50
62
  procIsIconic = user32win.NewProc("IsIconic")
51
63
  procIsWindowVisible = user32win.NewProc("IsWindowVisible")
@@ -57,12 +69,12 @@ var (
57
69
  procEnumDisplayMonitors = user32win.NewProc("EnumDisplayMonitors")
58
70
 
59
71
  // W6 审查修复追加:PNG→HICON 绘制(gdi32)、图标合成、子类化摘除、单实例互斥量。
60
- gdi32 = syscall.NewLazyDLL("gdi32.dll")
61
- procCreateDIBSection = gdi32.NewProc("CreateDIBSection")
62
- procDeleteObject = gdi32.NewProc("DeleteObject")
63
- procCreateIconIndirect = user32win.NewProc("CreateIconIndirect")
72
+ gdi32 = syscall.NewLazyDLL("gdi32.dll")
73
+ procCreateDIBSection = gdi32.NewProc("CreateDIBSection")
74
+ procDeleteObject = gdi32.NewProc("DeleteObject")
75
+ procCreateIconIndirect = user32win.NewProc("CreateIconIndirect")
64
76
  procRemoveWindowSubclass = comctl32.NewProc("RemoveWindowSubclass")
65
- procCreateMutexW = kernel32.NewProc("CreateMutexW")
77
+ procCreateMutexW = kernel32.NewProc("CreateMutexW")
66
78
 
67
79
  shcore = syscall.NewLazyDLL("shcore.dll")
68
80
  procGetDpiForMonitor = shcore.NewProc("GetDpiForMonitor")
@@ -90,20 +102,20 @@ var (
90
102
  procGlobalSize = kernel32.NewProc("GlobalSize")
91
103
  procGlobalFree = kernel32.NewProc("GlobalFree")
92
104
 
93
- procFindWindowW = user32win.NewProc("FindWindowW")
105
+ procFindWindowW = user32win.NewProc("FindWindowW")
94
106
  procSendMessageTimeoutW = user32win.NewProc("SendMessageTimeoutW")
95
107
 
96
108
  procShellExecuteW = shell32.NewProc("ShellExecuteW")
97
- shlwapi = syscall.NewLazyDLL("shlwapi.dll")
98
- procSHDeleteKeyW = shlwapi.NewProc("SHDeleteKeyW")
109
+ shlwapi = syscall.NewLazyDLL("shlwapi.dll")
110
+ procSHDeleteKeyW = shlwapi.NewProc("SHDeleteKeyW")
99
111
 
100
- advapi32 = syscall.NewLazyDLL("advapi32.dll")
101
- procRegOpenKeyExW = advapi32.NewProc("RegOpenKeyExW")
102
- procRegCreateKeyExW = advapi32.NewProc("RegCreateKeyExW")
103
- procRegSetValueExW = advapi32.NewProc("RegSetValueExW")
104
- procRegQueryValueExW = advapi32.NewProc("RegQueryValueExW")
105
- procRegDeleteValueW = advapi32.NewProc("RegDeleteValueW")
106
- procRegCloseKey = advapi32.NewProc("RegCloseKey")
112
+ advapi32 = syscall.NewLazyDLL("advapi32.dll")
113
+ procRegOpenKeyExW = advapi32.NewProc("RegOpenKeyExW")
114
+ procRegCreateKeyExW = advapi32.NewProc("RegCreateKeyExW")
115
+ procRegSetValueExW = advapi32.NewProc("RegSetValueExW")
116
+ procRegQueryValueExW = advapi32.NewProc("RegQueryValueExW")
117
+ procRegDeleteValueW = advapi32.NewProc("RegDeleteValueW")
118
+ procRegCloseKey = advapi32.NewProc("RegCloseKey")
107
119
 
108
120
  procSetProcessDpiAwarenessContext = user32win.NewProc("SetProcessDpiAwarenessContext")
109
121
  )
@@ -140,6 +152,11 @@ const (
140
152
 
141
153
  smCXSmall = 13 // SM_CXSMICON
142
154
  smCYSmall = 14 // SM_CYSMICON
155
+
156
+ smCXIcon = 11 // SM_CXICON:任务栏 / Alt-Tab 大图标尺寸
157
+ smCYIcon = 12 // SM_CYICON
158
+ wmSetIcon = 0x0080 // WM_SETICON:设置窗口大/小图标(ICON_BIG=1 / ICON_SMALL=0)
159
+ rtIcon = 3 // RT_ICON:ICO 内嵌单幅图像资源类型
143
160
  )
144
161
 
145
162
  // gwlStyle = GWL_STYLE(-16)。用变量声明,避免 uintptr 常量转换溢出。
@@ -205,6 +222,9 @@ func windowControl(hwnd uintptr, action string, mode TitleBarMode, paramsJSON st
205
222
  // 仅 frameless 返回 true:hidden 模式保留 DWM 原生按钮,
206
223
  // 前端若据 isFrameless 自绘按钮会与原生按钮重叠。
207
224
  return mode == TitleBarFrameless, nil
225
+ case "appIcon":
226
+ // exe 内嵌图标 → PNG data URL(无边框自绘标题栏用);无图标时返回 ""。
227
+ return exeAppIconDataURL(), nil
208
228
 
209
229
  // ---- W1 对标 Tauri:位置 / 尺寸 ----
210
230
  case "setPosition":
@@ -509,6 +529,154 @@ func loadExeIcon(cx, cy uintptr) uintptr {
509
529
  return 0
510
530
  }
511
531
 
532
+ // exeIconData 读取当前 exe 内嵌的 RT_ICON 资源原始字节。
533
+ // 不依赖 RT_GROUP_ICON 的 ID(不同注入工具写入的资源 ID 可能不同):
534
+ // 先按 rcedit 约定 RT_ICON ID=1 读取,失败回退 ID=0,与 loadExeIcon 的 ID 假设解耦。
535
+ func exeIconData() []byte {
536
+ hInst, _, _ := procGetModuleHandle.Call(0)
537
+ if hInst == 0 {
538
+ return nil
539
+ }
540
+ for _, id := range []uintptr{1, 0} {
541
+ // FindResourceW(hModule, MAKEINTRESOURCE(id), MAKEINTRESOURCE(RT_ICON))
542
+ hRes, _, _ := procFindResource.Call(hInst, id, rtIcon)
543
+ if hRes == 0 {
544
+ continue
545
+ }
546
+ size, _, _ := procSizeofResource.Call(hInst, hRes)
547
+ if size == 0 {
548
+ continue
549
+ }
550
+ hData, _, _ := procLoadResource.Call(hInst, hRes)
551
+ if hData == 0 {
552
+ continue
553
+ }
554
+ ptr, _, _ := procLockResource.Call(hData)
555
+ if ptr == 0 {
556
+ continue
557
+ }
558
+ return unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size)
559
+ }
560
+ return nil
561
+ }
562
+
563
+ // isPNG 判断字节流是否为 PNG 编码(.ico 内嵌 PNG 时 RT_ICON 数据即 PNG)。
564
+ func isPNG(b []byte) bool {
565
+ return len(b) >= 8 && b[0] == 0x89 && b[1] == 'P' && b[2] == 'N' && b[3] == 'G'
566
+ }
567
+
568
+ // exeAppIconDataURL 从当前 exe 的 PE 资源图标(RT_ICON)提取为 PNG data URL,
569
+ // 供无边框模式前端自绘标题栏显示应用图标——不依赖 resources 文件夹里的任何文件。
570
+ // 无法提取时返回空字符串(前端隐藏图标)。
571
+ func exeAppIconDataURL() string {
572
+ data := exeIconData()
573
+ if len(data) == 0 {
574
+ return ""
575
+ }
576
+ if isPNG(data) {
577
+ return "data:image/png;base64," + base64.StdEncoding.EncodeToString(data)
578
+ }
579
+ img, err := dibToPNG(data)
580
+ if err != nil {
581
+ return ""
582
+ }
583
+ return "data:image/png;base64," + base64.StdEncoding.EncodeToString(img)
584
+ }
585
+
586
+ // setWindowIcon 把 exe 内嵌的应用程序图标同步到窗口标题栏与任务栏:
587
+ // - native 模式:标题栏图标(ICON_SMALL)与任务栏 / Alt-Tab 图标(ICON_BIG)均来自 exe 图标,
588
+ // 保证「标题栏图标 = exe 图标」;
589
+ // - frameless 模式:无标题栏,但任务栏 / Alt-Tab 仍显示 exe 图标。
590
+ //
591
+ // 图标经 LoadImageW 从 PE 资源 RT_GROUP_ICON 加载,不依赖 resources 里的图标文件;
592
+ // exe 未注入图标时静默跳过(保留系统默认图标)。
593
+ func (a *App) setWindowIcon() {
594
+ hwnd := a.WindowHandle()
595
+ if hwnd == 0 {
596
+ return
597
+ }
598
+ cxIcon, _, _ := procGetSystemMetrics.Call(smCXIcon)
599
+ cyIcon, _, _ := procGetSystemMetrics.Call(smCYIcon)
600
+ cxSmall, _, _ := procGetSystemMetrics.Call(smCXSmall)
601
+ cySmall, _, _ := procGetSystemMetrics.Call(smCYSmall)
602
+ big := loadExeIcon(cxIcon, cyIcon)
603
+ small := loadExeIcon(cxSmall, cySmall)
604
+ if big != 0 {
605
+ procSendMessage.Call(hwnd, wmSetIcon, 1 /* ICON_BIG */, big)
606
+ procDestroyIcon.Call(big)
607
+ }
608
+ if small != 0 {
609
+ procSendMessage.Call(hwnd, wmSetIcon, 0 /* ICON_SMALL */, small)
610
+ procDestroyIcon.Call(small)
611
+ }
612
+ }
613
+
614
+ // dibToPNG 将 ICO 内嵌的 DIB 图像(BITMAPINFOHEADER 起始的裸 DIB,即 .ico 未内嵌
615
+ // PNG 时的 RT_ICON 数据)纯 Go 解析为 PNG 字节,不依赖 GDI 绘图 API。
616
+ // 支持 32/24 bpp;透明由 AND mask 决定(对应位=1 表示透明)。
617
+ func dibToPNG(data []byte) ([]byte, error) {
618
+ if len(data) < 40 {
619
+ return nil, fmt.Errorf("DIB too short: %d bytes", len(data))
620
+ }
621
+ biSize := binary.LittleEndian.Uint32(data[0:4])
622
+ if biSize < 40 {
623
+ return nil, fmt.Errorf("unsupported BITMAPINFOHEADER size %d", biSize)
624
+ }
625
+ width := int(binary.LittleEndian.Uint32(data[4:8]))
626
+ heightRaw := int32(binary.LittleEndian.Uint32(data[8:12]))
627
+ bitCount := binary.LittleEndian.Uint16(data[14:16])
628
+ if width <= 0 || heightRaw == 0 {
629
+ return nil, fmt.Errorf("bad dimensions %dx%d", width, heightRaw)
630
+ }
631
+ bpp := (int(bitCount) + 7) / 8
632
+ if bpp != 3 && bpp != 4 {
633
+ return nil, fmt.Errorf("unsupported bitcount %d", bitCount)
634
+ }
635
+ height := int(heightRaw)
636
+ if height < 0 {
637
+ height = -height
638
+ }
639
+ // ICO 的 DIB 高度可能为实际像素高度的 2 倍(上方 XOR + 下方 AND mask),
640
+ // 此时需折半,否则图像会被纵向拉伸。
641
+ pixelRowBytes := (width*bpp + 3) &^ 3
642
+ andRowBytes := ((width + 31) / 32) * 4
643
+ pxLen := pixelRowBytes * height
644
+ if pxLen > len(data)-int(biSize) && height%2 == 0 {
645
+ height /= 2
646
+ pxLen = pixelRowBytes * height
647
+ }
648
+ pxStart := int(biSize)
649
+ andStart := pxStart + pxLen
650
+ // AND mask 是否存在以实际数据长度为准:ICO 的 DIB 若数据不足(无 AND mask 段),
651
+ // andStart+andRowBytes*height 会超出 len(data),下方读取将越界 panic 崩掉壳进程。
652
+ hasAndMask := andStart+andRowBytes*height <= len(data)
653
+ img := image.NewRGBA(image.Rect(0, 0, width, height))
654
+ for y := 0; y < height; y++ {
655
+ srcY := height - 1 - y // DIB 自底向上:数据首行是图像末行
656
+ row := pxStart + srcY*pixelRowBytes
657
+ andRow := andStart + srcY*andRowBytes
658
+ for x := 0; x < width; x++ {
659
+ off := row + x*bpp
660
+ if off+bpp > len(data) {
661
+ return nil, fmt.Errorf("DIB data truncated at x=%d,y=%d", x, y)
662
+ }
663
+ b := data[off]
664
+ g := data[off+1]
665
+ r := data[off+2]
666
+ alpha := uint8(255)
667
+ if hasAndMask && (data[andRow+x/8]>>uint(7-x%8))&1 == 1 {
668
+ alpha = 0
669
+ }
670
+ img.SetRGBA(x, y, color.RGBA{R: r, G: g, B: b, A: alpha})
671
+ }
672
+ }
673
+ var buf bytes.Buffer
674
+ if err := png.Encode(&buf, img); err != nil {
675
+ return nil, err
676
+ }
677
+ return buf.Bytes(), nil
678
+ }
679
+
512
680
  // applyTitleBar 依据配置调整窗口标题栏(Windows 实现)。
513
681
  func (a *App) applyTitleBar() {
514
682
  hwnd := a.WindowHandle()
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ### Migrating from v0.1.1 to v0.10.0
4
+
5
+ 1. `Webview.Open()` has been removed. Use other webview APIs to create a window, open a link and run main UI loop.
6
+ 2. `Webview.Debug()` and `webview.Debugf()` have been removed. Use your favorite logging library to debug webview apps.
7
+ 3. `Webview.Settings` struct has been removed. Title, URL and size are controlled via other API setters and can be updated at any time, not only when webview is created.
8
+ 4. `Webview.Loop()` has been removed. Use `Run()` instead.
9
+ 5. `WebView.Run()`, `WebView.Terminate()`, `WebView.SetTitle()`, `WebView.Dispatch()` stayed the same.
10
+ 6. `WebView.Exit()` has been renamed to `WebView.Destroy()`
11
+ 7. `WebView.SetColor()` and `WebView.SetFullScreen()` have been removed. Use `Window()` to get native window handle and probably write some Cgo code to adjust native window to your taste.
12
+ 8. `Webview.Dialog` has been removed. But it is likely to be brought back as a standalone module.
13
+ 9. `WebView.Eval()` remained the same.
14
+ 10. `WebView.InjectCSS()` has been removed. Use eval to inject style tag with CSS inside.
15
+ 11. `WebView.Bind()` kept the name, but changed the semantics. Only functions can be bound. Not the structs, like in Lorca.
@@ -0,0 +1,32 @@
1
+ From: https://github.com/webview/webview_go
2
+ Upstream commit: 6173450d4dd6 (v0.0.0-20240831120633)
3
+ License: MIT (see LICENSE)
4
+
5
+ Why this copy exists
6
+ ------------------
7
+ Upstream hardcodes the CGO dependency name as `pkg-config: gtk+-3.0 webkit2gtk-4.0`.
8
+ Ubuntu 24.04 / Debian 13 removed the webkit2gtk-4.0 dev package (libsoup2 line) and ship
9
+ the API-compatible webkit2gtk-4.1 instead, so `go build` fails there with
10
+ "Package webkit2gtk-4.0 was not found".
11
+
12
+ What was changed (the only two deltas)
13
+ --------------------------------------
14
+ 1. webview.go: dropped the hardcoded linux `pkg-config:` line (comment marks the spot).
15
+ 2. webkit2_40.go / webkit2_41.go: the same directive, split by mutually exclusive build
16
+ tags. Default stays 4.0 (unchanged behaviour on old distros and in CI artifacts);
17
+ `-tags webkit2_41` selects 4.1.
18
+
19
+ Nothing else was touched: examples/ and webview_test.go were deleted, no code change.
20
+
21
+ Who flips the tag
22
+ -----------------
23
+ - repo/dev + CI: build.sh sources tools/webkit-env.sh (appends -tags=webkit2_41 to GOFLAGS)
24
+ - npm CLI: freedom-cli/lib/webkit.js (applyWebkitTags), used by `freedom shell build`
25
+ Detection is by pkg-config availability, so no user-facing flag is needed.
26
+
27
+ To refresh from upstream: copy the new tree over this directory, re-apply the two deltas
28
+ above, delete examples/ and webview_test.go, then re-sync the identical copy under
29
+ freedom-cli/templates/go/third_party/webview_go/ (it is what `freedom shell build` compiles).
30
+
31
+ webkitgtk-6.0 is NOT bridgeable this way (different C API: WebKitNetworkSession,
32
+ permission handlers); it needs an upstream port.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Serge Zaitsev
4
+ Copyright (c) 2020 webview
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # webview_go
2
+
3
+ [![GoDoc](https://godoc.org/github.com/webview/webview_go?status.svg)](https://godoc.org/github.com/webview/webview_go)
4
+ [![Go Report Card](https://goreportcard.com/badge/github.com/webview/webview_go)](https://goreportcard.com/report/github.com/webview/webview_go)
5
+
6
+ Go language binding for the [webview library][webview].
7
+
8
+ > [!NOTE]
9
+ > Versions <= 0.1.1 are available in the [old repository][webview].
10
+
11
+ ### Getting Started
12
+
13
+ See [Go package documentation][go-docs] for the Go API documentation, or simply read the source code.
14
+
15
+ Start with creating a new directory structure for your project.
16
+
17
+ ```sh
18
+ mkdir my-project && cd my-project
19
+ ```
20
+
21
+ Create a new Go module.
22
+
23
+ ```sh
24
+ go mod init example.com/app
25
+ ```
26
+
27
+ Save one of the example programs into your project directory.
28
+
29
+ ```sh
30
+ curl -sSLo main.go "https://raw.githubusercontent.com/webview/webview_go/master/examples/basic/main.go"
31
+ ```
32
+
33
+ Install dependencies.
34
+
35
+ ```sh
36
+ go get github.com/webview/webview_go
37
+ ```
38
+
39
+ Build the example. On Windows, add `-ldflags="-H windowsgui"` to the command line.
40
+
41
+ ```sh
42
+ go build
43
+ ```
44
+
45
+ ### Notes
46
+
47
+ Calling `Eval()` or `Dispatch()` before `Run()` does not work because the webview instance has only been configured and not yet started.
48
+
49
+ [go-docs]: https://pkg.go.dev/github.com/webview/webview_go
50
+ [webview]: https://github.com/webview/webview
@@ -0,0 +1,36 @@
1
+ #include "webview.h"
2
+
3
+ #include <stdlib.h>
4
+ #include <stdint.h>
5
+
6
+ struct binding_context {
7
+ webview_t w;
8
+ uintptr_t index;
9
+ };
10
+
11
+ void _webviewDispatchGoCallback(void *);
12
+ void _webviewBindingGoCallback(webview_t, char *, char *, uintptr_t);
13
+
14
+ static void _webview_dispatch_cb(webview_t w, void *arg) {
15
+ _webviewDispatchGoCallback(arg);
16
+ }
17
+
18
+ static void _webview_binding_cb(const char *id, const char *req, void *arg) {
19
+ struct binding_context *ctx = (struct binding_context *) arg;
20
+ _webviewBindingGoCallback(ctx->w, (char *)id, (char *)req, ctx->index);
21
+ }
22
+
23
+ void CgoWebViewDispatch(webview_t w, uintptr_t arg) {
24
+ webview_dispatch(w, _webview_dispatch_cb, (void *)arg);
25
+ }
26
+
27
+ void CgoWebViewBind(webview_t w, const char *name, uintptr_t index) {
28
+ struct binding_context *ctx = calloc(1, sizeof(struct binding_context));
29
+ ctx->w = w;
30
+ ctx->index = index;
31
+ webview_bind(w, name, _webview_binding_cb, (void *)ctx);
32
+ }
33
+
34
+ void CgoWebViewUnbind(webview_t w, const char *name) {
35
+ webview_unbind(w, name);
36
+ }