quickwin 1.0.0

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 (175) hide show
  1. package/README.en.md +197 -0
  2. package/README.md +196 -0
  3. package/examples/PdfCanvas.js +67 -0
  4. package/examples/PdfCanvas.tsx +83 -0
  5. package/examples/pdf_preview2.js +431 -0
  6. package/examples/pdf_preview2.ts +375 -0
  7. package/examples/pdf_viewer.js +17623 -0
  8. package/examples/pdf_viewer.tsx +279 -0
  9. package/examples/test_gallery.js +17452 -0
  10. package/examples/test_gallery.tsx +285 -0
  11. package/examples/test_react_lazy.js +17336 -0
  12. package/examples/test_react_lazy.tsx +63 -0
  13. package/examples/test_react_listbox.js +17339 -0
  14. package/examples/test_react_listbox.tsx +71 -0
  15. package/examples/test_root_window.js +17313 -0
  16. package/examples/test_root_window.tsx +41 -0
  17. package/examples/test_tab.js +17339 -0
  18. package/examples/test_tab.tsx +75 -0
  19. package/examples/tray_demo.js +79 -0
  20. package/examples/tray_demo.tsx +81 -0
  21. package/lib/assert.js +8 -0
  22. package/lib/assert.ts +7 -0
  23. package/lib/cache-utils.js +43 -0
  24. package/lib/cache-utils.ts +40 -0
  25. package/lib/dumpRects.js +25 -0
  26. package/lib/dumpRects.ts +25 -0
  27. package/lib/fetch.js +490 -0
  28. package/lib/fetch.ts +502 -0
  29. package/lib/http-common.js +342 -0
  30. package/lib/http-common.ts +399 -0
  31. package/lib/http-server.js +416 -0
  32. package/lib/http-server.ts +424 -0
  33. package/lib/polyfill.js +156 -0
  34. package/lib/polyfill.ts +171 -0
  35. package/lib/react-qw/components/Button.js +8 -0
  36. package/lib/react-qw/components/CheckBox.js +31 -0
  37. package/lib/react-qw/components/ComboBox.js +42 -0
  38. package/lib/react-qw/components/DateTimePicker.js +96 -0
  39. package/lib/react-qw/components/Input.js +50 -0
  40. package/lib/react-qw/components/Link.js +43 -0
  41. package/lib/react-qw/components/ListBox.js +48 -0
  42. package/lib/react-qw/components/ListView.js +328 -0
  43. package/lib/react-qw/components/PathPicker.js +136 -0
  44. package/lib/react-qw/components/ProgressBar.js +22 -0
  45. package/lib/react-qw/components/RadioButton.js +31 -0
  46. package/lib/react-qw/components/ScrollView.js +139 -0
  47. package/lib/react-qw/components/Slider.js +45 -0
  48. package/lib/react-qw/components/Tab.js +61 -0
  49. package/lib/react-qw/components/Tooltip.js +55 -0
  50. package/lib/react-qw/components/TreeView.js +130 -0
  51. package/lib/react-qw/components/index.js +16 -0
  52. package/lib/react-qw/components/index.ts +17 -0
  53. package/lib/react-qw/index.js +73 -0
  54. package/lib/react-qw/index.ts +90 -0
  55. package/lib/react-qw/jsx.d.ts +34 -0
  56. package/lib/react-qw/layout.js +82 -0
  57. package/lib/react-qw/layout.ts +111 -0
  58. package/lib/react-qw/props.js +27 -0
  59. package/lib/react-qw/props.ts +36 -0
  60. package/lib/react-qw/reconciler.js +333 -0
  61. package/lib/react-qw/reconciler.ts +373 -0
  62. package/lib/stream.js +2 -0
  63. package/lib/stream.ts +22 -0
  64. package/lib/text-codec.js +197 -0
  65. package/lib/text-codec.ts +171 -0
  66. package/lib/text-measure.js +52 -0
  67. package/lib/text-measure.ts +54 -0
  68. package/lib/url.js +278 -0
  69. package/lib/url.ts +201 -0
  70. package/lib/vendor/web-streams/ponyfill.d.mts +829 -0
  71. package/lib/vendor/web-streams/ponyfill.mjs +8 -0
  72. package/lib/websocket.js +541 -0
  73. package/lib/websocket.ts +555 -0
  74. package/package.json +44 -0
  75. package/quickwin.d.ts +594 -0
  76. package/quickwin_const.d.ts +900 -0
  77. package/qwin-nowasm.exe +0 -0
  78. package/qwin.exe +0 -0
  79. package/test/add.wasm +0 -0
  80. package/test/complex.wasm +0 -0
  81. package/test/complex_imports.wasm +0 -0
  82. package/test/debug/http_server_e2e.js +31 -0
  83. package/test/debug/minwasm.js +52 -0
  84. package/test/debug/mupdf_debug.js +104 -0
  85. package/test/debug/wasm.js +122 -0
  86. package/test/debug/wasm_export_debug.js +64 -0
  87. package/test/debug/wasm_names.js +46 -0
  88. package/test/global_imports.wasm +0 -0
  89. package/test/import_func.wasm +0 -0
  90. package/test/imports.wasm +0 -0
  91. package/test/run.js +91 -0
  92. package/test/run.ts +96 -0
  93. package/test/sjlj.wasm +0 -0
  94. package/test/test_basic.js +7 -0
  95. package/test/test_basic.ts +9 -0
  96. package/test/test_basic_fetch.js +34 -0
  97. package/test/test_basic_fetch.ts +33 -0
  98. package/test/test_brotli.js +48 -0
  99. package/test/test_brotli.ts +52 -0
  100. package/test/test_fetch_cache.js +169 -0
  101. package/test/test_fetch_cache.ts +187 -0
  102. package/test/test_ffi.js +157 -0
  103. package/test/test_ffi.ts +174 -0
  104. package/test/test_frame_encoding.js +128 -0
  105. package/test/test_frame_encoding.ts +132 -0
  106. package/test/test_helper.js +84 -0
  107. package/test/test_helper.ts +80 -0
  108. package/test/test_http_import.js +78 -0
  109. package/test/test_http_import.ts +74 -0
  110. package/test/test_http_server.js +142 -0
  111. package/test/test_http_server.ts +136 -0
  112. package/test/test_import_dir.js +4 -0
  113. package/test/test_import_dir.ts +5 -0
  114. package/test/test_ipv6.js +134 -0
  115. package/test/test_ipv6.ts +129 -0
  116. package/test/test_jsx.js +1314 -0
  117. package/test/test_meta_url.js +3 -0
  118. package/test/test_meta_url.ts +4 -0
  119. package/test/test_mupdf_render.js +69 -0
  120. package/test/test_mupdf_render.ts +74 -0
  121. package/test/test_mupdf_twice.js +77 -0
  122. package/test/test_mupdf_twice.ts +81 -0
  123. package/test/test_mupdf_wasm.js +33 -0
  124. package/test/test_mupdf_wasm.ts +30 -0
  125. package/test/test_net_event.js +57 -0
  126. package/test/test_net_event.ts +55 -0
  127. package/test/test_net_fetch.js +259 -0
  128. package/test/test_net_fetch.ts +238 -0
  129. package/test/test_net_websocket.js +180 -0
  130. package/test/test_net_websocket.ts +164 -0
  131. package/test/test_polyfill.js +144 -0
  132. package/test/test_polyfill.ts +159 -0
  133. package/test/test_react_complex.js +17440 -0
  134. package/test/test_react_counter.js +17331 -0
  135. package/test/test_react_flex.js +17643 -0
  136. package/test/test_react_hidden.js +17336 -0
  137. package/test/test_react_listview_link.js +17371 -0
  138. package/test/test_react_render.js +17326 -0
  139. package/test/test_url.js +173 -0
  140. package/test/test_url.ts +183 -0
  141. package/test/test_wasm_basic.js +85 -0
  142. package/test/test_wasm_basic.ts +73 -0
  143. package/test/test_wasm_import_global.js +41 -0
  144. package/test/test_wasm_import_global.ts +45 -0
  145. package/test/test_wasm_sjlj.js +145 -0
  146. package/test/test_wasm_sjlj.ts +125 -0
  147. package/test/test_wasm_types.js +97 -0
  148. package/test/test_wasm_types.ts +124 -0
  149. package/test/test_worker.js +21 -0
  150. package/test/test_worker.ts +24 -0
  151. package/test/test_worker_fetch.js +26 -0
  152. package/test/test_worker_fetch.ts +27 -0
  153. package/test/test_worker_net.js +51 -0
  154. package/test/test_worker_net.ts +54 -0
  155. package/test/test_worker_wasm_concurrent.js +44 -0
  156. package/test/test_worker_wasm_concurrent.ts +45 -0
  157. package/test/types.wasm +0 -0
  158. package/test/worker_fetch.js +13 -0
  159. package/test/worker_fetch.ts +13 -0
  160. package/test/worker_helper.js +1 -0
  161. package/test/worker_helper.ts +1 -0
  162. package/test/worker_http.js +18 -0
  163. package/test/worker_http.ts +20 -0
  164. package/test/worker_import_lib.js +12 -0
  165. package/test/worker_import_lib.ts +13 -0
  166. package/test/worker_module.js +16 -0
  167. package/test/worker_module.ts +17 -0
  168. package/test/worker_wasm_concurrent.js +22 -0
  169. package/test/worker_wasm_concurrent.ts +22 -0
  170. package/tsconfig.json +27 -0
  171. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
  172. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
  173. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  174. package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
  175. package/vendor/mupdf-wasm/mupdf.js +3317 -0
