quickwin 2.3.1 → 3.0.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.
Files changed (172) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +1 -2
  3. package/constants.ts +15 -0
  4. package/enum_map.d.ts +3 -0
  5. package/examples/PdfCanvas.js +1 -1
  6. package/examples/PdfCanvas.tsx +2 -2
  7. package/examples/execd/main.js +334 -188
  8. package/examples/pdf_preview2.js +76 -67
  9. package/examples/pdf_preview2.ts +84 -64
  10. package/examples/pdf_viewer.js +4932 -1029
  11. package/examples/pdf_viewer.tsx +11 -22
  12. package/examples/test_gallery.js +308 -143
  13. package/examples/test_gallery.tsx +12 -7
  14. package/examples/test_react_lazy.js +295 -133
  15. package/examples/test_react_lazy.tsx +4 -3
  16. package/examples/test_react_listbox.js +293 -132
  17. package/examples/test_react_listbox.tsx +3 -2
  18. package/examples/test_root_window.js +283 -126
  19. package/examples/test_tab.js +295 -133
  20. package/examples/test_tab.tsx +4 -3
  21. package/examples/tray_demo.js +37 -30
  22. package/examples/tray_demo.tsx +39 -32
  23. package/lib/abort.d.ts +21 -0
  24. package/lib/assert.d.ts +2 -0
  25. package/lib/bitmap.d.ts +7 -0
  26. package/lib/bitmap.js +40 -0
  27. package/lib/cache-utils.d.ts +6 -0
  28. package/lib/certs.d.ts +1 -0
  29. package/lib/certs.js +31 -3
  30. package/lib/dpi.d.ts +9 -0
  31. package/lib/dpi.js +30 -0
  32. package/lib/dumpRects.d.ts +2 -0
  33. package/lib/dumpRects.js +14 -7
  34. package/lib/exec-log.d.ts +23 -0
  35. package/lib/fetch.d.ts +4 -0
  36. package/lib/ffi/bind.d.ts +70 -0
  37. package/lib/ffi/bind.js +5 -4
  38. package/lib/ffi/ctype.d.ts +117 -0
  39. package/lib/ffi/index.d.ts +2 -0
  40. package/lib/ffi/struct.d.ts +174 -0
  41. package/lib/http-common.d.ts +143 -0
  42. package/lib/http-server.d.ts +59 -0
  43. package/lib/polyfill.d.ts +22 -0
  44. package/lib/react-qw/components/Button.d.ts +9 -0
  45. package/lib/react-qw/components/CheckBox.d.ts +11 -0
  46. package/lib/react-qw/components/ComboBox.d.ts +11 -0
  47. package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
  48. package/lib/react-qw/components/DateTimePicker.js +6 -7
  49. package/lib/react-qw/components/Input.d.ts +15 -0
  50. package/lib/react-qw/components/Input.js +5 -1
  51. package/lib/react-qw/components/Link.d.ts +12 -0
  52. package/lib/react-qw/components/ListBox.d.ts +13 -0
  53. package/lib/react-qw/components/ListView.d.ts +37 -0
  54. package/lib/react-qw/components/ListView.js +8 -6
  55. package/lib/react-qw/components/PathPicker.d.ts +23 -0
  56. package/lib/react-qw/components/ProgressBar.d.ts +9 -0
  57. package/lib/react-qw/components/RadioButton.d.ts +11 -0
  58. package/lib/react-qw/components/ScrollView.d.ts +9 -0
  59. package/lib/react-qw/components/ScrollView.js +28 -22
  60. package/lib/react-qw/components/Slider.d.ts +12 -0
  61. package/lib/react-qw/components/Tab.d.ts +14 -0
  62. package/lib/react-qw/components/Tooltip.d.ts +7 -0
  63. package/lib/react-qw/components/TreeView.d.ts +18 -0
  64. package/lib/react-qw/components/index.d.ts +16 -0
  65. package/lib/react-qw/index.d.ts +24 -0
  66. package/lib/react-qw/index.js +7 -5
  67. package/lib/react-qw/layout.d.ts +29 -0
  68. package/lib/react-qw/nmhdr.d.ts +1 -0
  69. package/lib/react-qw/props.d.ts +2 -0
  70. package/lib/react-qw/props.js +3 -3
  71. package/lib/react-qw/reconciler.d.ts +21 -0
  72. package/lib/react-qw/reconciler.js +12 -17
  73. package/lib/stream.d.ts +20 -0
  74. package/lib/text-codec.d.ts +39 -0
  75. package/lib/text-measure.d.ts +14 -0
  76. package/lib/text-measure.js +9 -6
  77. package/lib/url.d.ts +54 -0
  78. package/lib/websocket.d.ts +73 -0
  79. package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
  80. package/lib/windows/comctl32.d.ts +14 -0
  81. package/lib/windows/comctl32.js +3 -2
  82. package/lib/windows/comdlg32.d.ts +4 -0
  83. package/lib/windows/{gdi32.ts → gdi32.d.ts} +184 -97
  84. package/lib/windows/gdi32.js +28 -6
  85. package/lib/windows/{kernel32.ts → kernel32.d.ts} +145 -113
  86. package/lib/windows/kernel32.js +9 -0
  87. package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
  88. package/lib/windows/ntdll.d.ts +5 -0
  89. package/lib/windows/ole32.d.ts +3 -0
  90. package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
  91. package/lib/windows/shell32.d.ts +51 -0
  92. package/lib/windows/shell32.js +11 -2
  93. package/lib/windows/structs.d.ts +3141 -0
  94. package/lib/windows/structs.js +51 -0
  95. package/lib/windows/user32.d.ts +623 -0
  96. package/lib/windows/user32.js +6 -5
  97. package/lib/windows/version.d.ts +27 -0
  98. package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
  99. package/main.js +13 -5
  100. package/package.json +16 -7
  101. package/quickwin.d.ts +13 -115
  102. package/qwin-nowasm-x86.exe +0 -0
  103. package/qwin-nowasm.exe +0 -0
  104. package/qwin-x86.exe +0 -0
  105. package/qwin.exe +0 -0
  106. package/test/bench_ffi_callraw.js +3 -4
  107. package/test/bench_ffi_callraw.ts +3 -4
  108. package/test/debug/mupdf_debug.js +4 -22
  109. package/test/debug/wasm_export_debug.js +2 -12
  110. package/test/debug/wasm_names.js +3 -13
  111. package/test/example.pdf +0 -0
  112. package/test/mupdf-cdn.js +40 -0
  113. package/test/mupdf-cdn.ts +43 -0
  114. package/test/test_ffi.js +8 -9
  115. package/test/test_ffi.ts +8 -9
  116. package/test/test_ffi_abi.js +6 -6
  117. package/test/test_ffi_abi.ts +8 -8
  118. package/test/test_ffi_windows.js +82 -11
  119. package/test/test_ffi_windows.ts +85 -11
  120. package/test/test_gdi_draw.js +3 -3
  121. package/test/test_gdi_draw.ts +4 -4
  122. package/test/test_mupdf_render.js +4 -22
  123. package/test/test_mupdf_render.ts +4 -23
  124. package/test/test_mupdf_twice.js +4 -19
  125. package/test/test_mupdf_twice.ts +4 -20
  126. package/test/test_mupdf_wasm.js +10 -4
  127. package/test/test_mupdf_wasm.ts +6 -4
  128. package/test/test_react_complex.js +316 -140
  129. package/test/test_react_counter.js +295 -133
  130. package/test/test_react_flex.js +297 -138
  131. package/test/test_react_hidden.js +296 -134
  132. package/test/test_react_listview_link.js +288 -131
  133. package/test/test_react_notify.js +290 -133
  134. package/test/test_react_render.js +295 -133
  135. package/lib/abort.ts +0 -69
  136. package/lib/assert.ts +0 -7
  137. package/lib/cache-utils.ts +0 -40
  138. package/lib/certs.ts +0 -229
  139. package/lib/dumpRects.ts +0 -26
  140. package/lib/exec-log.ts +0 -30
  141. package/lib/fetch.ts +0 -589
  142. package/lib/ffi/bind.ts +0 -539
  143. package/lib/ffi/ctype.ts +0 -371
  144. package/lib/ffi/index.ts +0 -6
  145. package/lib/ffi/struct.ts +0 -780
  146. package/lib/http-common.ts +0 -441
  147. package/lib/http-server.ts +0 -604
  148. package/lib/polyfill.ts +0 -171
  149. package/lib/react-qw/components/index.ts +0 -17
  150. package/lib/react-qw/index.ts +0 -96
  151. package/lib/react-qw/layout.ts +0 -111
  152. package/lib/react-qw/nmhdr.ts +0 -7
  153. package/lib/react-qw/props.ts +0 -41
  154. package/lib/react-qw/reconciler.ts +0 -383
  155. package/lib/stream.ts +0 -22
  156. package/lib/text-codec.ts +0 -171
  157. package/lib/text-measure.ts +0 -41
  158. package/lib/url.ts +0 -201
  159. package/lib/websocket.ts +0 -600
  160. package/lib/windows/comctl32.ts +0 -19
  161. package/lib/windows/comdlg32.ts +0 -10
  162. package/lib/windows/ntdll.ts +0 -11
  163. package/lib/windows/ole32.ts +0 -9
  164. package/lib/windows/shell32.ts +0 -39
  165. package/lib/windows/structs.ts +0 -752
  166. package/lib/windows/user32.ts +0 -619
  167. package/lib/windows/version.ts +0 -35
  168. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +0 -571
  169. package/vendor/mupdf-wasm/mupdf-wasm.js +0 -2749
  170. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  171. package/vendor/mupdf-wasm/mupdf.d.ts +0 -939
  172. package/vendor/mupdf-wasm/mupdf.js +0 -3317
