quickwin 2026.5.2-3.145209

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 (83) hide show
  1. package/README.md +6 -0
  2. package/examples/pdf_preview.js +440 -0
  3. package/examples/pdf_preview.ts +470 -0
  4. package/examples/preact_demo.js +35 -0
  5. package/examples/preact_demo.tsx +49 -0
  6. package/examples/tray_demo.js +75 -0
  7. package/examples/tray_demo.tsx +79 -0
  8. package/lib/fetch.js +746 -0
  9. package/lib/fetch.ts +811 -0
  10. package/lib/polyfill.js +500 -0
  11. package/lib/polyfill.ts +454 -0
  12. package/lib/preact/hooks.js +287 -0
  13. package/lib/preact/hooks.ts +330 -0
  14. package/lib/preact/jsx-runtime.js +1 -0
  15. package/lib/preact/jsx-runtime.ts +2 -0
  16. package/lib/preact/jsx.d.ts +36 -0
  17. package/lib/preact/layout.js +153 -0
  18. package/lib/preact/layout.ts +183 -0
  19. package/lib/preact/preact.js +54 -0
  20. package/lib/preact/preact.ts +133 -0
  21. package/lib/preact/props.js +99 -0
  22. package/lib/preact/props.ts +119 -0
  23. package/lib/preact/render.js +320 -0
  24. package/lib/preact/render.ts +353 -0
  25. package/lib/websocket.js +540 -0
  26. package/lib/websocket.ts +574 -0
  27. package/package.json +32 -0
  28. package/quickwin.d.ts +657 -0
  29. package/test/add.wasm +0 -0
  30. package/test/complex.wasm +0 -0
  31. package/test/complex_imports.wasm +0 -0
  32. package/test/global_imports.wasm +0 -0
  33. package/test/import_func.wasm +0 -0
  34. package/test/imports.wasm +0 -0
  35. package/test/run.js +86 -0
  36. package/test/run.ts +90 -0
  37. package/test/sjlj.wasm +0 -0
  38. package/test/test_basic.js +7 -0
  39. package/test/test_basic.ts +9 -0
  40. package/test/test_brotli.js +48 -0
  41. package/test/test_brotli.ts +52 -0
  42. package/test/test_fetch_cache.js +131 -0
  43. package/test/test_fetch_cache.ts +141 -0
  44. package/test/test_ffi.js +157 -0
  45. package/test/test_ffi.ts +174 -0
  46. package/test/test_frame_encoding.js +128 -0
  47. package/test/test_frame_encoding.ts +132 -0
  48. package/test/test_helper.js +84 -0
  49. package/test/test_helper.ts +80 -0
  50. package/test/test_http_import.js +78 -0
  51. package/test/test_http_import.ts +74 -0
  52. package/test/test_mupdf_render.js +69 -0
  53. package/test/test_mupdf_render.ts +74 -0
  54. package/test/test_mupdf_twice.js +77 -0
  55. package/test/test_mupdf_twice.ts +81 -0
  56. package/test/test_mupdf_wasm.js +33 -0
  57. package/test/test_mupdf_wasm.ts +30 -0
  58. package/test/test_net_event.js +63 -0
  59. package/test/test_net_event.ts +59 -0
  60. package/test/test_net_fetch.js +153 -0
  61. package/test/test_net_fetch.ts +131 -0
  62. package/test/test_net_websocket.js +158 -0
  63. package/test/test_net_websocket.ts +144 -0
  64. package/test/test_polyfill.js +58 -0
  65. package/test/test_polyfill.ts +60 -0
  66. package/test/test_url.js +173 -0
  67. package/test/test_url.ts +183 -0
  68. package/test/test_wasm_basic.js +82 -0
  69. package/test/test_wasm_basic.ts +70 -0
  70. package/test/test_wasm_import_global.js +41 -0
  71. package/test/test_wasm_import_global.ts +39 -0
  72. package/test/test_wasm_sjlj.js +153 -0
  73. package/test/test_wasm_sjlj.ts +134 -0
  74. package/test/test_wasm_types.js +96 -0
  75. package/test/test_wasm_types.ts +108 -0
  76. package/test/types.wasm +0 -0
  77. package/tsconfig.json +18 -0
  78. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
  79. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
  80. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  81. package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
  82. package/vendor/mupdf-wasm/mupdf.js +3317 -0
  83. package/win-mingw64.exe +0 -0
