quickwin 1.0.2 → 1.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.
Files changed (78) hide show
  1. package/README.en.md +204 -204
  2. package/README.md +203 -203
  3. package/examples/PdfCanvas.tsx +83 -83
  4. package/examples/exec_server.ts +35 -35
  5. package/examples/pdf_preview2.ts +375 -375
  6. package/examples/pdf_viewer.tsx +279 -279
  7. package/examples/test_gallery.tsx +285 -285
  8. package/examples/test_react_lazy.tsx +63 -63
  9. package/examples/test_react_listbox.tsx +71 -71
  10. package/examples/test_root_window.tsx +41 -41
  11. package/examples/test_tab.tsx +75 -75
  12. package/examples/tray_demo.tsx +81 -81
  13. package/lib/assert.ts +6 -6
  14. package/lib/cache-utils.ts +40 -40
  15. package/lib/dumpRects.ts +25 -25
  16. package/lib/fetch.ts +502 -502
  17. package/lib/http-common.ts +399 -399
  18. package/lib/http-server.ts +424 -424
  19. package/lib/polyfill.ts +171 -171
  20. package/lib/react-qw/components/index.ts +17 -17
  21. package/lib/react-qw/index.ts +90 -90
  22. package/lib/react-qw/jsx.d.ts +34 -34
  23. package/lib/react-qw/layout.ts +111 -111
  24. package/lib/react-qw/props.ts +36 -36
  25. package/lib/react-qw/reconciler.ts +373 -373
  26. package/lib/stream.ts +21 -21
  27. package/lib/text-codec.ts +171 -171
  28. package/lib/text-measure.ts +54 -54
  29. package/lib/url.ts +201 -201
  30. package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
  31. package/lib/vendor/web-streams/ponyfill.mjs +8 -8
  32. package/lib/websocket.js +8 -0
  33. package/lib/websocket.ts +563 -555
  34. package/package.json +50 -44
  35. package/qwin-nowasm-x86.exe +0 -0
  36. package/qwin-nowasm.exe +0 -0
  37. package/qwin-x86.exe +0 -0
  38. package/qwin.exe +0 -0
  39. package/test/run.ts +96 -96
  40. package/test/test_basic.ts +9 -9
  41. package/test/test_basic_fetch.ts +33 -33
  42. package/test/test_brotli.ts +52 -52
  43. package/test/test_fetch_cache.ts +187 -187
  44. package/test/test_ffi.ts +174 -174
  45. package/test/test_frame_encoding.ts +132 -132
  46. package/test/test_helper.ts +80 -80
  47. package/test/test_http_import.ts +74 -74
  48. package/test/test_http_server.ts +136 -136
  49. package/test/test_import_dir.ts +5 -5
  50. package/test/test_ipv6.ts +129 -129
  51. package/test/test_meta_url.ts +4 -4
  52. package/test/test_mupdf_render.ts +74 -74
  53. package/test/test_mupdf_twice.ts +81 -81
  54. package/test/test_mupdf_wasm.ts +30 -30
  55. package/test/test_net_event.ts +55 -55
  56. package/test/test_net_fetch.ts +238 -238
  57. package/test/test_net_websocket.ts +164 -164
  58. package/test/test_polyfill.ts +159 -159
  59. package/test/test_url.ts +183 -183
  60. package/test/test_wasm_basic.ts +73 -73
  61. package/test/test_wasm_import_global.ts +45 -45
  62. package/test/test_wasm_sjlj.ts +125 -125
  63. package/test/test_wasm_types.ts +124 -124
  64. package/test/test_worker.ts +24 -24
  65. package/test/test_worker_fetch.ts +27 -27
  66. package/test/test_worker_net.ts +54 -54
  67. package/test/test_worker_wasm_concurrent.ts +45 -45
  68. package/test/worker_fetch.ts +13 -13
  69. package/test/worker_helper.ts +1 -1
  70. package/test/worker_http.ts +20 -20
  71. package/test/worker_import_lib.ts +13 -13
  72. package/test/worker_module.ts +17 -17
  73. package/test/worker_wasm_concurrent.ts +22 -22
  74. package/tsconfig.json +27 -27
  75. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
  76. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
  77. package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
  78. package/vendor/mupdf-wasm/mupdf.js +3317 -3317