@@ -1,383 +0,0 @@
1
- import ReactReconciler from 'react-reconciler'
2
- import { createContext } from 'react'
3
- import { DefaultEventPriority, NoEventPriority } from 'react-reconciler/constants'
4
- import * as gui from 'gui'
5
- import * as os from 'os'
6
- import { GetDC, ReleaseDC, GetWindowLongPtr, CallWindowProc, SetWindowPos, InvalidateRect, SetParent, SetWindowText } from '../windows/user32.js'
7
- import { GetDeviceCaps } from '../windows/gdi32.js'
8
- import { NULL } from '../ffi/ctype.js'
9
- import { applyProps } from './props.js'
10
- import { calculateFlexLayout, type FlexStyle } from './layout.js'
11
- import { getButtonIdealSize, measureTextForHwnd } from '../text-measure.js'
12
- import type { WIntrinsicProps } from './jsx.js'
13
- import { Gwlp, SetWindowPosFlag, SetWindowPosHwnd, ShowWindowCmd, WindowStyle, WmMsg } from '../../constants'
14
-
15
- // DEBUG 由 esbuild --define 在 bundle 时替换(见 build.ts),生产环境为 false
16
- declare const DEBUG: boolean
17
-
18
- const dpiFont = gui.CreateSystemDpiFont()
19
- // GetScaleFactor 等价内联:GetDC → GetDeviceCaps(LOGPIXELSX=88) → ReleaseDC 配对
20
- export const scaleFactor = (() => {
21
- const hdc = GetDC(NULL)
22
- const dpi = hdc ? GetDeviceCaps(hdc, 88) : 96
23
- if (hdc) ReleaseDC(NULL, hdc)
24
- return dpi / 96
25
- })()
26
-
27
- type Container = gui.HWND
28
- export type Props = WIntrinsicProps
29
-
30
- export interface Instance {
31
- hwnd: gui.HWND | null
32
- type: string
33
- props: Props
34
- children: Instance[]
35
- lastRect?: { x: number; y: number; w: number; h: number }
36
- }
37
-
38
- const DELAYED_CONTROLS = new Set(['SysListView32', 'SysTreeView32', 'LISTBOX', 'COMBOBOX', 'msctls_trackbar32', 'SysDateTimePick32', 'SysLink'])
39
-
40
- function isDelayedControl(winClass: string): boolean {
41
- return DELAYED_CONTROLS.has(winClass)
42
- }
43
-
44
- function setupWindowProc(instance: Instance, hwnd: gui.HWND) {
45
- instance.hwnd = hwnd
46
- const oldProc = GetWindowLongPtr(hwnd, Gwlp.WNDPROC)
47
- instancesByHwnd.set(hwnd, instance)
48
- gui.SetWindowProc(hwnd, (hwnd: gui.HWND, msg: number, wParam: number, lParam: number) => {
49
- const ev = instance.props.onEvent
50
- if (!ev) return CallWindowProc(oldProc, hwnd, msg, wParam, lParam)
51
- if (typeof ev === 'object') {
52
- return ev.fn({ hwnd, msg, wParam, lParam, callOldWndProc: () => CallWindowProc(oldProc, hwnd, msg, wParam, lParam) })
53
- }
54
- const result = CallWindowProc(oldProc, hwnd, msg, wParam, lParam)
55
- const override = ev({ hwnd, msg, wParam, lParam })
56
- return typeof override === 'number' && Number.isInteger(override) ? override : result
57
- })
58
- }
59
-
60
- function ensureChildWindow(child: Instance, parentHwnd: gui.HWND): gui.HWND {
61
- if (child.hwnd !== null) return child.hwnd
62
- const sty = child.props.style || {}
63
- const ws = ((child.props.ws ?? 0) | WindowStyle.CHILD) & ~WindowStyle.VISIBLE
64
- const hwnd = gui.CreateWindow(
65
- child.type, child.props.text || '', ws,
66
- 0, 0,
67
- (typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor,
68
- parentHwnd, null
69
- )!
70
- if (dpiFont) gui.SendMessage(hwnd, WmMsg.SETFONT, dpiFont, 1)
71
- setupWindowProc(child, hwnd)
72
- return hwnd
73
- }
74
-
75
- type HostContext = Record<string, never>
76
-
77
- export const instancesByHwnd = new Map<gui.HWND, Instance>()
78
-
79
- function runFlexLayout(inst: Instance) {
80
- const children = inst.children
81
- if (children.length === 0) return
82
- const s = inst.props.style
83
- const flex: FlexStyle = s || {}
84
- const visible = children.filter(c => !c.props.hidden)
85
- if (visible.length === 0) return
86
- const rect = gui.GetClientRect(inst.hwnd!)
87
- if (!rect) { console.log('flex: no rect for', inst.hwnd, inst.type); return }
88
- const pw = (rect.right - rect.left) / scaleFactor
89
- const ph = (rect.bottom - rect.top) / scaleFactor
90
- if (pw <= 0 || ph <= 0) { console.log('flex: zero size for', inst.hwnd, inst.type, pw, ph); return }
91
- const autoSizes = new Map<Instance, { w: number; h: number }>()
92
- for (const child of visible) {
93
- const sty = child.props.style || {}
94
- if (!child.hwnd) continue
95
- if (child.type === 'BUTTON' && (sty.width === 'auto' || sty.height === 'auto')) {
96
- const ideal = getButtonIdealSize(child.hwnd)
97
- autoSizes.set(child, { w: ideal.width / scaleFactor, h: ideal.height / scaleFactor })
98
- } else if (child.type === 'STATIC' && (sty.width === 'auto' || sty.height === 'auto') && child.props.text) {
99
- const measured = measureTextForHwnd(child.hwnd, child.props.text!)
100
- autoSizes.set(child, { w: measured.width / scaleFactor, h: measured.height / scaleFactor })
101
- }
102
- }
103
- const results = calculateFlexLayout(flex, pw, ph, visible.map(c => {
104
- const sty = c.props.style || {}
105
- const auto = autoSizes.get(c)
106
- return {
107
- style: {
108
- ...sty,
109
- width: auto ? (sty.width === 'auto' ? auto.w : sty.width) : sty.width,
110
- height: auto ? (sty.height === 'auto' ? auto.h : sty.height) : sty.height,
111
- }
112
- }
113
- }))
114
- const pl = flex.paddingLeft ?? flex.padding ?? 0
115
- const pt = flex.paddingTop ?? flex.padding ?? 0
116
- for (let i = 0; i < results.length; i++) {
117
- const r = results[i]!
118
- const child = visible[i]!
119
- const lr = child.lastRect
120
- if (lr && lr.x === r.x && lr.y === r.y && lr.w === r.width && lr.h === r.height) continue
121
- console.log('flex: set', child.type, child.hwnd, 'to', r.x, r.y, r.width, r.height)
122
- SetWindowPos(child.hwnd!, SetWindowPosHwnd.TOP, (r.x + pl) * scaleFactor, (r.y + pt) * scaleFactor, r.width * scaleFactor, r.height * scaleFactor, SetWindowPosFlag.SWP_SHOWWINDOW)
123
- InvalidateRect(child.hwnd!, NULL, 1)
124
- child.lastRect = { x: r.x, y: r.y, w: r.width, h: r.height }
125
- }
126
- for (const c of children) runFlexLayout(c)
127
- }
128
-
129
- export function forceFlexLayout(hwnd: gui.HWND): void {
130
- const inst = instancesByHwnd.get(hwnd)
131
- if (inst) runFlexLayout(inst)
132
- }
133
-
134
- /**
135
- * 扩展 HostConfig 类型定义
136
- *
137
- * @types/react-reconciler 的 HostConfig 存在以下问题:
138
- * 1. 缺少 rendererPackageName/rendererVersion 属性
139
- *
140
- * 解决方案:扩展补充缺失定义
141
- */
142
- type QuickWinHostConfig = ReactReconciler.HostConfig<
143
- string, Props, Container, Instance, Instance,
144
- never, never, never, gui.HWND,
145
- HostContext, never, number, -1, null
146
- > & {
147
- // rendererPackageName/rendererVersion(原类型定义中缺失)
148
- rendererPackageName: string
149
- rendererVersion: string
150
- }
151
-
152
- let currentUpdatePriority = NoEventPriority
153
-
154
- const hostConfig: QuickWinHostConfig = {
155
- // Core methods
156
- createInstance(type: string, props: Props, rootContainer: Container) {
157
- if (DEBUG) console.log('[reconciler] createInstance called:', type, 'class=' + props.type)
158
- const winClass = props.type
159
- if (isDelayedControl(winClass)) {
160
- if (DEBUG) console.log('[reconciler] delayed control, skipping CreateWindow:', winClass)
161
- return { hwnd: null, type: winClass, props, children: [] }
162
- }
163
- if (winClass === 'STATIC' && props.text !== undefined && props.children !== undefined && props.children !== null) {
164
- throw new Error('[STATIC] text and children are mutually exclusive')
165
- }
166
- const sty = props.style || {}
167
- // reconciler 创建的都是子窗口,确保 WS_CHILD 避免定位异常;创建时不带 WS_VISIBLE 防止 (0,0) 闪烁
168
- const ws = ((props.ws ?? 0) | WindowStyle.CHILD) & ~WindowStyle.VISIBLE
169
- if (DEBUG) console.log('[reconciler] CreateWindow args:', winClass, props.text ?? '', ws, 0, 0, sty.width ?? 100, sty.height ?? 30, rootContainer)
170
- const hwnd = gui.CreateWindow(
171
- winClass, props.text ?? '', ws,
172
- 0, 0,
173
- (typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor,
174
- rootContainer, null
175
- )!
176
- if (dpiFont) gui.SendMessage(hwnd, WmMsg.SETFONT, dpiFont, 1)
177
- if (DEBUG) console.log('[reconciler] createInstance hwnd result:', hwnd, 'null?', hwnd === null)
178
- const instance: Instance = { hwnd, type: winClass, props, children: [] }
179
- setupWindowProc(instance, hwnd)
180
- return instance
181
- },
182
-
183
- createTextInstance(_text: string, _rootContainer: Container) {
184
- throw new Error('should never be called (shouldSetTextContent=true)')
185
- },
186
-
187
- appendInitialChild(parent: Instance, child: Instance) {
188
- if (child.hwnd === null) {
189
- ensureChildWindow(child, parent.hwnd!)
190
- } else {
191
- SetParent(child.hwnd, parent.hwnd ?? NULL)
192
- }
193
- if (DEBUG) console.log('[reconciler] appendInitialChild parent:', parent.hwnd, 'child:', child.hwnd)
194
- parent.children.push(child)
195
- },
196
-
197
- appendChild(parent: Instance, child: Instance) {
198
- if (child.hwnd === null) {
199
- ensureChildWindow(child, parent.hwnd!)
200
- } else {
201
- SetParent(child.hwnd, parent.hwnd ?? NULL)
202
- }
203
- if (DEBUG) console.log('[reconciler] appendChild parent:', parent.hwnd, 'child:', child.hwnd)
204
- parent.children.push(child)
205
- },
206
-
207
- appendChildToContainer(container: Container, child: Instance) {
208
- if (child.hwnd === null) {
209
- ensureChildWindow(child, container)
210
- } else {
211
- SetParent(child.hwnd, container)
212
- }
213
- const rootInst = instancesByHwnd.get(container)
214
- if (rootInst) rootInst.children.push(child)
215
- if (DEBUG) console.log('[reconciler] appendChildToContainer container:', container, 'child hwnd:', child.hwnd)
216
- },
217
-
218
- insertBefore(parent: Instance, child: Instance, beforeChild: Instance) {
219
- if (child.hwnd === null) {
220
- ensureChildWindow(child, parent.hwnd!)
221
- } else {
222
- SetParent(child.hwnd, parent.hwnd ?? NULL)
223
- }
224
- if (DEBUG) console.log('[reconciler] insertBefore parent:', parent.hwnd, 'child:', child.hwnd, 'before:', beforeChild.hwnd)
225
- const idx = parent.children.indexOf(beforeChild)
226
- if (idx >= 0) parent.children.splice(idx, 0, child)
227
- },
228
-
229
- insertInContainerBefore(container: Container, child: Instance, _before: Instance) {
230
- if (child.hwnd === null) {
231
- ensureChildWindow(child, container)
232
- } else {
233
- SetParent(child.hwnd, container)
234
- }
235
- const rootInst = instancesByHwnd.get(container)
236
- if (!rootInst) throw new Error('insertInContainerBefore: no root instance for container')
237
- const idx = rootInst.children.indexOf(_before)
238
- if (idx >= 0) rootInst.children.splice(idx, 0, child)
239
- else rootInst.children.push(child)
240
- if (DEBUG) console.log('[reconciler] insertInContainerBefore container:', container, 'child hwnd:', child.hwnd)
241
- },
242
-
243
- removeChild(parent: Instance, child: Instance) {
244
- if (DEBUG) console.log('[reconciler] removeChild parent:', parent.hwnd, 'child:', child.hwnd, 'type:', child.type)
245
- if (child.hwnd !== null) gui.DestroyWindow(child.hwnd)
246
- const idx = parent.children.indexOf(child)
247
- if (idx >= 0) parent.children.splice(idx, 1)
248
- },
249
-
250
- removeChildFromContainer(container: Container, child: Instance) {
251
- if (child.hwnd === null) return
252
- if (DEBUG) console.log('[reconciler] removeChildFromContainer container:', container, 'child hwnd:', child.hwnd)
253
- gui.DestroyWindow(child.hwnd)
254
- const rootInst = instancesByHwnd.get(container)
255
- if (rootInst) {
256
- const idx = rootInst.children.indexOf(child)
257
- if (idx >= 0) rootInst.children.splice(idx, 1)
258
- }
259
- },
260
-
261
- commitUpdate(instance: Instance, _type: string, oldProps: Props, newProps: Props, _internalHandle: unknown) {
262
- applyProps(instance, newProps, oldProps)
263
- },
264
-
265
- commitMount(_instance: Instance, _type: string, _props: Props, _internal: unknown) { },
266
-
267
- finalizeInitialChildren(instance: Instance, type: string, props: Props) {
268
- if (DEBUG) console.log('[reconciler] finalizeInitialChildren instance:', instance.hwnd, 'props:', JSON.stringify(props))
269
- applyProps(instance, props, { type })
270
- return false
271
- },
272
-
273
- resetAfterCommit(containerInfo: Container) {
274
- if (DEBUG) console.log('[reconciler] resetAfterCommit')
275
- const rootInst = instancesByHwnd.get(containerInfo)
276
- if (!rootInst) throw new Error('resetAfterCommit: no root instance for container')
277
- runFlexLayout(rootInst)
278
- },
279
-
280
- resetTextContent(_instance: Instance) { },
281
-
282
- commitTextUpdate(instance: Instance, _oldText: string, newText: string) {
283
- SetWindowText(instance.hwnd!, newText)
284
- },
285
-
286
- hideInstance(instance: Instance) {
287
- if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.HIDE)
288
- },
289
-
290
- hideTextInstance(instance: Instance) {
291
- if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.HIDE)
292
- },
293
-
294
- unhideInstance(instance: Instance, _props: Props) {
295
- if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.SHOW)
296
- },
297
-
298
- unhideTextInstance(instance: Instance, _text: string) {
299
- if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.SHOW)
300
- },
301
-
302
- shouldSetTextContent(_type: string, props: Props) {
303
- return typeof props.children === 'string' || typeof props.children === 'number'
304
- },
305
-
306
- getPublicInstance(instance: Instance) {
307
- return instance.hwnd!
308
- },
309
-
310
- getRootHostContext(_rootContainer: Container) {
311
- return {}
312
- },
313
-
314
- getChildHostContext(_parentContext: HostContext, _type: string) {
315
- return {}
316
- },
317
-
318
- prepareForCommit(_containerInfo: Container) {
319
- return null
320
- },
321
-
322
- preparePortalMount(_containerInfo: Container) { },
323
-
324
- clearContainer(_container: Container) {
325
- // 不需要清理,React reconciler 会通过 removeChildFromContainer 来移除子节点
326
- },
327
-
328
- scheduleTimeout(fn: () => void, delay: number) {
329
- return os.setTimeout(fn, delay)
330
- },
331
-
332
- cancelTimeout(id: os.TimerId) {
333
- os.clearTimeout?.(id)
334
- },
335
-
336
- noTimeout: -1,
337
-
338
- supportsMutation: true,
339
- supportsPersistence: false,
340
- supportsHydration: false,
341
- isPrimaryRenderer: true,
342
- supportsMicrotasks: false,
343
-
344
- setCurrentUpdatePriority(p: number) { currentUpdatePriority = p },
345
- getCurrentUpdatePriority() { return currentUpdatePriority },
346
- resolveUpdatePriority() { return DefaultEventPriority },
347
-
348
- resolveEventType() { return null },
349
- resolveEventTimeStamp() { return Date.now() },
350
- trackSchedulerEvent() { },
351
-
352
- shouldAttemptEagerTransition() { return false },
353
- detachDeletedInstance(instance: Instance) {
354
- if (DEBUG) console.log('[reconciler] detachDeletedInstance hwnd:', instance.hwnd, 'type:', instance.type)
355
- if (instance.hwnd !== null) instancesByHwnd.delete(instance.hwnd)
356
- },
357
-
358
- // 添加必需方法
359
- getInstanceFromNode(_node: unknown) { return null },
360
- beforeActiveInstanceBlur() { },
361
- afterActiveInstanceBlur() { },
362
- prepareScopeUpdate(_scopeInstance: unknown, _instance: unknown) { },
363
- getInstanceFromScope(_scopeInstance: unknown) { return null },
364
-
365
- // Suspense/Concurrent Mode 支持
366
- maySuspendCommit() { return false },
367
- requestPostPaintCallback() { },
368
- preloadInstance() { return true },
369
- startSuspendingCommit() { },
370
- suspendInstance() { },
371
- waitForCommitToBeReady() { return null },
372
-
373
- rendererVersion: '0.1.0',
374
- rendererPackageName: 'react-qw',
375
- NotPendingTransition: null,
376
- // @types/react.Context<T> 与 @types/react-reconciler.ReactContext<T> 是同一运行时对象
377
- // 的两个不兼容类型定义(ReactContext 多 _currentValue 等字段),此 cast 不可避免
378
- HostTransitionContext: createContext(null) as unknown as ReactReconciler.ReactContext<null>,
379
- resetFormInstance(_form: never) { },
380
- }
381
-
382
- const reconciler = ReactReconciler(hostConfig)
383
- export default reconciler
package/lib/stream.ts DELETED
@@ -1,22 +0,0 @@
1
- export {}
2
-
3
- import {
4
- ReadableStream as ReadableStreamImpl,
5
- ReadableStreamDefaultController as ReadableStreamDefaultControllerImpl,
6
- ReadableStreamDefaultReader as ReadableStreamDefaultReaderImpl,
7
- } from './vendor/web-streams/ponyfill.mjs'
8
-
9
- declare global {
10
- type ReadableStreamReadResult<R = any> =
11
- | { done: false; value: R }
12
- | { done: true; value?: undefined }
13
-
14
- interface ReadableStreamReader<R = any> extends ReadableStreamDefaultReaderImpl<R> {}
15
- interface ReadableStreamDefaultReader<R = any> extends ReadableStreamDefaultReaderImpl<R> {}
16
- interface ReadableStream<R = any> extends ReadableStreamImpl<R> {}
17
- interface ReadableStreamDefaultController<R = any> extends ReadableStreamDefaultControllerImpl<R> {}
18
-
19
- var ReadableStream: typeof ReadableStreamImpl
20
- }
21
-
22
- globalThis.ReadableStream = ReadableStreamImpl
package/lib/text-codec.ts DELETED
@@ -1,171 +0,0 @@
1
- export {} // make this a module so declare global works
2
-
3
- declare global {
4
- interface TextDecoder<T extends _Label | undefined = _Label | undefined> extends TextDecoderImpl<T> {}
5
- var TextDecoder: typeof TextDecoderImpl
6
-
7
- interface TextEncoder<T extends _Label | undefined = _Label | undefined> extends TextEncoderImpl<T> {}
8
- var TextEncoder: typeof TextEncoderImpl
9
- }
10
-
11
- const _labelMap = {
12
- 'utf-8': 'utf-8',
13
- 'utf8': 'utf-8',
14
- 'unicode-1-1-utf-8': 'utf-8',
15
- 'utf-16': 'utf-16le',
16
- 'utf-16le': 'utf-16le',
17
- 'utf-16be': 'utf-16be',
18
- } as const
19
-
20
- type _Label = keyof typeof _labelMap
21
- type _Encoding = typeof _labelMap[_Label]
22
- type _Result<T> = T extends _Label ? typeof _labelMap[T] : 'utf-8'
23
-
24
- function _normalizeLabel<T extends _Label | undefined>(label: T): _Result<T> {
25
- if (!label) return 'utf-8' as _Result<T>
26
- const result = _labelMap[label.trim().toLowerCase() as _Label]
27
- if (result) return result as _Result<T>
28
- throw new RangeError(`The encoding label "${label}" is not supported`)
29
- }
30
-
31
- function _decodeUTF8(buf: Uint8Array, fatal: boolean): string {
32
- let out = ''
33
- for (let i = 0; i < buf.length; ) {
34
- const b = buf[i++]!
35
- if (b < 0x80) { out += String.fromCharCode(b); continue }
36
- if (b < 0xC0) { out += fatal ? (() => { throw new TypeError('unexpected continuation byte') })() : '\uFFFD'; continue }
37
- let c: number, min: number
38
- if (b < 0xE0) {
39
- if (i >= buf.length) { out += fatal ? (() => { throw new TypeError('incomplete utf-8') })() : '\uFFFD'; break }
40
- const b2 = buf[i++]!
41
- if ((b2 & 0xC0) !== 0x80) { out += '\uFFFD'; continue }
42
- c = ((b & 0x1F) << 6) | (b2 & 0x3F)
43
- min = 0x80
44
- } else if (b < 0xF0) {
45
- if (i + 1 >= buf.length) { out += fatal ? (() => { throw new TypeError('incomplete utf-8') })() : '\uFFFD'; break }
46
- const b2 = buf[i++]!; const b3 = buf[i++]!
47
- if (((b2 & 0xC0) !== 0x80) || ((b3 & 0xC0) !== 0x80)) { out += '\uFFFD'; continue }
48
- c = ((b & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)
49
- min = 0x800
50
- } else if (b < 0xF8) {
51
- if (i + 2 >= buf.length) { out += fatal ? (() => { throw new TypeError('incomplete utf-8') })() : '\uFFFD'; break }
52
- const b2 = buf[i++]!; const b3 = buf[i++]!; const b4 = buf[i++]!
53
- if (((b2 & 0xC0) !== 0x80) || ((b3 & 0xC0) !== 0x80) || ((b4 & 0xC0) !== 0x80)) { out += '\uFFFD'; continue }
54
- c = ((b & 0x07) << 18) | ((b2 & 0x3F) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F)
55
- min = 0x10000
56
- } else { out += fatal ? (() => { throw new TypeError('invalid utf-8 start') })() : '\uFFFD'; continue }
57
- if (c < min || (c >= 0xD800 && c <= 0xDFFF) || c > 0x10FFFF) {
58
- out += fatal ? (() => { throw new TypeError('invalid utf-8 codepoint') })() : '\uFFFD'; continue
59
- }
60
- if (c <= 0xFFFF) out += String.fromCharCode(c)
61
- else { c -= 0x10000; out += String.fromCharCode(0xD800 | (c >> 10), 0xDC00 | (c & 0x3FF)) }
62
- }
63
- return out
64
- }
65
-
66
- function _decodeUTF16(buf: Uint8Array, le: boolean, fatal: boolean): string {
67
- const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
68
- let out = ''
69
- for (let i = 0; i + 1 < buf.length; i += 2) {
70
- let c = dv.getUint16(i, le)
71
- if (c >= 0xD800 && c <= 0xDBFF) {
72
- i += 2
73
- if (i + 1 >= buf.length) {
74
- out += fatal ? (() => { throw new TypeError('truncated surrogate pair') })() : '\uFFFD'; break
75
- }
76
- const c2 = dv.getUint16(i, le)
77
- if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
78
- out += String.fromCharCode(c, c2)
79
- } else {
80
- out += fatal ? (() => { throw new TypeError('invalid surrogate pair') })() : '\uFFFD'
81
- i -= 2
82
- }
83
- } else if (c >= 0xDC00 && c <= 0xDFFF) {
84
- out += fatal ? (() => { throw new TypeError('unexpected trailing surrogate') })() : '\uFFFD'
85
- } else {
86
- out += String.fromCharCode(c)
87
- }
88
- }
89
- return out
90
- }
91
-
92
- class TextDecoderImpl<T extends _Label | undefined = _Label | undefined> {
93
- _encoding: _Encoding
94
- _fatal: boolean
95
- _ignoreBOM: boolean
96
-
97
- constructor(label?: T, options?: { fatal?: boolean, ignoreBOM?: boolean }) {
98
- this._encoding = _normalizeLabel(label)
99
- this._fatal = options?.fatal ?? false
100
- this._ignoreBOM = options?.ignoreBOM ?? false
101
- }
102
-
103
- get encoding(): _Result<T> { return this._encoding as _Result<T> }
104
- get fatal(): boolean { return this._fatal }
105
- get ignoreBOM(): boolean { return this._ignoreBOM }
106
-
107
- decode(buffer?: ArrayBufferView | ArrayBuffer | null): string {
108
- if (!buffer) return ''
109
- const arr = buffer instanceof Uint8Array ? buffer
110
- : buffer instanceof ArrayBuffer ? new Uint8Array(buffer)
111
- : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)
112
-
113
- if (this._encoding === 'utf-8') {
114
- return _decodeUTF8(arr, this._fatal)
115
- }
116
-
117
- const le = this._encoding === 'utf-16le'
118
- let offset = 0
119
-
120
- if (arr.length >= 2 && !this._ignoreBOM) {
121
- const bomLo = le ? arr[0]! : arr[1]!
122
- const bomHi = le ? arr[1]! : arr[0]!
123
- const bom = (bomHi << 8) | bomLo
124
- if (bom === 0xFEFF || bom === 0xFFFE) {
125
- offset = 2
126
- }
127
- }
128
-
129
- return _decodeUTF16(arr.slice(offset), le, this._fatal)
130
- }
131
- }
132
-
133
- class TextEncoderImpl<T extends _Label | undefined = _Label | undefined> {
134
- _encoding: _Encoding
135
-
136
- constructor(label?: T) {
137
- this._encoding = _normalizeLabel(label)
138
- }
139
-
140
- get encoding(): _Result<T> { return this._encoding as _Result<T> }
141
-
142
- encode(input?: string): Uint8Array<ArrayBuffer> {
143
- if (!input) return new Uint8Array(0)
144
-
145
- if (this._encoding === 'utf-8') {
146
- const bytes: number[] = []
147
- for (let i = 0; i < input.length; i++) {
148
- let c = input.charCodeAt(i)
149
- if (c < 0x80) { bytes.push(c) }
150
- else if (c < 0x800) { bytes.push(0xC0 | (c >> 6), 0x80 | (c & 0x3F)) }
151
- else if (c < 0xD800 || c >= 0xE000) { bytes.push(0xE0 | (c >> 12), 0x80 | ((c >> 6) & 0x3F), 0x80 | (c & 0x3F)) }
152
- else { i++; const c2 = input.charCodeAt(i); c = 0x10000 + ((c & 0x3FF) << 10) | (c2 & 0x3FF); bytes.push(0xF0 | (c >> 18), 0x80 | ((c >> 12) & 0x3F), 0x80 | ((c >> 6) & 0x3F), 0x80 | (c & 0x3F)) }
153
- }
154
- return new Uint8Array(bytes)
155
- }
156
-
157
- const le = this._encoding === 'utf-16le'
158
- const buf = new ArrayBuffer(input.length * 2)
159
- const dv = new DataView(buf)
160
- for (let i = 0; i < input.length; i++) dv.setUint16(i * 2, input.charCodeAt(i), le)
161
- return new Uint8Array(buf)
162
- }
163
- }
164
-
165
- if (typeof globalThis.TextDecoder === 'undefined') {
166
- globalThis.TextDecoder = TextDecoderImpl
167
- }
168
-
169
- if (typeof globalThis.TextEncoder === 'undefined') {
170
- globalThis.TextEncoder = TextEncoderImpl
171
- }
@@ -1,41 +0,0 @@
1
- import { NULL, type Ptr } from './ffi/ctype.js'
2
- import * as gui from 'gui'
3
- import { DrawText, GetDC, ReleaseDC } from './windows/user32.js'
4
- import { SelectObject } from './windows/gdi32.js'
5
- import { RECT, SIZE } from './windows/structs.js'
6
- import { ButtonExtMsg, DrawTextFlag, WmMsg } from '../constants'
7
-
8
- export function measureText(hdc: Ptr<'HDC'>, text: string, maxWidth: number): { width: number; height: number } {
9
- // rect 双向位不注册 encoder(CALCRECT 就地写回):encode(初值) 新建 + DeepPartial 初值一步到位
10
- //(left/top/bottom 缺省跳过保持 0)→ 传 .ptr → decode 读回
11
- const r = RECT.encode({ right: maxWidth })
12
- DrawText(hdc, text, -1, r.ptr, DrawTextFlag.CALCRECT)
13
- const { right, bottom } = RECT.decode(r)
14
- return { width: right, height: bottom }
15
- }
16
-
17
- export function getButtonIdealSize(hwnd: gui.HWND): { width: number; height: number } {
18
- // SIZE 纯出参位(GETIDEALSIZE 就地写回):alloc → 传 .ptr → decode 读回
19
- const size = SIZE.alloc()
20
- gui.SendMessage(hwnd, ButtonExtMsg.GETIDEALSIZE, 0, size.ptr)
21
- const { cx, cy } = SIZE.decode(size)
22
- return { width: cx, height: cy }
23
- }
24
-
25
- export function measureTextForHwnd(hwnd: gui.HWND, text: string): { width: number; height: number } {
26
- const hdc = GetDC(hwnd)
27
- if (!hdc) return { width: 0, height: 0 }
28
- // SendMessage 返回 LRESULT 裸数;GETFONT 语义为 HFONT——断言进 GDI 对象品牌链
29
- const hFont = gui.SendMessage(hwnd, WmMsg.GETFONT, 0, 0) as Ptr<'HGDIOBJ'>
30
- const oldFont = hFont ? SelectObject(hdc, hFont) : NULL
31
- const result = measureText(hdc, text, 0)
32
- if (hFont) SelectObject(hdc, oldFont)
33
- ReleaseDC(hwnd, hdc)
34
- const wr = gui.GetWindowRect(hwnd)
35
- const cr = gui.GetClientRect(hwnd)
36
- if (wr && cr) {
37
- result.width += (wr.right - wr.left) - (cr.right - cr.left)
38
- result.height += (wr.bottom - wr.top) - (cr.bottom - cr.top)
39
- }
40
- return result
41
- }