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
+ 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
+ }
@@ -0,0 +1,52 @@
1
+ import * as ffi from 'ffi';
2
+ import * as win from 'win';
3
+ import * as gui from 'gui';
4
+ function loadProc(lib, name) {
5
+ const ptr = win.GetProcAddress(lib, name);
6
+ if (!ptr)
7
+ throw new Error(`Cannot load ${name}`);
8
+ return ptr;
9
+ }
10
+ const user32 = win.LoadLibrary('user32.dll');
11
+ const gdi32 = win.LoadLibrary('gdi32.dll');
12
+ if (!user32 || !gdi32)
13
+ throw new Error('Failed to load system DLLs');
14
+ const DrawTextW = loadProc(user32, 'DrawTextW');
15
+ const GetDC = loadProc(user32, 'GetDC');
16
+ const ReleaseDC = loadProc(user32, 'ReleaseDC');
17
+ const SelectObject = loadProc(gdi32, 'SelectObject');
18
+ const FFI_TYPE_UINT64 = ffi.FFI_TYPE_UINT64;
19
+ const FFI_S32 = ffi.FFI_TYPE_SINT32;
20
+ const FFI_PTR = ffi.FFI_TYPE_POINTER;
21
+ export function measureText(hdc, text, maxWidth) {
22
+ const textBuf = new TextEncoder('utf-16le').encode(text + '\0').buffer;
23
+ const rect = new ArrayBuffer(16);
24
+ const dv = new DataView(rect);
25
+ dv.setInt32(8, maxWidth, true);
26
+ ffi.ffiCall(DrawTextW, [FFI_TYPE_UINT64, FFI_PTR, FFI_S32, FFI_PTR, FFI_S32], [hdc, textBuf, -1, rect, 1024 /* gui.DrawTextFlag.CALCRECT */], FFI_TYPE_UINT64);
27
+ return { width: dv.getInt32(8, true), height: dv.getInt32(12, true) };
28
+ }
29
+ export function getButtonIdealSize(hwnd) {
30
+ const size = new ArrayBuffer(8);
31
+ gui.SendMessage(hwnd, 5633 /* gui.ButtonExtMsg.GETIDEALSIZE */, 0, ffi.bufferPtr(size));
32
+ const dv = new DataView(size);
33
+ return { width: dv.getInt32(0, true), height: dv.getInt32(4, true) };
34
+ }
35
+ export function measureTextForHwnd(hwnd, text) {
36
+ const hdc = ffi.ffiCall(GetDC, [FFI_TYPE_UINT64], [hwnd], FFI_TYPE_UINT64);
37
+ if (!hdc)
38
+ return { width: 0, height: 0 };
39
+ const hFont = gui.SendMessage(hwnd, 49 /* gui.WmMsg.GETFONT */, 0, 0);
40
+ const oldFont = hFont ? ffi.ffiCall(SelectObject, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hdc, hFont], FFI_TYPE_UINT64) : 0;
41
+ const result = measureText(hdc, text, 0);
42
+ if (hFont)
43
+ ffi.ffiCall(SelectObject, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hdc, oldFont], FFI_TYPE_UINT64);
44
+ ffi.ffiCall(ReleaseDC, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32);
45
+ const wr = gui.GetWindowRect(hwnd);
46
+ const cr = gui.GetClientRect(hwnd);
47
+ if (wr && cr) {
48
+ result.width += (wr.right - wr.left) - (cr.right - cr.left);
49
+ result.height += (wr.bottom - wr.top) - (cr.bottom - cr.top);
50
+ }
51
+ return result;
52
+ }
@@ -0,0 +1,54 @@
1
+ import * as ffi from 'ffi'
2
+ import * as win from 'win'
3
+ import * as gui from 'gui'
4
+
5
+ function loadProc(lib: win.HMODULE, name: string): number {
6
+ const ptr = win.GetProcAddress(lib, name)
7
+ if (!ptr) throw new Error(`Cannot load ${name}`)
8
+ return ptr
9
+ }
10
+
11
+ const user32 = win.LoadLibrary('user32.dll')
12
+ const gdi32 = win.LoadLibrary('gdi32.dll')
13
+ if (!user32 || !gdi32) throw new Error('Failed to load system DLLs')
14
+ const DrawTextW = loadProc(user32, 'DrawTextW')
15
+ const GetDC = loadProc(user32, 'GetDC')
16
+ const ReleaseDC = loadProc(user32, 'ReleaseDC')
17
+ const SelectObject = loadProc(gdi32, 'SelectObject')
18
+
19
+ const FFI_TYPE_UINT64 = ffi.FFI_TYPE_UINT64
20
+ const FFI_S32 = ffi.FFI_TYPE_SINT32
21
+ const FFI_PTR = ffi.FFI_TYPE_POINTER
22
+
23
+ export function measureText(hdc: number, text: string, maxWidth: number): { width: number; height: number } {
24
+ const textBuf = new TextEncoder('utf-16le').encode(text + '\0').buffer
25
+ const rect = new ArrayBuffer(16)
26
+ const dv = new DataView(rect)
27
+ dv.setInt32(8, maxWidth, true)
28
+ ffi.ffiCall(DrawTextW, [FFI_TYPE_UINT64, FFI_PTR, FFI_S32, FFI_PTR, FFI_S32], [hdc, textBuf, -1, rect, gui.DrawTextFlag.CALCRECT], FFI_TYPE_UINT64)
29
+ return { width: dv.getInt32(8, true), height: dv.getInt32(12, true) }
30
+ }
31
+
32
+ export function getButtonIdealSize(hwnd: gui.HWND): { width: number; height: number } {
33
+ const size = new ArrayBuffer(8)
34
+ gui.SendMessage(hwnd, gui.ButtonExtMsg.GETIDEALSIZE, 0, ffi.bufferPtr(size))
35
+ const dv = new DataView(size)
36
+ return { width: dv.getInt32(0, true), height: dv.getInt32(4, true) }
37
+ }
38
+
39
+ export function measureTextForHwnd(hwnd: gui.HWND, text: string): { width: number; height: number } {
40
+ const hdc = ffi.ffiCall(GetDC, [FFI_TYPE_UINT64], [hwnd], FFI_TYPE_UINT64)
41
+ if (!hdc) return { width: 0, height: 0 }
42
+ const hFont = gui.SendMessage(hwnd, gui.WmMsg.GETFONT, 0, 0)
43
+ const oldFont = hFont ? ffi.ffiCall(SelectObject, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hdc, hFont], FFI_TYPE_UINT64) : 0
44
+ const result = measureText(hdc, text, 0)
45
+ if (hFont) ffi.ffiCall(SelectObject, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hdc, oldFont], FFI_TYPE_UINT64)
46
+ ffi.ffiCall(ReleaseDC, [FFI_TYPE_UINT64, FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32)
47
+ const wr = gui.GetWindowRect(hwnd)
48
+ const cr = gui.GetClientRect(hwnd)
49
+ if (wr && cr) {
50
+ result.width += (wr.right - wr.left) - (cr.right - cr.left)
51
+ result.height += (wr.bottom - wr.top) - (cr.bottom - cr.top)
52
+ }
53
+ return result
54
+ }
package/lib/url.js ADDED
@@ -0,0 +1,278 @@
1
+ function _encode(s) { return encodeURIComponent(s); }
2
+ function _decode(s) {
3
+ try {
4
+ return decodeURIComponent(s.replace(/\+/g, ' '));
5
+ }
6
+ catch {
7
+ return s;
8
+ }
9
+ }
10
+ function _normalizePath(path) {
11
+ if (!path)
12
+ return '/';
13
+ const parts = path.split('/');
14
+ const out = [];
15
+ let hasRoot = path[0] === '/';
16
+ if (hasRoot)
17
+ out.push('');
18
+ for (const p of parts) {
19
+ if (p === '' || p === '.')
20
+ continue;
21
+ if (p === '..') {
22
+ const last = out[out.length - 1];
23
+ if (last !== undefined && last !== '')
24
+ out.pop();
25
+ }
26
+ else {
27
+ out.push(p);
28
+ }
29
+ }
30
+ if (path.endsWith('/') && out[out.length - 1] !== '')
31
+ out.push('');
32
+ const result = out.join('/');
33
+ if (hasRoot && !result.startsWith('/'))
34
+ return '/' + result;
35
+ return result || '/';
36
+ }
37
+ function _parseURL(url) {
38
+ const r = { scheme: '', user: '', pass: '', host: '', port: '', path: '/', query: '', fragment: '' };
39
+ const fi = url.indexOf('#');
40
+ if (fi >= 0) {
41
+ r.fragment = url.slice(fi + 1);
42
+ url = url.slice(0, fi);
43
+ }
44
+ const qi = url.indexOf('?');
45
+ if (qi >= 0) {
46
+ r.query = url.slice(qi + 1);
47
+ url = url.slice(0, qi);
48
+ }
49
+ const sm = url.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*):(.*)$/);
50
+ if (sm) {
51
+ r.scheme = sm[1].toLowerCase();
52
+ url = sm[2];
53
+ }
54
+ if (url.startsWith('//')) {
55
+ url = url.slice(2);
56
+ const si = url.indexOf('/');
57
+ let auth;
58
+ if (si < 0) {
59
+ auth = url;
60
+ url = '';
61
+ }
62
+ else {
63
+ auth = url.slice(0, si);
64
+ url = url.slice(si);
65
+ }
66
+ const ai = auth.lastIndexOf('@');
67
+ if (ai >= 0) {
68
+ const ui = auth.slice(0, ai);
69
+ auth = auth.slice(ai + 1);
70
+ const ci = ui.indexOf(':');
71
+ if (ci >= 0) {
72
+ r.user = _decode(ui.slice(0, ci));
73
+ r.pass = _decode(ui.slice(ci + 1));
74
+ }
75
+ else
76
+ r.user = _decode(ui);
77
+ }
78
+ if (auth.startsWith('[')) {
79
+ const cb = auth.indexOf(']');
80
+ r.host = auth.slice(1, cb).toLowerCase();
81
+ const pp = auth.slice(cb + 1);
82
+ if (pp.startsWith(':'))
83
+ r.port = pp.slice(1);
84
+ }
85
+ else {
86
+ const ci = auth.lastIndexOf(':');
87
+ if (ci >= 0 && ci === auth.lastIndexOf(':')) {
88
+ r.host = auth.slice(0, ci).toLowerCase();
89
+ r.port = auth.slice(ci + 1);
90
+ }
91
+ else
92
+ r.host = auth.toLowerCase();
93
+ }
94
+ }
95
+ r.path = url;
96
+ if (r.host && !r.path.startsWith('/'))
97
+ r.path = '/' + r.path;
98
+ return r;
99
+ }
100
+ class URLSearchParamsImpl {
101
+ _list = [];
102
+ constructor(init) {
103
+ if (typeof init === 'string') {
104
+ const s = init.startsWith('?') ? init.slice(1) : init;
105
+ if (!s)
106
+ return;
107
+ for (const p of s.split('&')) {
108
+ const eq = p.indexOf('=');
109
+ if (eq < 0) {
110
+ this._list.push([_decode(p), '']);
111
+ continue;
112
+ }
113
+ this._list.push([_decode(p.slice(0, eq)), _decode(p.slice(eq + 1))]);
114
+ }
115
+ }
116
+ else if (init) {
117
+ if (Array.isArray(init))
118
+ for (const [k, v] of init)
119
+ this._list.push([String(k), String(v)]);
120
+ else
121
+ for (const k of Object.keys(init))
122
+ this._list.push([k, String(init[k])]);
123
+ }
124
+ }
125
+ append(key, value) { this._list.push([key, value]); }
126
+ delete(key) { this._list = this._list.filter(([k]) => k !== key); }
127
+ get(key) { for (const [k, v] of this._list)
128
+ if (k === key)
129
+ return v; return null; }
130
+ getAll(key) { return this._list.filter(([k]) => k === key).map(([, v]) => v); }
131
+ has(key) { return this._list.some(([k]) => k === key); }
132
+ set(key, value) {
133
+ let f = false;
134
+ for (let i = 0; i < this._list.length; i++) {
135
+ if (this._list[i][0] === key) {
136
+ if (!f) {
137
+ this._list[i][1] = value;
138
+ f = true;
139
+ }
140
+ else {
141
+ this._list.splice(i, 1);
142
+ i--;
143
+ }
144
+ }
145
+ }
146
+ if (!f)
147
+ this._list.push([key, value]);
148
+ }
149
+ sort() { this._list.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0); }
150
+ forEach(fn) { for (const [k, v] of this._list)
151
+ fn(v, k); }
152
+ *keys() { for (const [k] of this._list)
153
+ yield k; }
154
+ *values() { for (const [, v] of this._list)
155
+ yield v; }
156
+ *entries() { for (const item of this._list)
157
+ yield [item[0], item[1]]; }
158
+ *[Symbol.iterator]() { for (const item of this._list)
159
+ yield item; }
160
+ toString() { return this._list.map(([k, v]) => _encode(k) + '=' + _encode(v)).join('&'); }
161
+ get size() { return this._list.length; }
162
+ }
163
+ if (typeof globalThis.URLSearchParams === 'undefined') {
164
+ globalThis.URLSearchParams = URLSearchParamsImpl;
165
+ }
166
+ class URLImpl {
167
+ _scheme = '';
168
+ _username = '';
169
+ _password = '';
170
+ _hostname = '';
171
+ _port = '';
172
+ _pathname = '/';
173
+ _query = '';
174
+ _fragment = '';
175
+ _searchParams = new URLSearchParamsImpl();
176
+ constructor(url, base) {
177
+ let p = _parseURL(url);
178
+ if (p.scheme) {
179
+ this._apply(p);
180
+ return;
181
+ }
182
+ if (base === undefined)
183
+ throw new TypeError('Invalid URL');
184
+ const b = new URLImpl(base);
185
+ if (p.host) {
186
+ p.scheme = b._scheme;
187
+ this._apply(p);
188
+ return;
189
+ }
190
+ if (!p.path && !p.query) {
191
+ this._apply({
192
+ scheme: b._scheme, user: b._username, pass: b._password,
193
+ host: b._hostname, port: b._port,
194
+ path: b._pathname, query: b._query, fragment: p.fragment || ''
195
+ });
196
+ return;
197
+ }
198
+ p.scheme = b._scheme;
199
+ if (!p.user)
200
+ p.user = b._username;
201
+ if (!p.pass)
202
+ p.pass = b._password;
203
+ if (!p.host)
204
+ p.host = b._hostname;
205
+ if (!p.port)
206
+ p.port = b._port;
207
+ if (!p.fragment)
208
+ p.fragment = '';
209
+ if (p.path && !p.path.startsWith('/')) {
210
+ const bd = b._pathname.substring(0, b._pathname.lastIndexOf('/') + 1);
211
+ p.path = _normalizePath(bd + p.path);
212
+ }
213
+ else if (!p.path) {
214
+ p.path = b._pathname;
215
+ if (!p.query)
216
+ p.query = b._query;
217
+ }
218
+ this._apply(p);
219
+ }
220
+ _apply(p) {
221
+ this._scheme = p.scheme;
222
+ this._username = p.user;
223
+ this._password = p.pass;
224
+ this._hostname = p.host;
225
+ this._port = p.port;
226
+ this._pathname = _normalizePath(p.path || '/');
227
+ this._query = p.query;
228
+ this._fragment = p.fragment;
229
+ this._searchParams = new URLSearchParamsImpl(this._query);
230
+ }
231
+ get href() { return this.toString(); }
232
+ get protocol() { return this._scheme + ':'; }
233
+ get hostname() { return this._hostname; }
234
+ get port() { return this._port; }
235
+ get pathname() { return this._pathname; }
236
+ get search() { return this._query ? '?' + this._query : ''; }
237
+ get hash() { return this._fragment ? '#' + this._fragment : ''; }
238
+ get host() {
239
+ const h = this._hostname;
240
+ const ipv6 = h.includes(':');
241
+ return (ipv6 ? '[' + h + ']' : h) + (this._port ? ':' + this._port : '');
242
+ }
243
+ get origin() {
244
+ const h = this._hostname;
245
+ const ipv6 = h.includes(':');
246
+ return this._scheme + '://' + (ipv6 ? '[' + h + ']' : h) + (this._port ? ':' + this._port : '');
247
+ }
248
+ get username() { return this._username; }
249
+ get password() { return this._password; }
250
+ get searchParams() { return this._searchParams; }
251
+ toString() {
252
+ let s = this._scheme + ':';
253
+ const special = ['http', 'https', 'ws', 'wss', 'ftp', 'file'];
254
+ if (this._hostname || (special.includes(this._scheme) && this._pathname.startsWith('/'))) {
255
+ s += '//';
256
+ if (this._username) {
257
+ s += _encode(this._username);
258
+ if (this._password)
259
+ s += ':' + _encode(this._password);
260
+ s += '@';
261
+ }
262
+ s += this._hostname.includes(':') ? '[' + this._hostname + ']' : this._hostname;
263
+ if (this._port)
264
+ s += ':' + this._port;
265
+ }
266
+ s += this._pathname;
267
+ if (this._query)
268
+ s += '?' + this._query;
269
+ if (this._fragment)
270
+ s += '#' + this._fragment;
271
+ return s;
272
+ }
273
+ toJSON() { return this.toString(); }
274
+ }
275
+ if (typeof globalThis.URL === 'undefined') {
276
+ globalThis.URL = URLImpl;
277
+ }
278
+ export {};