package/lib/polyfill.ts CHANGED
@@ -1,171 +1,171 @@
1
- import * as os from 'os'
2
- import './url.js'
3
- import './text-codec.js'
4
-
5
-
6
- declare global {
7
- interface Console {
8
- error: (...args: unknown[]) => void
9
- }
10
-
11
- var window: typeof globalThis
12
-
13
- type TimerId = os.TimerId
14
- function setTimeout<P extends unknown[]>(fn: (...argsf: P) => void, ms?: number, ...argsr: P): TimerId
15
- function clearTimeout(id: TimerId): void
16
-
17
- function btoa(data: string): string
18
- function atob(data: string): string
19
-
20
- interface SubtleCrypto {
21
- digest(algorithm: 'SHA-1', data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer>
22
- }
23
-
24
- interface Crypto {
25
- getRandomValues<T extends ArrayBufferView>(array: T): T
26
- subtle: SubtleCrypto
27
- }
28
-
29
- var crypto: Crypto
30
- }
31
-
32
- // 1. window — triggers Emscripten browser path
33
- if (typeof globalThis.window === 'undefined')
34
- globalThis.window = globalThis
35
-
36
- // 2. setTimeout polyfill — bridge to os.setTimeout
37
- if (typeof globalThis.setTimeout === 'undefined') {
38
- globalThis.setTimeout = ((fn, ms, ...args) => {
39
- return os.setTimeout(() => fn(...args), ms ?? 0)
40
- }) satisfies typeof setTimeout
41
- }
42
-
43
- // 2b. clearTimeout polyfill — bridge to os.clearTimeout
44
- if (typeof globalThis.clearTimeout === 'undefined') {
45
- globalThis.clearTimeout = os.clearTimeout
46
- }
47
-
48
- // 3. console.error polyfill — QuickJS only has console.log
49
- if (typeof console.error === 'undefined') {
50
- console.error = (...args: unknown[]) => { console.log(...args) }
51
- }
52
-
53
-
54
- // ── SHA-1 ──
55
-
56
- function sha1Bytes(bytes: number[]): ArrayBuffer {
57
- const len = bytes.length
58
- const ml = len * 8
59
-
60
- bytes.push(0x80)
61
- while (bytes.length % 64 !== 56) { bytes.push(0) }
62
- bytes.push(0)
63
- bytes.push(0)
64
- bytes.push(0)
65
- bytes.push(0)
66
- bytes.push((ml >>> 24) & 0xFF)
67
- bytes.push((ml >>> 16) & 0xFF)
68
- bytes.push((ml >>> 8) & 0xFF)
69
- bytes.push(ml & 0xFF)
70
-
71
- let h0 = 0x67452301, h1 = 0xEFCDAB89
72
- let h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0
73
- const rotl = (x: number, n: number): number => ((x << n) | (x >>> (32 - n))) >>> 0
74
-
75
- for (let i = 0; i < bytes.length; i += 64) {
76
- const w: number[] = []
77
- for (let t = 0; t < 16; t++) {
78
- w[t] = (bytes[i + 4 * t]! << 24) | (bytes[i + 4 * t + 1]! << 16) |
79
- (bytes[i + 4 * t + 2]! << 8) | bytes[i + 4 * t + 3]!
80
- }
81
- for (let t = 16; t < 80; t++) {
82
- w[t] = rotl(w[t - 3]! ^ w[t - 8]! ^ w[t - 14]! ^ w[t - 16]!, 1)
83
- }
84
-
85
- let a = h0, b = h1, c = h2, d = h3, e = h4
86
- for (let t = 0; t < 80; t++) {
87
- let f: number, k: number
88
- if (t < 20) { f = (b & c) | (~b & d); k = 0x5A827999 }
89
- else if (t < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1 }
90
- else if (t < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC }
91
- else { f = b ^ c ^ d; k = 0xCA62C1D6 }
92
- const temp = (rotl(a, 5) + f + e + k + w[t]!) >>> 0
93
- e = d; d = c; c = rotl(b, 30); b = a; a = temp
94
- }
95
- h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0
96
- h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0; h4 = (h4 + e) >>> 0
97
- }
98
-
99
- const result = new Uint8Array(20)
100
- const w32 = (off: number, v: number) => {
101
- result[off] = (v >>> 24) & 0xFF; result[off + 1] = (v >>> 16) & 0xFF
102
- result[off + 2] = (v >>> 8) & 0xFF; result[off + 3] = v & 0xFF
103
- }
104
- w32(0, h0); w32(4, h1); w32(8, h2); w32(12, h3); w32(16, h4)
105
- return result.buffer
106
- }
107
-
108
- // ── Base64 ──
109
-
110
- const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
111
-
112
- function b64Encode(data: Uint8Array): string {
113
- let r = ''
114
- for (let i = 0; i < data.length; i += 3) {
115
- const b0 = data[i]!, b1 = i + 1 < data.length ? data[i + 1]! : 0, b2 = i + 2 < data.length ? data[i + 2]! : 0
116
- r += B64[b0 >> 2]! + B64[((b0 & 3) << 4) | (b1 >> 4)]!
117
- r += i + 1 < data.length ? B64[((b1 & 15) << 2) | (b2 >> 6)]! : '='
118
- r += i + 2 < data.length ? B64[b2 & 63]! : '='
119
- }
120
- return r
121
- }
122
-
123
- function b64Decode(str: string): Uint8Array {
124
- str = str.replace(/[^A-Za-z0-9+/=]/g, '')
125
- const bytes: number[] = []
126
- for (let i = 0; i < str.length; i += 4) {
127
- const c0 = B64.indexOf(str[i]!), c1 = B64.indexOf(str[i + 1]!)
128
- const c2 = B64.indexOf(str[i + 2]!), c3 = B64.indexOf(str[i + 3]!)
129
- bytes.push((c0 << 2) | (c1 >> 4))
130
- if (c2 >= 0) bytes.push(((c1 & 15) << 4) | (c2 >> 2))
131
- if (c3 >= 0) bytes.push(((c2 & 3) << 6) | c3)
132
- }
133
- return new Uint8Array(bytes)
134
- }
135
-
136
- // ── btoa / atob ──
137
-
138
- if (typeof globalThis.btoa === 'undefined') {
139
- globalThis.btoa = (data: string): string => {
140
- const bytes = new Uint8Array(data.length)
141
- for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 0xFF
142
- return b64Encode(bytes)
143
- }
144
- }
145
-
146
- if (typeof globalThis.atob === 'undefined') {
147
- globalThis.atob = (data: string): string => {
148
- const bytes = b64Decode(data)
149
- let r = ''
150
- for (let i = 0; i < bytes.length; i++) r += String.fromCharCode(bytes[i]!)
151
- return r
152
- }
153
- }
154
-
155
- if (typeof globalThis.crypto === 'undefined') {
156
- globalThis.crypto = {
157
- getRandomValues: <T extends ArrayBufferView>(array: T): T => {
158
- const view = new Uint8Array(array.buffer, array.byteOffset, array.byteLength)
159
- for (let i = 0; i < view.length; i++) view[i] = (Math.random() * 256) | 0
160
- return array
161
- },
162
- subtle: {
163
- digest: (algorithm: string, data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer> => {
164
- if (algorithm.toUpperCase() !== 'SHA-1')
165
- return Promise.reject(new Error('Digest: ' + algorithm + ' not supported'))
166
- const bytes = data instanceof Uint8Array ? Array.from(data) : Array.from(new Uint8Array(data))
167
- return Promise.resolve(sha1Bytes(bytes))
168
- }
169
- }
170
- }
171
- }
1
+ import * as os from 'os'
2
+ import './url.js'
3
+ import './text-codec.js'
4
+
5
+
6
+ declare global {
7
+ interface Console {
8
+ error: (...args: unknown[]) => void
9
+ }
10
+
11
+ var window: typeof globalThis
12
+
13
+ type TimerId = os.TimerId
14
+ function setTimeout<P extends unknown[]>(fn: (...argsf: P) => void, ms?: number, ...argsr: P): TimerId
15
+ function clearTimeout(id: TimerId): void
16
+
17
+ function btoa(data: string): string
18
+ function atob(data: string): string
19
+
20
+ interface SubtleCrypto {
21
+ digest(algorithm: 'SHA-1', data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer>
22
+ }
23
+
24
+ interface Crypto {
25
+ getRandomValues<T extends ArrayBufferView>(array: T): T
26
+ subtle: SubtleCrypto
27
+ }
28
+
29
+ var crypto: Crypto
30
+ }
31
+
32
+ // 1. window — triggers Emscripten browser path
33
+ if (typeof globalThis.window === 'undefined')
34
+ globalThis.window = globalThis
35
+
36
+ // 2. setTimeout polyfill — bridge to os.setTimeout
37
+ if (typeof globalThis.setTimeout === 'undefined') {
38
+ globalThis.setTimeout = ((fn, ms, ...args) => {
39
+ return os.setTimeout(() => fn(...args), ms ?? 0)
40
+ }) satisfies typeof setTimeout
41
+ }
42
+
43
+ // 2b. clearTimeout polyfill — bridge to os.clearTimeout
44
+ if (typeof globalThis.clearTimeout === 'undefined') {
45
+ globalThis.clearTimeout = os.clearTimeout
46
+ }
47
+
48
+ // 3. console.error polyfill — QuickJS only has console.log
49
+ if (typeof console.error === 'undefined') {
50
+ console.error = (...args: unknown[]) => { console.log(...args) }
51
+ }
52
+
53
+
54
+ // ── SHA-1 ──
55
+
56
+ function sha1Bytes(bytes: number[]): ArrayBuffer {
57
+ const len = bytes.length
58
+ const ml = len * 8
59
+
60
+ bytes.push(0x80)
61
+ while (bytes.length % 64 !== 56) { bytes.push(0) }
62
+ bytes.push(0)
63
+ bytes.push(0)
64
+ bytes.push(0)
65
+ bytes.push(0)
66
+ bytes.push((ml >>> 24) & 0xFF)
67
+ bytes.push((ml >>> 16) & 0xFF)
68
+ bytes.push((ml >>> 8) & 0xFF)
69
+ bytes.push(ml & 0xFF)
70
+
71
+ let h0 = 0x67452301, h1 = 0xEFCDAB89
72
+ let h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0
73
+ const rotl = (x: number, n: number): number => ((x << n) | (x >>> (32 - n))) >>> 0
74
+
75
+ for (let i = 0; i < bytes.length; i += 64) {
76
+ const w: number[] = []
77
+ for (let t = 0; t < 16; t++) {
78
+ w[t] = (bytes[i + 4 * t]! << 24) | (bytes[i + 4 * t + 1]! << 16) |
79
+ (bytes[i + 4 * t + 2]! << 8) | bytes[i + 4 * t + 3]!
80
+ }
81
+ for (let t = 16; t < 80; t++) {
82
+ w[t] = rotl(w[t - 3]! ^ w[t - 8]! ^ w[t - 14]! ^ w[t - 16]!, 1)
83
+ }
84
+
85
+ let a = h0, b = h1, c = h2, d = h3, e = h4
86
+ for (let t = 0; t < 80; t++) {
87
+ let f: number, k: number
88
+ if (t < 20) { f = (b & c) | (~b & d); k = 0x5A827999 }
89
+ else if (t < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1 }
90
+ else if (t < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC }
91
+ else { f = b ^ c ^ d; k = 0xCA62C1D6 }
92
+ const temp = (rotl(a, 5) + f + e + k + w[t]!) >>> 0
93
+ e = d; d = c; c = rotl(b, 30); b = a; a = temp
94
+ }
95
+ h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0
96
+ h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0; h4 = (h4 + e) >>> 0
97
+ }
98
+
99
+ const result = new Uint8Array(20)
100
+ const w32 = (off: number, v: number) => {
101
+ result[off] = (v >>> 24) & 0xFF; result[off + 1] = (v >>> 16) & 0xFF
102
+ result[off + 2] = (v >>> 8) & 0xFF; result[off + 3] = v & 0xFF
103
+ }
104
+ w32(0, h0); w32(4, h1); w32(8, h2); w32(12, h3); w32(16, h4)
105
+ return result.buffer
106
+ }
107
+
108
+ // ── Base64 ──
109
+
110
+ const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
111
+
112
+ function b64Encode(data: Uint8Array): string {
113
+ let r = ''
114
+ for (let i = 0; i < data.length; i += 3) {
115
+ const b0 = data[i]!, b1 = i + 1 < data.length ? data[i + 1]! : 0, b2 = i + 2 < data.length ? data[i + 2]! : 0
116
+ r += B64[b0 >> 2]! + B64[((b0 & 3) << 4) | (b1 >> 4)]!
117
+ r += i + 1 < data.length ? B64[((b1 & 15) << 2) | (b2 >> 6)]! : '='
118
+ r += i + 2 < data.length ? B64[b2 & 63]! : '='
119
+ }
120
+ return r
121
+ }
122
+
123
+ function b64Decode(str: string): Uint8Array {
124
+ str = str.replace(/[^A-Za-z0-9+/=]/g, '')
125
+ const bytes: number[] = []
126
+ for (let i = 0; i < str.length; i += 4) {
127
+ const c0 = B64.indexOf(str[i]!), c1 = B64.indexOf(str[i + 1]!)
128
+ const c2 = B64.indexOf(str[i + 2]!), c3 = B64.indexOf(str[i + 3]!)
129
+ bytes.push((c0 << 2) | (c1 >> 4))
130
+ if (c2 >= 0) bytes.push(((c1 & 15) << 4) | (c2 >> 2))
131
+ if (c3 >= 0) bytes.push(((c2 & 3) << 6) | c3)
132
+ }
133
+ return new Uint8Array(bytes)
134
+ }
135
+
136
+ // ── btoa / atob ──
137
+
138
+ if (typeof globalThis.btoa === 'undefined') {
139
+ globalThis.btoa = (data: string): string => {
140
+ const bytes = new Uint8Array(data.length)
141
+ for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 0xFF
142
+ return b64Encode(bytes)
143
+ }
144
+ }
145
+
146
+ if (typeof globalThis.atob === 'undefined') {
147
+ globalThis.atob = (data: string): string => {
148
+ const bytes = b64Decode(data)
149
+ let r = ''
150
+ for (let i = 0; i < bytes.length; i++) r += String.fromCharCode(bytes[i]!)
151
+ return r
152
+ }
153
+ }
154
+
155
+ if (typeof globalThis.crypto === 'undefined') {
156
+ globalThis.crypto = {
157
+ getRandomValues: <T extends ArrayBufferView>(array: T): T => {
158
+ const view = new Uint8Array(array.buffer, array.byteOffset, array.byteLength)
159
+ for (let i = 0; i < view.length; i++) view[i] = (Math.random() * 256) | 0
160
+ return array
161
+ },
162
+ subtle: {
163
+ digest: (algorithm: string, data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer> => {
164
+ if (algorithm.toUpperCase() !== 'SHA-1')
165
+ return Promise.reject(new Error('Digest: ' + algorithm + ' not supported'))
166
+ const bytes = data instanceof Uint8Array ? Array.from(data) : Array.from(new Uint8Array(data))
167
+ return Promise.resolve(sha1Bytes(bytes))
168
+ }
169
+ }
170
+ }
171
+ }
@@ -1,17 +1,17 @@
1
- export { Button, type ButtonProps } from './Button.js'
2
- export { CheckBox, type CheckBoxProps } from './CheckBox.js'
3
- export { ComboBox, type ComboBoxProps } from './ComboBox.js'
4
- export { DateTimePicker, type DateTimePickerProps } from './DateTimePicker.js'
5
- export { Input, type InputProps } from './Input.js'
6
- export { Link, type LinkProps } from './Link.js'
7
- export { ListBox, type ListBoxProps } from './ListBox.js'
8
- export { ListView, makeColorBlock, type ListViewProps, type Column, type CellStyle, type Align } from './ListView.js'
9
- export { ProgressBar, type ProgressBarProps } from './ProgressBar.js'
10
- export { RadioButton, type RadioButtonProps } from './RadioButton.js'
11
- export { ScrollView, type ScrollViewProps } from './ScrollView.js'
12
- export { Slider, type SliderProps } from './Slider.js'
13
- export { Tab, type TabProps } from './Tab.js'
14
- export { Tooltip, type TooltipProps } from './Tooltip.js'
15
- export { TreeView, type TreeViewProps, type TreeNode } from './TreeView.js'
16
- export { PathPicker, type PathPickerProps } from './PathPicker.js'
17
-
1
+ export { Button, type ButtonProps } from './Button.js'
2
+ export { CheckBox, type CheckBoxProps } from './CheckBox.js'
3
+ export { ComboBox, type ComboBoxProps } from './ComboBox.js'
4
+ export { DateTimePicker, type DateTimePickerProps } from './DateTimePicker.js'
5
+ export { Input, type InputProps } from './Input.js'
6
+ export { Link, type LinkProps } from './Link.js'
7
+ export { ListBox, type ListBoxProps } from './ListBox.js'
8
+ export { ListView, makeColorBlock, type ListViewProps, type Column, type CellStyle, type Align } from './ListView.js'
9
+ export { ProgressBar, type ProgressBarProps } from './ProgressBar.js'
10
+ export { RadioButton, type RadioButtonProps } from './RadioButton.js'
11
+ export { ScrollView, type ScrollViewProps } from './ScrollView.js'
12
+ export { Slider, type SliderProps } from './Slider.js'
13
+ export { Tab, type TabProps } from './Tab.js'
14
+ export { Tooltip, type TooltipProps } from './Tooltip.js'
15
+ export { TreeView, type TreeViewProps, type TreeNode } from './TreeView.js'
16
+ export { PathPicker, type PathPickerProps } from './PathPicker.js'
17
+
@@ -1,90 +1,90 @@
1
- import * as gui from 'gui'
2
- import React from 'react'
3
- import reconciler, { scaleFactor, instancesByHwnd, forceFlexLayout, type Instance } from './reconciler.js'
4
-
5
- const noop = () => { }
6
-
7
- export interface RootWindowConfig {
8
- text?: string
9
- ws?: number
10
- x?: number
11
- y?: number
12
- width?: number
13
- height?: number
14
- noShowWindow?: boolean
15
- onEvent?: (e: { hwnd: gui.HWND; msg: number; wParam: number; lParam: number }) => number | void
16
- }
17
-
18
- let defaultClassRegistered = false
19
- const DEFAULT_CLASS = '_QuickWinDefault'
20
- const rootMap = new Map<gui.HWND, { root: ReturnType<typeof reconciler.createContainer>, onEvent?: RootWindowConfig['onEvent'] }>()
21
-
22
- function ensureDefaultClass() {
23
- if (defaultClassRegistered) return DEFAULT_CLASS
24
- gui.RegisterClass(DEFAULT_CLASS, (hwnd, msg, wParam, lParam) => {
25
- const entry = rootMap.get(hwnd)
26
- if (msg === gui.WmMsg.DESTROY && entry) {
27
- rootMap.delete(hwnd)
28
- reconciler.updateContainer(null, entry.root, null, () => {
29
- instancesByHwnd.delete(hwnd)
30
- })
31
- }
32
- const result = entry?.onEvent?.({ hwnd, msg, wParam, lParam })
33
- if (typeof result === 'number') return result
34
- if (msg === gui.WmMsg.SIZE) forceFlexLayout(hwnd)
35
- return gui.DefWindowProc(hwnd, msg, wParam, lParam)
36
- })
37
- defaultClassRegistered = true
38
- return DEFAULT_CLASS
39
- }
40
-
41
- export function createRoot(container: gui.HWND | RootWindowConfig) {
42
- let hwnd: gui.HWND
43
- if (typeof container === 'object') {
44
- const cfg = container
45
- const className = ensureDefaultClass()
46
- const ws = (cfg.ws ?? 0) | gui.WindowStyle.OVERLAPPEDWINDOW
47
- const w = (cfg.width ?? 800) * scaleFactor
48
- const h = (cfg.height ?? 600) * scaleFactor
49
- const [sw, sh] = gui.GetScreenSize()
50
- const x = cfg.x === undefined ? ((sw - w) / 2) | 0
51
- : cfg.x === gui.CreatePos.USEDEFAULT ? gui.CreatePos.USEDEFAULT
52
- : cfg.x * scaleFactor
53
- const y = cfg.y === undefined ? ((sh - h) / 2) | 0
54
- : cfg.y === gui.CreatePos.USEDEFAULT ? gui.CreatePos.USEDEFAULT
55
- : cfg.y * scaleFactor
56
- const hwin = gui.CreateWindow(className, cfg.text || '', ws, x, y, w, h, null, null)
57
- if (!hwin) throw new Error('CreateWindow failed')
58
- hwnd = hwin
59
- if (!cfg.noShowWindow) gui.ShowWindow(hwnd)
60
- } else {
61
- hwnd = container
62
- }
63
- const root = reconciler.createContainer(
64
- hwnd, 0, null, false, null, '',
65
- noop, noop, noop, noop,
66
- )
67
- const rootInst: Instance = { hwnd, type: '_root', props: { type: '_root', style: { flexDirection: 'column', alignItems: 'stretch' } }, children: [] }
68
- instancesByHwnd.set(hwnd, rootInst)
69
- if (typeof container === 'object')
70
- rootMap.set(hwnd, { root, onEvent: container.onEvent })
71
- return {
72
- hwnd,
73
- render(element: React.ReactNode) {
74
- reconciler.updateContainer(element, root, null, noop)
75
- },
76
- unmount() {
77
- reconciler.updateContainer(null, root, null, noop)
78
- }
79
- }
80
- }
81
-
82
- export * from './components/index.js'
83
-
84
- export function render(element: React.ReactNode, containerOrConfig?: gui.HWND | RootWindowConfig | null, _callback?: () => void) {
85
- if (containerOrConfig == null) {
86
- throw new Error('render() requires a window handle or RootWindowConfig as second argument')
87
- }
88
- const root = createRoot(containerOrConfig)
89
- root.render(element)
90
- }
1
+ import * as gui from 'gui'
2
+ import React from 'react'
3
+ import reconciler, { scaleFactor, instancesByHwnd, forceFlexLayout, type Instance } from './reconciler.js'
4
+
5
+ const noop = () => { }
6
+
7
+ export interface RootWindowConfig {
8
+ text?: string
9
+ ws?: number
10
+ x?: number
11
+ y?: number
12
+ width?: number
13
+ height?: number
14
+ noShowWindow?: boolean
15
+ onEvent?: (e: { hwnd: gui.HWND; msg: number; wParam: number; lParam: number }) => number | void
16
+ }
17
+
18
+ let defaultClassRegistered = false
19
+ const DEFAULT_CLASS = '_QuickWinDefault'
20
+ const rootMap = new Map<gui.HWND, { root: ReturnType<typeof reconciler.createContainer>, onEvent?: RootWindowConfig['onEvent'] }>()
21
+
22
+ function ensureDefaultClass() {
23
+ if (defaultClassRegistered) return DEFAULT_CLASS
24
+ gui.RegisterClass(DEFAULT_CLASS, (hwnd, msg, wParam, lParam) => {
25
+ const entry = rootMap.get(hwnd)
26
+ if (msg === gui.WmMsg.DESTROY && entry) {
27
+ rootMap.delete(hwnd)
28
+ reconciler.updateContainer(null, entry.root, null, () => {
29
+ instancesByHwnd.delete(hwnd)
30
+ })
31
+ }
32
+ const result = entry?.onEvent?.({ hwnd, msg, wParam, lParam })
33
+ if (typeof result === 'number') return result
34
+ if (msg === gui.WmMsg.SIZE) forceFlexLayout(hwnd)
35
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam)
36
+ })
37
+ defaultClassRegistered = true
38
+ return DEFAULT_CLASS
39
+ }
40
+
41
+ export function createRoot(container: gui.HWND | RootWindowConfig) {
42
+ let hwnd: gui.HWND
43
+ if (typeof container === 'object') {
44
+ const cfg = container
45
+ const className = ensureDefaultClass()
46
+ const ws = (cfg.ws ?? 0) | gui.WindowStyle.OVERLAPPEDWINDOW
47
+ const w = (cfg.width ?? 800) * scaleFactor
48
+ const h = (cfg.height ?? 600) * scaleFactor
49
+ const [sw, sh] = gui.GetScreenSize()
50
+ const x = cfg.x === undefined ? ((sw - w) / 2) | 0
51
+ : cfg.x === gui.CreatePos.USEDEFAULT ? gui.CreatePos.USEDEFAULT
52
+ : cfg.x * scaleFactor
53
+ const y = cfg.y === undefined ? ((sh - h) / 2) | 0
54
+ : cfg.y === gui.CreatePos.USEDEFAULT ? gui.CreatePos.USEDEFAULT
55
+ : cfg.y * scaleFactor
56
+ const hwin = gui.CreateWindow(className, cfg.text || '', ws, x, y, w, h, null, null)
57
+ if (!hwin) throw new Error('CreateWindow failed')
58
+ hwnd = hwin
59
+ if (!cfg.noShowWindow) gui.ShowWindow(hwnd)
60
+ } else {
61
+ hwnd = container
62
+ }
63
+ const root = reconciler.createContainer(
64
+ hwnd, 0, null, false, null, '',
65
+ noop, noop, noop, noop,
66
+ )
67
+ const rootInst: Instance = { hwnd, type: '_root', props: { type: '_root', style: { flexDirection: 'column', alignItems: 'stretch' } }, children: [] }
68
+ instancesByHwnd.set(hwnd, rootInst)
69
+ if (typeof container === 'object')
70
+ rootMap.set(hwnd, { root, onEvent: container.onEvent })
71
+ return {
72
+ hwnd,
73
+ render(element: React.ReactNode) {
74
+ reconciler.updateContainer(element, root, null, noop)
75
+ },
76
+ unmount() {
77
+ reconciler.updateContainer(null, root, null, noop)
78
+ }
79
+ }
80
+ }
81
+
82
+ export * from './components/index.js'
83
+
84
+ export function render(element: React.ReactNode, containerOrConfig?: gui.HWND | RootWindowConfig | null, _callback?: () => void) {
85
+ if (containerOrConfig == null) {
86
+ throw new Error('render() requires a window handle or RootWindowConfig as second argument')
87
+ }
88
+ const root = createRoot(containerOrConfig)
89
+ root.render(element)
90
+ }
@@ -1,34 +1,34 @@
1
- import type { HWND } from 'gui'
2
-
3
- interface WStyle {
4
- width?: number | 'auto'; height?: number | 'auto'
5
- flexDirection?: 'row' | 'column'
6
- justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'
7
- alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch'
8
- alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch'
9
- gap?: number
10
- padding?: number
11
- paddingTop?: number
12
- paddingRight?: number
13
- paddingBottom?: number
14
- paddingLeft?: number
15
- flexGrow?: number
16
- }
17
-
18
- interface WEvent {
19
- hwnd: HWND; msg: number; wParam: number; lParam: number
20
- }
21
- interface WOnEventObj {
22
- fn: (e: WEvent & {callOldWndProc: () => number}) => number
23
- }
24
- interface WIntrinsicProps {
25
- key?: string | number; type: string; text?: string; ws?: number
26
- disabled?: boolean; visible?: boolean; hidden?: boolean; style?: WStyle
27
- onEvent?: ((e: WEvent) => number | void) | WOnEventObj
28
- children?: React.ReactNode
29
- ref?: React.Ref<HWND>
30
- }
31
- declare module 'react' {
32
- namespace JSX { interface IntrinsicElements { w: WIntrinsicProps } }
33
- }
34
- export type { WEvent, WIntrinsicProps, WStyle }
1
+ import type { HWND } from 'gui'
2
+
3
+ interface WStyle {
4
+ width?: number | 'auto'; height?: number | 'auto'
5
+ flexDirection?: 'row' | 'column'
6
+ justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'
7
+ alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch'
8
+ alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch'
9
+ gap?: number
10
+ padding?: number
11
+ paddingTop?: number
12
+ paddingRight?: number
13
+ paddingBottom?: number
14
+ paddingLeft?: number
15
+ flexGrow?: number
16
+ }
17
+
18
+ interface WEvent {
19
+ hwnd: HWND; msg: number; wParam: number; lParam: number
20
+ }
21
+ interface WOnEventObj {
22
+ fn: (e: WEvent & {callOldWndProc: () => number}) => number
23
+ }
24
+ interface WIntrinsicProps {
25
+ key?: string | number; type: string; text?: string; ws?: number
26
+ disabled?: boolean; visible?: boolean; hidden?: boolean; style?: WStyle
27
+ onEvent?: ((e: WEvent) => number | void) | WOnEventObj
28
+ children?: React.ReactNode
29
+ ref?: React.Ref<HWND>
30
+ }
31
+ declare module 'react' {
32
+ namespace JSX { interface IntrinsicElements { w: WIntrinsicProps } }
33
+ }
34
+ export type { WEvent, WIntrinsicProps, WStyle }