@@ -0,0 +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
+ }
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ export const Button = forwardRef(({ children, onClick, style, disabled }, ref) => {
4
+ return (_jsx("w", { type: "BUTTON", text: children != null ? (Array.isArray(children) ? children.join('') : String(children)) : '', ws: 268435456 /* gui.WindowStyle.VISIBLE */, style: style, disabled: disabled, ref: ref, onEvent: (e) => {
5
+ if (e.msg === 514 /* gui.WmMsg.LBUTTONUP */)
6
+ onClick?.();
7
+ } }));
8
+ });
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useState, useEffect, useRef } from 'react';
3
+ import * as gui from 'gui';
4
+ export const CheckBox = forwardRef(({ checked: controlledChecked, defaultChecked = false, onChange, label, style, disabled }, ref) => {
5
+ const [internalChecked, setInternalChecked] = useState(defaultChecked);
6
+ const isControlled = controlledChecked !== undefined;
7
+ const displayChecked = isControlled ? controlledChecked : internalChecked;
8
+ const cbRef = useRef(null);
9
+ useEffect(() => {
10
+ const h = cbRef.current;
11
+ if (!h)
12
+ return;
13
+ gui.SendMessage(h, 241 /* gui.ButtonMsg.SETCHECK */, displayChecked ? 1 /* gui.ButtonCheckState.CHECKED */ : 0 /* gui.ButtonCheckState.UNCHECKED */, 0);
14
+ }, [displayChecked]);
15
+ return (_jsx("w", { type: "BUTTON", text: label ?? '', ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 3 /* gui.ButtonStyle.AUTOCHECKBOX */, style: style, disabled: disabled, ref: (h) => {
16
+ cbRef.current = h;
17
+ if (typeof ref === 'function')
18
+ ref(h);
19
+ else if (ref)
20
+ ref.current = h;
21
+ }, onEvent: (e) => {
22
+ if (e.msg === 514 /* gui.WmMsg.LBUTTONUP */) {
23
+ const newChecked = gui.SendMessage(e.hwnd, 240 /* gui.ButtonMsg.GETCHECK */, 0, 0) !== 0;
24
+ if (newChecked !== displayChecked) {
25
+ if (!isControlled)
26
+ setInternalChecked(newChecked);
27
+ onChange?.(newChecked);
28
+ }
29
+ }
30
+ } }));
31
+ });
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useRef, useEffect, useState } from 'react';
3
+ import * as gui from 'gui';
4
+ export const ComboBox = forwardRef(({ items, selectedIndex: controlledIndex, defaultSelectedIndex = -1, onChange, style }, ref) => {
5
+ const [internalIndex, setInternalIndex] = useState(defaultSelectedIndex);
6
+ const isControlled = controlledIndex !== undefined;
7
+ const sel = isControlled ? controlledIndex : internalIndex;
8
+ const cbRef = useRef(null);
9
+ const cbWs = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */
10
+ | 3 /* gui.ComboBoxStyle.DROPDOWNLIST */ | 512 /* gui.ComboBoxStyle.HASSTRINGS */;
11
+ useEffect(() => {
12
+ const h = cbRef.current;
13
+ if (!h)
14
+ return;
15
+ gui.SendMessage(h, 331 /* gui.ComboBoxMsg.RESETCONTENT */, 0, 0);
16
+ for (const item of items)
17
+ gui.SendMessage(h, 323 /* gui.ComboBoxMsg.ADDSTRING */, 0, item);
18
+ if (sel >= 0 && sel < items.length)
19
+ gui.SendMessage(h, 334 /* gui.ComboBoxMsg.SETCURSEL */, sel, 0);
20
+ }, [items]);
21
+ useEffect(() => {
22
+ const h = cbRef.current;
23
+ if (h)
24
+ gui.SendMessage(h, 334 /* gui.ComboBoxMsg.SETCURSEL */, sel, 0);
25
+ }, [sel]);
26
+ return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
27
+ if (e.msg !== 273 /* gui.WmMsg.COMMAND */)
28
+ return;
29
+ const code = (e.wParam >> 16) & 0xFFFF;
30
+ if (code === 1 /* gui.CbnCode.SELCHANGE */) {
31
+ const h = cbRef.current;
32
+ if (!h)
33
+ return;
34
+ const newSel = gui.SendMessage(h, 327 /* gui.ComboBoxMsg.GETCURSEL */, 0, 0);
35
+ if (newSel !== sel && newSel >= 0) {
36
+ if (!isControlled)
37
+ setInternalIndex(newSel);
38
+ onChange?.(newSel);
39
+ }
40
+ }
41
+ }, children: _jsx("w", { type: "COMBOBOX", ws: cbWs, style: { flexGrow: 1 }, ref: (h) => { cbRef.current = h; } }) }));
42
+ });
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useRef, useEffect, useState } from 'react';
3
+ import * as gui from 'gui';
4
+ import * as ffi from 'ffi';
5
+ function readI16(ptr, offset) {
6
+ return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8);
7
+ }
8
+ function readI32(ptr, offset) {
9
+ return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
10
+ (ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24);
11
+ }
12
+ function bufPtr(buf) {
13
+ return ffi.bufferPtr(buf);
14
+ }
15
+ function dateToSysTimeBuf(d) {
16
+ const buf = new ArrayBuffer(16);
17
+ const dv = new DataView(buf);
18
+ dv.setUint16(0, d.getFullYear(), true);
19
+ dv.setUint16(2, d.getMonth() + 1, true);
20
+ dv.setUint16(4, 0, true); // wDayOfWeek (ignored on set)
21
+ dv.setUint16(6, d.getDate(), true);
22
+ dv.setUint16(8, d.getHours(), true);
23
+ dv.setUint16(10, d.getMinutes(), true);
24
+ dv.setUint16(12, d.getSeconds(), true);
25
+ dv.setUint16(14, d.getMilliseconds(), true);
26
+ return buf;
27
+ }
28
+ const DateTimePicker = forwardRef(function DateTimePicker({ value, onChange, defaultValue, format = 'short', allowNone, updown, style }, ref) {
29
+ const [internalDate, setInternalDate] = useState(defaultValue ?? null);
30
+ const isControlled = value !== undefined;
31
+ const effectiveDate = isControlled ? value : internalDate;
32
+ const dpRef = useRef(null);
33
+ const onChangeRef = useRef(onChange);
34
+ onChangeRef.current = onChange;
35
+ let dpStyle = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */;
36
+ if (format === 'long')
37
+ dpStyle |= 4 /* gui.DtStyle.LONGDATEFORMAT */;
38
+ else if (format === 'time')
39
+ dpStyle |= 9 /* gui.DtStyle.TIMEFORMAT */;
40
+ if (updown)
41
+ dpStyle |= 1 /* gui.DtStyle.UPDOWN */;
42
+ if (allowNone)
43
+ dpStyle |= 2 /* gui.DtStyle.SHOWNONE */;
44
+ useEffect(() => {
45
+ const h = dpRef.current;
46
+ if (!h)
47
+ return;
48
+ const d = effectiveDate;
49
+ if (d) {
50
+ const buf = dateToSysTimeBuf(d);
51
+ gui.SendMessage(h, 4098 /* gui.DtMsg.SETSYSTEMTIME */, 0 /* gui.DtFlag.GDT_VALID */, bufPtr(buf));
52
+ }
53
+ else {
54
+ gui.SendMessage(h, 4098 /* gui.DtMsg.SETSYSTEMTIME */, 1 /* gui.DtFlag.GDT_NONE */, 0);
55
+ }
56
+ }, [effectiveDate]);
57
+ useEffect(() => {
58
+ const h = dpRef.current;
59
+ if (!h)
60
+ return;
61
+ gui.InvalidateRect(h, null, true);
62
+ // 发送 WM_SIZE 让控件(尤其是 DTS_UPDOWN)重新布局内部子窗口
63
+ const cr = gui.GetClientRect(h);
64
+ if (cr) {
65
+ const w = cr.right - cr.left;
66
+ const hh = cr.bottom - cr.top;
67
+ gui.SendMessage(h, 5 /* gui.WmMsg.SIZE */, 0, (hh << 16) | w);
68
+ }
69
+ }, []);
70
+ return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
71
+ if (e.msg === 78 /* gui.WmMsg.NOTIFY */) {
72
+ const code = readI32(e.lParam, 16);
73
+ if (code === -759 /* gui.DtNotifyCode.DATETIMECHANGE */) {
74
+ const dwFlags = readI32(e.lParam, 24);
75
+ if (dwFlags === 1 /* gui.DtFlag.GDT_NONE */) {
76
+ if (!isControlled)
77
+ setInternalDate(null);
78
+ onChangeRef.current?.(null);
79
+ }
80
+ else {
81
+ const year = readI16(e.lParam, 28);
82
+ const month = readI16(e.lParam, 30);
83
+ const day = readI16(e.lParam, 34);
84
+ const hour = readI16(e.lParam, 36);
85
+ const min = readI16(e.lParam, 38);
86
+ const sec = readI16(e.lParam, 40);
87
+ const d = new Date(year, month - 1, day, hour, min, sec);
88
+ if (!isControlled)
89
+ setInternalDate(d);
90
+ onChangeRef.current?.(d);
91
+ }
92
+ }
93
+ }
94
+ }, children: _jsx("w", { type: "SysDateTimePick32", ws: dpStyle, style: { flexGrow: 1 }, ref: (h) => { dpRef.current = h; } }) }));
95
+ });
96
+ export { DateTimePicker };
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useState, useRef, useEffect } from 'react';
3
+ import * as gui from 'gui';
4
+ import * as ffi from 'ffi';
5
+ function textToUtf16(s) {
6
+ const buf = new ArrayBuffer((s.length + 1) * 2);
7
+ const dv = new DataView(buf);
8
+ for (let i = 0; i < s.length; i++)
9
+ dv.setUint16(i * 2, s.charCodeAt(i), true);
10
+ return buf;
11
+ }
12
+ export const Input = forwardRef(({ value: controlledValue, defaultValue = '', onChange, placeholder, password, multiline, readonly, number, disabled, style }, ref) => {
13
+ const [internalValue, setInternalValue] = useState(defaultValue);
14
+ const isControlled = controlledValue !== undefined;
15
+ const displayValue = isControlled ? controlledValue : internalValue;
16
+ const inputRef = useRef(null);
17
+ let ws = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */;
18
+ if (multiline)
19
+ ws |= 4 /* gui.EditStyle.MULTILINE */ | 64 /* gui.EditStyle.AUTOVSCROLL */ | 2097152 /* gui.WindowStyle.VSCROLL */;
20
+ if (readonly)
21
+ ws |= 2048 /* gui.EditStyle.READONLY */;
22
+ if (number)
23
+ ws |= 8192 /* gui.EditStyle.NUMBER */;
24
+ if (password)
25
+ ws |= 32 /* gui.EditStyle.PASSWORD */ | 128 /* gui.EditStyle.AUTOHSCROLL */;
26
+ useEffect(() => {
27
+ const h = inputRef.current;
28
+ if (!h || !placeholder)
29
+ return;
30
+ const buf = textToUtf16(placeholder);
31
+ gui.SendMessage(h, 5377 /* gui.EditMsg.SETCUEBANNER */, 1, ffi.bufferPtr(buf));
32
+ }, [placeholder]);
33
+ return (_jsx("w", { type: "EDIT", text: displayValue, ws: ws, style: style, disabled: disabled, ref: (h) => {
34
+ inputRef.current = h;
35
+ if (typeof ref === 'function')
36
+ ref(h);
37
+ else if (ref)
38
+ ref.current = h;
39
+ }, onEvent: (e) => {
40
+ if (e.msg === 258 /* gui.WmMsg.CHAR */) {
41
+ const h = inputRef.current;
42
+ if (!h)
43
+ return;
44
+ const newText = gui.GetWindowText(h);
45
+ if (!isControlled)
46
+ setInternalValue(newText);
47
+ onChange?.(newText);
48
+ }
49
+ } }));
50
+ });
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useRef, useEffect } from 'react';
3
+ import * as gui from 'gui';
4
+ import * as ffi from 'ffi';
5
+ function readI32(ptr, offset) {
6
+ return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
7
+ (ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24);
8
+ }
9
+ function readUtf16(ptr, offset, maxWords) {
10
+ const chars = [];
11
+ for (let i = 0; i < maxWords; i++) {
12
+ const lo = ffi.readByte(ptr + offset + i * 2);
13
+ const hi = ffi.readByte(ptr + offset + i * 2 + 1);
14
+ const code = (hi << 8) | lo;
15
+ if (code === 0)
16
+ break;
17
+ chars.push(String.fromCharCode(code));
18
+ }
19
+ return chars.join('');
20
+ }
21
+ const Link = forwardRef(function Link({ href, children, onClick, style }, ref) {
22
+ const displayText = children != null ? String(children) : (href ?? '');
23
+ const linkText = href ? `<A HREF="${href}">${displayText}</A>` : displayText;
24
+ const linkRef = useRef(null);
25
+ const onClickRef = useRef(onClick);
26
+ onClickRef.current = onClick;
27
+ useEffect(() => {
28
+ const h = linkRef.current;
29
+ if (!h)
30
+ return;
31
+ gui.InvalidateRect(h, null, true);
32
+ }, [linkText]);
33
+ return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
34
+ if (e.msg === 78 /* gui.WmMsg.NOTIFY */) {
35
+ const code = readI32(e.lParam, 16);
36
+ if (code === -2 /* gui.SysLinkNotifyCode.CLICK */ || code === -4 /* gui.SysLinkNotifyCode.RETURN */) {
37
+ const url = readUtf16(e.lParam, 136, 2048);
38
+ onClickRef.current?.(url);
39
+ }
40
+ }
41
+ }, children: _jsx("w", { type: "SysLink", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 65536 /* gui.WindowStyle.TABSTOP */, text: linkText, style: { flexGrow: 1 }, ref: (h) => { linkRef.current = h; } }) }));
42
+ });
43
+ export { Link };
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useRef, useEffect, useState, useImperativeHandle } from 'react';
3
+ import * as gui from 'gui';
4
+ export const ListBox = forwardRef(({ items, selectedIndex: controlledIndex, defaultSelectedIndex = -1, onChange, style, sort, scrollToBottom }, ref) => {
5
+ const [internalIndex, setInternalIndex] = useState(defaultSelectedIndex);
6
+ const isControlled = controlledIndex !== undefined;
7
+ const sel = isControlled ? controlledIndex : internalIndex;
8
+ const lbRef = useRef(null);
9
+ useImperativeHandle(ref, () => lbRef.current);
10
+ const lbWs = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */ | 2097152 /* gui.WindowStyle.VSCROLL */
11
+ | 64 /* gui.ListBoxStyle.HASSTRINGS */ | 1 /* gui.ListBoxStyle.NOTIFY */ | 256 /* gui.ListBoxStyle.NOINTEGRALHEIGHT */
12
+ | (sort ? 2 /* gui.ListBoxStyle.SORT */ : 0);
13
+ useEffect(() => {
14
+ const h = lbRef.current;
15
+ if (!h)
16
+ return;
17
+ gui.SendMessage(h, 388 /* gui.LbMsg.RESETCONTENT */, 0, 0);
18
+ for (const item of items)
19
+ gui.SendMessage(h, 384 /* gui.LbMsg.ADDSTRING */, 0, item);
20
+ if (sel >= 0 && sel < items.length)
21
+ gui.SendMessage(h, 390 /* gui.LbMsg.SETCURSEL */, sel, 0);
22
+ if (scrollToBottom && items.length > 0)
23
+ gui.SendMessage(h, 407 /* gui.LbMsg.SETTOPINDEX */, items.length - 1, 0);
24
+ }, [items]);
25
+ useEffect(() => {
26
+ const h = lbRef.current;
27
+ if (h)
28
+ gui.SendMessage(h, 390 /* gui.LbMsg.SETCURSEL */, sel, 0);
29
+ }, [sel]);
30
+ return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, onEvent: (e) => {
31
+ if (e.msg !== 273 /* gui.WmMsg.COMMAND */)
32
+ return;
33
+ const code = (e.wParam >> 16) & 0xFFFF;
34
+ if (code === 1 /* gui.LbnCode.SELCHANGE */) {
35
+ const h = lbRef.current;
36
+ if (!h)
37
+ return;
38
+ const newSel = gui.SendMessage(h, 392 /* gui.LbMsg.GETCURSEL */, 0, 0);
39
+ if (newSel !== sel && newSel >= 0) {
40
+ if (!isControlled)
41
+ setInternalIndex(newSel);
42
+ onChange?.(newSel);
43
+ }
44
+ }
45
+ }, children: _jsx("w", { type: "LISTBOX", ws: lbWs, style: { flexGrow: 1 }, ref: (h) => {
46
+ lbRef.current = h;
47
+ } }) }));
48
+ });