@@ -0,0 +1,353 @@
1
+ import '../polyfill.js'
2
+ import * as gui from 'gui'
3
+ import * as os from 'os'
4
+ import { applyProps, destroyWindow } from './props.js'
5
+ import { layout as doLayout } from './layout.js'
6
+ import { options, type VNode as PreactVNode, type ComponentChild } from './preact.js'
7
+
8
+ const scaleFactor = gui.GetScaleFactor()
9
+ const dpiFont = gui.CreateSystemDpiFont()
10
+
11
+ const HWND_PROP = '__qw_hwnd'
12
+ const STYLE_PROP = '__qw_style'
13
+ const CHILDREN_HWNDS_PROP = '__qw_children'
14
+ const RENDERED_VNODE_PROP = '__qw_rendered'
15
+
16
+ interface QWComponent {
17
+ _vnode: QWVNode | null
18
+ props: any
19
+ context: any
20
+ __hooks: { _list: any[]; _pendingEffects: any[] } | null
21
+ _renderCallbacks: Array<() => void>
22
+ _parentDom: unknown
23
+ _qw_parent_hwnd: gui.HWND
24
+ _dirty: boolean
25
+ _forceUpdate(): void
26
+ }
27
+
28
+ interface QWVNode extends PreactVNode {
29
+ [key: string]: unknown
30
+ }
31
+
32
+ let rootHwnd: gui.HWND | null = null
33
+ let rootVNode: QWVNode | null = null
34
+
35
+ const WIN32_CLASS: Record<string, string> = {
36
+ button: 'BUTTON', edit: 'EDIT', static: 'STATIC',
37
+ checkbox: 'BUTTON', groupbox: 'BUTTON', combobox: 'COMBOBOX',
38
+ listbox: 'LISTBOX', progressbar: 'msctls_progress32',
39
+ }
40
+
41
+ const WIN32_STYLE: Record<string, number> = {
42
+ button: gui.ButtonStyle.PUSHBUTTON,
43
+ edit: gui.WindowStyle.BORDER | gui.EditStyle.AUTOHSCROLL,
44
+ static: gui.StaticStyle.LEFT,
45
+ checkbox: gui.ButtonStyle.AUTOCHECKBOX,
46
+ groupbox: gui.ButtonStyle.GROUPBOX,
47
+ combobox: gui.ComboBoxStyle.DROPDOWNLIST,
48
+ listbox: gui.ListBoxStyle.NOTIFY | gui.WindowStyle.VSCROLL,
49
+ progressbar: 0,
50
+ }
51
+
52
+ function createControl(type: string, parentHwnd: gui.HWND, vnode: QWVNode): gui.HWND {
53
+ const base = gui.WindowStyle.CHILD | gui.WindowStyle.VISIBLE
54
+ const winClass = WIN32_CLASS[type] || 'STATIC'
55
+ const style = base | (WIN32_STYLE[type] ?? gui.StaticStyle.LEFT)
56
+ const text = (vnode.props?.text || vnode.props?.value || '') as string
57
+ const hwnd = gui.CreateWindow(winClass, text, style, 0, 0, 0, 0, parentHwnd, null)
58
+ if (!hwnd) return 0 as gui.HWND
59
+ gui.SendMessage(hwnd, gui.WmMsg.SETFONT, dpiFont as unknown as number, 1)
60
+ applyProps(hwnd, vnode.props || {}, vnode)
61
+ return hwnd
62
+ }
63
+
64
+ function isVNode(val: unknown): val is QWVNode {
65
+ return val != null && typeof val === 'object' && (val as any).constructor === undefined && (val as any).type !== undefined
66
+ }
67
+
68
+ function getChildren(vnode: QWVNode): unknown[] {
69
+ const children = vnode.props?.children
70
+ if (children == null) return []
71
+ if (Array.isArray(children)) return children.filter((c: unknown) => c != null && c !== false && c !== true)
72
+ if (typeof children === 'object' && (children as any).type !== undefined) return [children]
73
+ return []
74
+ }
75
+
76
+ function renderToWin32(vnode: unknown, parentHwnd: gui.HWND, context: any): gui.HWND {
77
+ if (vnode == null || vnode === false || vnode === true) return 0 as gui.HWND
78
+
79
+ if (typeof vnode === 'string' || typeof vnode === 'number') {
80
+ const hwnd = gui.CreateWindow('STATIC', String(vnode), gui.WindowStyle.CHILD | gui.WindowStyle.VISIBLE | gui.StaticStyle.LEFT, 0, 0, 0, 0, parentHwnd, null)
81
+ if (hwnd) gui.SendMessage(hwnd, gui.WmMsg.SETFONT, dpiFont as unknown as number, 1)
82
+ return hwnd
83
+ }
84
+
85
+ if (Array.isArray(vnode)) {
86
+ for (const child of vnode) renderToWin32(child, parentHwnd, context)
87
+ return 0 as gui.HWND
88
+ }
89
+
90
+ if (!isVNode(vnode)) return 0 as gui.HWND
91
+
92
+ if (typeof vnode.type === 'function') {
93
+ return renderComponent(vnode, parentHwnd, context)
94
+ }
95
+
96
+ if (vnode.type === 'w') {
97
+ const ctrlType = (vnode.props?.type as string) || ''
98
+ let hwnd: gui.HWND
99
+ if (ctrlType) {
100
+ hwnd = createControl(ctrlType, parentHwnd, vnode)
101
+ } else {
102
+ hwnd = gui.CreateWindow('STATIC', '', gui.WindowStyle.CHILD | gui.WindowStyle.VISIBLE, 0, 0, 0, 0, parentHwnd, null)
103
+ if (hwnd) {
104
+ gui.SendMessage(hwnd, gui.WmMsg.SETFONT, dpiFont as unknown as number, 1)
105
+ applyProps(hwnd, vnode.props || {}, vnode)
106
+ }
107
+ }
108
+ if (!hwnd) return 0 as gui.HWND
109
+
110
+ vnode[HWND_PROP] = hwnd
111
+ vnode[STYLE_PROP] = vnode.props?.style ?? {}
112
+
113
+ const children = getChildren(vnode)
114
+ const childHwnds: number[] = []
115
+ for (const child of children) {
116
+ const childHwnd = renderToWin32(child, hwnd, context)
117
+ if (childHwnd) childHwnds.push(childHwnd as unknown as number)
118
+ }
119
+ vnode[CHILDREN_HWNDS_PROP] = childHwnds
120
+ return hwnd
121
+ }
122
+
123
+ return 0 as gui.HWND
124
+ }
125
+
126
+ function invokeComponent(component: QWComponent, vnode: QWVNode): ComponentChild {
127
+ options._diff?.(vnode)
128
+ options._render?.(vnode)
129
+ try {
130
+ const fn = vnode.type as (...args: any[]) => any
131
+ return fn.call(component, component.props, component.context)
132
+ } catch (e: unknown) {
133
+ const errHandler = (component as any)._errorHandler
134
+ if (errHandler) {
135
+ errHandler(e)
136
+ return null
137
+ }
138
+ console.log('[preact-render] render threw: ' + e)
139
+ return null
140
+ } finally {
141
+ options.diffed?.(vnode)
142
+ }
143
+ }
144
+
145
+ function renderComponent(vnode: QWVNode, parentHwnd: gui.HWND, context: any): gui.HWND {
146
+ const rendered = invokeComponent(newComponent(vnode, parentHwnd, context), vnode)
147
+ if (rendered == null) return 0 as gui.HWND
148
+ const resultHwnd = renderToWin32(rendered, parentHwnd, context)
149
+ vnode[HWND_PROP] = resultHwnd
150
+ vnode[RENDERED_VNODE_PROP] = rendered
151
+ return resultHwnd
152
+ }
153
+
154
+ function newComponent(vnode: QWVNode, parentHwnd: gui.HWND, context: any): QWComponent {
155
+ const component: QWComponent = {
156
+ _vnode: vnode,
157
+ props: vnode.props,
158
+ context,
159
+ __hooks: { _list: [], _pendingEffects: [] },
160
+ _renderCallbacks: [],
161
+ _parentDom: true,
162
+ _qw_parent_hwnd: parentHwnd,
163
+ _dirty: false,
164
+ _forceUpdate() {
165
+ component._dirty = true
166
+ scheduleUpdate(component)
167
+ },
168
+ }
169
+ vnode._component = component as any
170
+ return component
171
+ }
172
+
173
+ function destroyHwnd(hwnd: number): void {
174
+ if (!hwnd) return
175
+ gui.RemoveWindow(hwnd as gui.HWND)
176
+ destroyWindow(hwnd)
177
+ }
178
+
179
+ function destroyVNode(vnode: QWVNode): void {
180
+ options.unmount?.(vnode)
181
+
182
+ const hwnd = vnode[HWND_PROP] as number | undefined
183
+ if (hwnd) destroyHwnd(hwnd)
184
+ vnode[HWND_PROP] = 0
185
+
186
+ for (const child of getChildren(vnode)) {
187
+ if (isVNode(child)) destroyVNode(child)
188
+ }
189
+
190
+ const extra = vnode[CHILDREN_HWNDS_PROP] as number[] | undefined
191
+ if (extra) {
192
+ for (const h of extra) destroyHwnd(h)
193
+ }
194
+ vnode[CHILDREN_HWNDS_PROP] = []
195
+ }
196
+
197
+ function reconcile(
198
+ oldVNode: unknown,
199
+ newVNode: unknown,
200
+ parentHwnd: gui.HWND,
201
+ context: any
202
+ ): gui.HWND {
203
+ if (oldVNode == null || oldVNode === false || oldVNode === true) {
204
+ return renderToWin32(newVNode, parentHwnd, context)
205
+ }
206
+ if (newVNode == null || newVNode === false || newVNode === true) {
207
+ if (isVNode(oldVNode)) destroyVNode(oldVNode as QWVNode)
208
+ return 0 as gui.HWND
209
+ }
210
+ if (typeof oldVNode !== typeof newVNode ||
211
+ typeof oldVNode === 'string' || typeof oldVNode === 'number' ||
212
+ Array.isArray(oldVNode) || Array.isArray(newVNode)) {
213
+ if (isVNode(oldVNode)) destroyVNode(oldVNode as QWVNode)
214
+ return renderToWin32(newVNode, parentHwnd, context)
215
+ }
216
+ if (!isVNode(oldVNode) || !isVNode(newVNode)) {
217
+ return renderToWin32(newVNode, parentHwnd, context)
218
+ }
219
+
220
+ const o = oldVNode as QWVNode
221
+ const n = newVNode as QWVNode
222
+
223
+ if (typeof n.type === 'function') {
224
+ const oldComp = o._component as QWComponent | null
225
+ let comp: QWComponent
226
+ if (oldComp) {
227
+ oldComp._vnode = n
228
+ oldComp.props = n.props
229
+ oldComp._qw_parent_hwnd = parentHwnd
230
+ comp = oldComp
231
+ } else {
232
+ comp = newComponent(n, parentHwnd, context)
233
+ }
234
+ n._component = comp as any
235
+
236
+ const oldResult = o[RENDERED_VNODE_PROP]
237
+ const newResult = invokeComponent(comp, n)
238
+
239
+ let resultHwnd: gui.HWND
240
+ if (newResult != null) {
241
+ resultHwnd = reconcile(oldResult || null, newResult, parentHwnd, context)
242
+ } else {
243
+ if (oldResult && isVNode(oldResult)) destroyVNode(oldResult as QWVNode)
244
+ resultHwnd = 0 as gui.HWND
245
+ }
246
+ n[HWND_PROP] = resultHwnd
247
+ n[RENDERED_VNODE_PROP] = newResult
248
+ return resultHwnd
249
+ }
250
+
251
+ if (o.type === 'w' && n.type === 'w') {
252
+ const oldCtrl = (o.props?.type as string) || ''
253
+ const newCtrl = (n.props?.type as string) || ''
254
+ if (oldCtrl === newCtrl) {
255
+ const hwnd = o[HWND_PROP] as gui.HWND | undefined
256
+ if (hwnd) {
257
+ n[HWND_PROP] = hwnd
258
+ n[STYLE_PROP] = n.props?.style ?? {}
259
+ n._oldProc = o._oldProc
260
+ applyProps(hwnd, n.props || {}, n)
261
+
262
+ const oldChildren = getChildren(o)
263
+ const newChildren = getChildren(n)
264
+ const oldChildHwnds = o[CHILDREN_HWNDS_PROP] as number[] | undefined
265
+ const childHwnds: number[] = []
266
+ const maxLen = Math.max(oldChildren.length, newChildren.length)
267
+ for (let i = 0; i < maxLen; i++) {
268
+ const oc = oldChildren[i]
269
+ const nc = newChildren[i]
270
+ if (oc != null && oc !== false && oc !== true) {
271
+ if (nc != null && nc !== false && nc !== true) {
272
+ if (typeof oc === 'string' && typeof nc === 'string') {
273
+ if (oc !== nc && oldChildHwnds && oldChildHwnds[i]) {
274
+ applyProps(oldChildHwnds[i] as gui.HWND, { text: nc })
275
+ }
276
+ if (oldChildHwnds && oldChildHwnds[i])
277
+ childHwnds.push(oldChildHwnds[i])
278
+ } else {
279
+ const ch = reconcile(oc, nc, hwnd, context)
280
+ if (ch) childHwnds.push(ch as unknown as number)
281
+ }
282
+ } else {
283
+ if (isVNode(oc)) destroyVNode(oc)
284
+ else if (oldChildHwnds && oldChildHwnds[i])
285
+ destroyHwnd(oldChildHwnds[i])
286
+ }
287
+ } else if (nc != null && nc !== false && nc !== true) {
288
+ const ch = renderToWin32(nc, hwnd, context)
289
+ if (ch) childHwnds.push(ch as unknown as number)
290
+ }
291
+ }
292
+ n[CHILDREN_HWNDS_PROP] = childHwnds
293
+ return hwnd
294
+ }
295
+ }
296
+ }
297
+
298
+ destroyVNode(o)
299
+ return renderToWin32(n, parentHwnd, context)
300
+ }
301
+
302
+ function scheduleUpdate(component: QWComponent): void {
303
+ if (!component._dirty) return
304
+ os.setTimeout(() => {
305
+ if (!component._dirty) return
306
+ component._dirty = false
307
+
308
+ const vnode = component._vnode as QWVNode
309
+ const parentHwnd = component._qw_parent_hwnd
310
+ if (!parentHwnd) return
311
+
312
+ const oldRendered = vnode[RENDERED_VNODE_PROP]
313
+ const rendered = invokeComponent(component, vnode)
314
+
315
+ if (rendered != null) {
316
+ reconcile(oldRendered || null, rendered, parentHwnd, component.context)
317
+ } else if (oldRendered) {
318
+ if (isVNode(oldRendered)) destroyVNode(oldRendered as QWVNode)
319
+ }
320
+ vnode[RENDERED_VNODE_PROP] = rendered
321
+
322
+ const commitQueue = [component]
323
+ options._commit?.(vnode, commitQueue as any)
324
+
325
+ if (rootHwnd && rootVNode) {
326
+ doLayout(rootHwnd as unknown as number, rootVNode)
327
+ }
328
+ }, 0)
329
+ }
330
+
331
+ export function notifyResize(hwnd: gui.HWND): void {
332
+ if (rootVNode) doLayout(hwnd as unknown as number, rootVNode)
333
+ }
334
+
335
+ export function render(vnode: any, containerHwnd: gui.HWND): gui.HWND {
336
+ rootHwnd = containerHwnd
337
+ rootVNode = vnode
338
+
339
+ renderToWin32(vnode, containerHwnd, {})
340
+
341
+ if (vnode._component) {
342
+ const commitQueue = [vnode._component]
343
+ options._commit?.(vnode, commitQueue as any)
344
+ }
345
+
346
+ doLayout(containerHwnd as unknown as number, vnode)
347
+ return containerHwnd
348
+ }
349
+
350
+ export { HWND_PROP, STYLE_PROP, CHILDREN_HWNDS_PROP, RENDERED_VNODE_PROP, isVNode, getChildren, type QWVNode, type QWComponent }
351
+ // re-export as VNode for layout.ts compat
352
+ export type VNode = QWVNode
353
+ export { scaleFactor }