tgui-core 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 (152) hide show
  1. package/.editorconfig +10 -0
  2. package/.eslintrc.cjs +78 -0
  3. package/.gitattributes +4 -0
  4. package/.prettierrc.yml +1 -0
  5. package/.vscode/extensions.json +6 -0
  6. package/.vscode/settings.json +5 -0
  7. package/README.md +1 -0
  8. package/global.d.ts +173 -0
  9. package/package.json +25 -0
  10. package/src/assets.ts +43 -0
  11. package/src/backend.ts +369 -0
  12. package/src/common/collections.ts +349 -0
  13. package/src/common/color.ts +94 -0
  14. package/src/common/events.ts +45 -0
  15. package/src/common/exhaustive.ts +19 -0
  16. package/src/common/fp.ts +38 -0
  17. package/src/common/keycodes.ts +86 -0
  18. package/src/common/keys.ts +39 -0
  19. package/src/common/math.ts +98 -0
  20. package/src/common/perf.ts +72 -0
  21. package/src/common/random.ts +32 -0
  22. package/src/common/react.ts +65 -0
  23. package/src/common/redux.ts +196 -0
  24. package/src/common/storage.js +196 -0
  25. package/src/common/string.ts +173 -0
  26. package/src/common/timer.ts +68 -0
  27. package/src/common/type-utils.ts +41 -0
  28. package/src/common/types.ts +9 -0
  29. package/src/common/uuid.ts +24 -0
  30. package/src/common/vector.ts +51 -0
  31. package/src/components/AnimatedNumber.tsx +185 -0
  32. package/src/components/Autofocus.tsx +23 -0
  33. package/src/components/Blink.jsx +69 -0
  34. package/src/components/BlockQuote.tsx +15 -0
  35. package/src/components/BodyZoneSelector.tsx +149 -0
  36. package/src/components/Box.tsx +255 -0
  37. package/src/components/Button.tsx +415 -0
  38. package/src/components/ByondUi.jsx +121 -0
  39. package/src/components/Chart.tsx +160 -0
  40. package/src/components/Collapsible.tsx +45 -0
  41. package/src/components/ColorBox.tsx +30 -0
  42. package/src/components/Dialog.tsx +85 -0
  43. package/src/components/Dimmer.tsx +19 -0
  44. package/src/components/Divider.tsx +26 -0
  45. package/src/components/DmIcon.tsx +72 -0
  46. package/src/components/DraggableControl.jsx +282 -0
  47. package/src/components/Dropdown.tsx +246 -0
  48. package/src/components/FakeTerminal.jsx +52 -0
  49. package/src/components/FitText.tsx +99 -0
  50. package/src/components/Flex.tsx +105 -0
  51. package/src/components/Grid.tsx +44 -0
  52. package/src/components/Icon.tsx +91 -0
  53. package/src/components/Image.tsx +63 -0
  54. package/src/components/InfinitePlane.jsx +192 -0
  55. package/src/components/Input.tsx +181 -0
  56. package/src/components/KeyListener.tsx +40 -0
  57. package/src/components/Knob.tsx +185 -0
  58. package/src/components/LabeledControls.tsx +50 -0
  59. package/src/components/LabeledList.tsx +130 -0
  60. package/src/components/MenuBar.tsx +238 -0
  61. package/src/components/Modal.tsx +25 -0
  62. package/src/components/NoticeBox.tsx +48 -0
  63. package/src/components/NumberInput.tsx +328 -0
  64. package/src/components/Popper.tsx +100 -0
  65. package/src/components/ProgressBar.tsx +79 -0
  66. package/src/components/RestrictedInput.jsx +301 -0
  67. package/src/components/RoundGauge.tsx +189 -0
  68. package/src/components/Section.tsx +125 -0
  69. package/src/components/Slider.tsx +173 -0
  70. package/src/components/Stack.tsx +101 -0
  71. package/src/components/StyleableSection.tsx +30 -0
  72. package/src/components/Table.tsx +90 -0
  73. package/src/components/Tabs.tsx +90 -0
  74. package/src/components/TextArea.tsx +198 -0
  75. package/src/components/TimeDisplay.jsx +64 -0
  76. package/src/components/Tooltip.tsx +147 -0
  77. package/src/components/TrackOutsideClicks.tsx +35 -0
  78. package/src/components/VirtualList.tsx +69 -0
  79. package/src/constants.ts +355 -0
  80. package/src/debug/KitchenSink.jsx +56 -0
  81. package/src/debug/actions.js +11 -0
  82. package/src/debug/hooks.js +10 -0
  83. package/src/debug/index.ts +10 -0
  84. package/src/debug/middleware.js +86 -0
  85. package/src/debug/reducer.js +22 -0
  86. package/src/debug/selectors.js +7 -0
  87. package/src/drag.ts +280 -0
  88. package/src/events.ts +237 -0
  89. package/src/focus.ts +25 -0
  90. package/src/format.ts +173 -0
  91. package/src/hotkeys.ts +212 -0
  92. package/src/http.ts +16 -0
  93. package/src/layouts/Layout.tsx +75 -0
  94. package/src/layouts/NtosWindow.tsx +162 -0
  95. package/src/layouts/Pane.tsx +56 -0
  96. package/src/layouts/Window.tsx +227 -0
  97. package/src/renderer.ts +50 -0
  98. package/styles/base.scss +32 -0
  99. package/styles/colors.scss +92 -0
  100. package/styles/components/BlockQuote.scss +20 -0
  101. package/styles/components/Button.scss +175 -0
  102. package/styles/components/ColorBox.scss +12 -0
  103. package/styles/components/Dialog.scss +105 -0
  104. package/styles/components/Dimmer.scss +22 -0
  105. package/styles/components/Divider.scss +27 -0
  106. package/styles/components/Dropdown.scss +72 -0
  107. package/styles/components/Flex.scss +31 -0
  108. package/styles/components/Icon.scss +25 -0
  109. package/styles/components/Input.scss +68 -0
  110. package/styles/components/Knob.scss +131 -0
  111. package/styles/components/LabeledList.scss +49 -0
  112. package/styles/components/MenuBar.scss +75 -0
  113. package/styles/components/Modal.scss +14 -0
  114. package/styles/components/NoticeBox.scss +65 -0
  115. package/styles/components/NumberInput.scss +76 -0
  116. package/styles/components/ProgressBar.scss +63 -0
  117. package/styles/components/RoundGauge.scss +88 -0
  118. package/styles/components/Section.scss +143 -0
  119. package/styles/components/Slider.scss +54 -0
  120. package/styles/components/Stack.scss +59 -0
  121. package/styles/components/Table.scss +44 -0
  122. package/styles/components/Tabs.scss +144 -0
  123. package/styles/components/TextArea.scss +84 -0
  124. package/styles/components/Tooltip.scss +24 -0
  125. package/styles/functions.scss +79 -0
  126. package/styles/layouts/Layout.scss +57 -0
  127. package/styles/layouts/NtosHeader.scss +20 -0
  128. package/styles/layouts/NtosWindow.scss +26 -0
  129. package/styles/layouts/TitleBar.scss +111 -0
  130. package/styles/layouts/Window.scss +103 -0
  131. package/styles/main.scss +97 -0
  132. package/styles/reset.scss +68 -0
  133. package/styles/themes/abductor.scss +68 -0
  134. package/styles/themes/admin.scss +38 -0
  135. package/styles/themes/cardtable.scss +57 -0
  136. package/styles/themes/hackerman.scss +70 -0
  137. package/styles/themes/malfunction.scss +67 -0
  138. package/styles/themes/neutral.scss +50 -0
  139. package/styles/themes/ntOS95.scss +166 -0
  140. package/styles/themes/ntos.scss +44 -0
  141. package/styles/themes/ntos_cat.scss +148 -0
  142. package/styles/themes/ntos_darkmode.scss +44 -0
  143. package/styles/themes/ntos_lightmode.scss +67 -0
  144. package/styles/themes/ntos_spooky.scss +69 -0
  145. package/styles/themes/ntos_synth.scss +99 -0
  146. package/styles/themes/ntos_terminal.scss +112 -0
  147. package/styles/themes/paper.scss +184 -0
  148. package/styles/themes/retro.scss +72 -0
  149. package/styles/themes/spookyconsole.scss +73 -0
  150. package/styles/themes/syndicate.scss +67 -0
  151. package/styles/themes/wizard.scss +68 -0
  152. package/tsconfig.json +34 -0
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { useState } from 'react';
8
+
9
+ import { Flex, Section, Tabs } from '../components';
10
+ import { Pane, Window } from '../layouts';
11
+
12
+ const r = require.context('../stories', false, /\.stories\.jsx$/);
13
+
14
+ /**
15
+ * @returns {{
16
+ * meta: {
17
+ * title: string,
18
+ * render: () => any,
19
+ * },
20
+ * }[]}
21
+ */
22
+ const getStories = () => r.keys().map((path) => r(path));
23
+
24
+ export const KitchenSink = (props) => {
25
+ const { panel } = props;
26
+ const [theme] = useState(null);
27
+ const [pageIndex, setPageIndex] = useState(0);
28
+ const stories = getStories();
29
+ const story = stories[pageIndex];
30
+ const Layout = panel ? Pane : Window;
31
+ return (
32
+ <Layout title="Kitchen Sink" width={600} height={500} theme={theme}>
33
+ <Flex height="100%">
34
+ <Flex.Item m={1} mr={0}>
35
+ <Section fill fitted>
36
+ <Tabs vertical>
37
+ {stories.map((story, i) => (
38
+ <Tabs.Tab
39
+ key={i}
40
+ color="transparent"
41
+ selected={i === pageIndex}
42
+ onClick={() => setPageIndex(i)}
43
+ >
44
+ {story.meta.title}
45
+ </Tabs.Tab>
46
+ ))}
47
+ </Tabs>
48
+ </Section>
49
+ </Flex.Item>
50
+ <Flex.Item position="relative" grow={1}>
51
+ <Layout.Content scrollable>{story.meta.render()}</Layout.Content>
52
+ </Flex.Item>
53
+ </Flex>
54
+ </Layout>
55
+ );
56
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { createAction } from 'common/redux';
8
+
9
+ export const toggleKitchenSink = createAction('debug/toggleKitchenSink');
10
+ export const toggleDebugLayout = createAction('debug/toggleDebugLayout');
11
+ export const openExternalBrowser = createAction('debug/openExternalBrowser');
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { useSelector } from '../backend';
8
+ import { selectDebug } from './selectors';
9
+
10
+ export const useDebug = () => useSelector(selectDebug);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ export { useDebug } from './hooks';
8
+ export { KitchenSink } from './KitchenSink';
9
+ export { debugMiddleware, relayMiddleware } from './middleware';
10
+ export { debugReducer } from './reducer';
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { KEY_BACKSPACE, KEY_F10, KEY_F11, KEY_F12 } from 'common/keycodes';
8
+
9
+ import { globalEvents } from '../events';
10
+ import { acquireHotKey } from '../hotkeys';
11
+ import {
12
+ openExternalBrowser,
13
+ toggleDebugLayout,
14
+ toggleKitchenSink,
15
+ } from './actions';
16
+
17
+ // prettier-ignore
18
+ const relayedTypes = [
19
+ 'backend/update',
20
+ 'chat/message',
21
+ ];
22
+
23
+ export const debugMiddleware = (store) => {
24
+ acquireHotKey(KEY_F11);
25
+ acquireHotKey(KEY_F12);
26
+ globalEvents.on('keydown', (key) => {
27
+ if (key.code === KEY_F11) {
28
+ store.dispatch(toggleDebugLayout());
29
+ }
30
+ if (key.code === KEY_F12) {
31
+ store.dispatch(toggleKitchenSink());
32
+ }
33
+ if (key.ctrl && key.alt && key.code === KEY_BACKSPACE) {
34
+ // NOTE: We need to call this in a timeout, because we need a clean
35
+ // stack in order for this to be a fatal error.
36
+ setTimeout(() => {
37
+ // prettier-ignore
38
+ throw new Error(
39
+ 'OOPSIE WOOPSIE!! UwU We made a fucky wucky!! A wittle'
40
+ + ' fucko boingo! The code monkeys at our headquarters are'
41
+ + ' working VEWY HAWD to fix this!');
42
+ });
43
+ }
44
+ });
45
+ return (next) => (action) => next(action);
46
+ };
47
+
48
+ export const relayMiddleware = (store) => {
49
+ const devServer = require('tgui-dev-server/link/client.cjs');
50
+ const externalBrowser = location.search === '?external';
51
+ if (externalBrowser) {
52
+ devServer.subscribe((msg) => {
53
+ const { type, payload } = msg;
54
+ if (type === 'relay' && payload.windowId === Byond.windowId) {
55
+ store.dispatch({
56
+ ...payload.action,
57
+ relayed: true,
58
+ });
59
+ }
60
+ });
61
+ } else {
62
+ acquireHotKey(KEY_F10);
63
+ globalEvents.on('keydown', (key) => {
64
+ if (key === KEY_F10) {
65
+ store.dispatch(openExternalBrowser());
66
+ }
67
+ });
68
+ }
69
+ return (next) => (action) => {
70
+ const { type, payload, relayed } = action;
71
+ if (type === openExternalBrowser.type) {
72
+ window.open(location.href + '?external', '_blank');
73
+ return;
74
+ }
75
+ if (relayedTypes.includes(type) && !relayed && !externalBrowser) {
76
+ devServer.sendMessage({
77
+ type: 'relay',
78
+ payload: {
79
+ windowId: Byond.windowId,
80
+ action,
81
+ },
82
+ });
83
+ }
84
+ return next(action);
85
+ };
86
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ export const debugReducer = (state = {}, action) => {
8
+ const { type, payload } = action;
9
+ if (type === 'debug/toggleKitchenSink') {
10
+ return {
11
+ ...state,
12
+ kitchenSink: !state.kitchenSink,
13
+ };
14
+ }
15
+ if (type === 'debug/toggleDebugLayout') {
16
+ return {
17
+ ...state,
18
+ debugLayout: !state.debugLayout,
19
+ };
20
+ }
21
+ return state;
22
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ export const selectDebug = (state) => state.debug;
package/src/drag.ts ADDED
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @file
3
+ * @copyright 2020 Aleksej Komarov
4
+ * @license MIT
5
+ */
6
+
7
+ import { storage } from './common/storage';
8
+ import { vecAdd, vecMultiply, vecScale, vecSubtract } from './common/vector';
9
+
10
+ const pixelRatio = window.devicePixelRatio ?? 1;
11
+ let windowKey = Byond.windowId;
12
+ let dragging = false;
13
+ let resizing = false;
14
+ let screenOffset: [number, number] = [0, 0];
15
+ let screenOffsetPromise: Promise<[number, number]>;
16
+ let dragPointOffset: [number, number];
17
+ let resizeMatrix: [number, number];
18
+ let initialSize: [number, number];
19
+ let size: [number, number];
20
+
21
+ // Set the window key
22
+ export const setWindowKey = (key: string): void => {
23
+ windowKey = key;
24
+ };
25
+
26
+ // Get window position
27
+ export const getWindowPosition = (): [number, number] => [
28
+ window.screenLeft * pixelRatio,
29
+ window.screenTop * pixelRatio,
30
+ ];
31
+
32
+ // Get window size
33
+ export const getWindowSize = (): [number, number] => [
34
+ window.innerWidth * pixelRatio,
35
+ window.innerHeight * pixelRatio,
36
+ ];
37
+
38
+ // Set window position
39
+ const setWindowPosition = (vec: [number, number]) => {
40
+ const byondPos = vecAdd(vec, screenOffset);
41
+ return Byond.winset(Byond.windowId, {
42
+ pos: byondPos[0] + ',' + byondPos[1],
43
+ });
44
+ };
45
+
46
+ // Set window size
47
+ const setWindowSize = (vec: [number, number]) => {
48
+ return Byond.winset(Byond.windowId, {
49
+ size: vec[0] + 'x' + vec[1],
50
+ });
51
+ };
52
+
53
+ // Get screen position
54
+ const getScreenPosition = (): [number, number] => [
55
+ 0 - screenOffset[0],
56
+ 0 - screenOffset[1],
57
+ ];
58
+
59
+ // Get screen size
60
+ const getScreenSize = (): [number, number] => [
61
+ window.screen.availWidth * pixelRatio,
62
+ window.screen.availHeight * pixelRatio,
63
+ ];
64
+
65
+ /**
66
+ * Moves an item to the top of the recents array, and keeps its length
67
+ * limited to the number in `limit` argument.
68
+ *
69
+ * Uses a strict equality check for comparisons.
70
+ *
71
+ * Returns new recents and an item which was trimmed.
72
+ */
73
+ export const touchRecents = (
74
+ recents: string[],
75
+ touchedItem: string,
76
+ limit = 50
77
+ ): [string[], string | undefined] => {
78
+ const nextRecents: string[] = [touchedItem];
79
+ let trimmedItem: string | undefined;
80
+ for (let i = 0; i < recents.length; i++) {
81
+ const item = recents[i];
82
+ if (item === touchedItem) {
83
+ continue;
84
+ }
85
+ if (nextRecents.length < limit) {
86
+ nextRecents.push(item);
87
+ } else {
88
+ trimmedItem = item;
89
+ }
90
+ }
91
+ return [nextRecents, trimmedItem];
92
+ };
93
+
94
+ // Store window geometry in local storage
95
+ const storeWindowGeometry = async () => {
96
+ const geometry = {
97
+ pos: getWindowPosition(),
98
+ size: getWindowSize(),
99
+ };
100
+ storage.set(windowKey, geometry);
101
+ // Update the list of stored geometries
102
+ const [geometries, trimmedKey] = touchRecents(
103
+ (await storage.get('geometries')) || [],
104
+ windowKey
105
+ );
106
+ if (trimmedKey) {
107
+ storage.remove(trimmedKey);
108
+ }
109
+ storage.set('geometries', geometries);
110
+ };
111
+
112
+ // Recall window geometry from local storage and apply it
113
+ export const recallWindowGeometry = async (
114
+ options: {
115
+ fancy?: boolean;
116
+ pos?: [number, number];
117
+ size?: [number, number];
118
+ locked?: boolean;
119
+ } = {}
120
+ ) => {
121
+ const geometry = options.fancy && (await storage.get(windowKey));
122
+ // options.pos is assumed to already be in display-pixels
123
+ let pos = geometry?.pos || options.pos;
124
+ let size = options.size;
125
+ // Convert size from css-pixels to display-pixels
126
+ if (size) {
127
+ size = [size[0] * pixelRatio, size[1] * pixelRatio];
128
+ }
129
+ // Wait until screen offset gets resolved
130
+ await screenOffsetPromise;
131
+ const areaAvailable = getScreenSize();
132
+ // Set window size
133
+ if (size) {
134
+ // Constraint size to not exceed available screen area
135
+ size = [
136
+ Math.min(areaAvailable[0], size[0]),
137
+ Math.min(areaAvailable[1], size[1]),
138
+ ];
139
+ setWindowSize(size);
140
+ }
141
+ // Set window position
142
+ if (pos) {
143
+ // Constraint window position if monitor lock was set in preferences.
144
+ if (size && options.locked) {
145
+ pos = constraintPosition(pos, size)[1];
146
+ }
147
+ setWindowPosition(pos);
148
+ // Set window position at the center of the screen.
149
+ } else if (size) {
150
+ pos = vecAdd(
151
+ vecScale(areaAvailable, 0.5),
152
+ vecScale(size, -0.5),
153
+ vecScale(screenOffset, -1.0)
154
+ );
155
+ setWindowPosition(pos);
156
+ }
157
+ };
158
+
159
+ // Setup draggable window
160
+ export const setupDrag = async () => {
161
+ // Calculate screen offset caused by the windows taskbar
162
+ let windowPosition = getWindowPosition();
163
+
164
+ screenOffsetPromise = Byond.winget(Byond.windowId, 'pos').then((pos) => [
165
+ pos.x - windowPosition[0],
166
+ pos.y - windowPosition[1],
167
+ ]);
168
+ screenOffset = await screenOffsetPromise;
169
+ };
170
+
171
+ /**
172
+ * Constraints window position to safe screen area, accounting for safe
173
+ * margins which could be a system taskbar.
174
+ */
175
+ const constraintPosition = (
176
+ pos: [number, number],
177
+ size: [number, number]
178
+ ): [boolean, [number, number]] => {
179
+ const screenPos = getScreenPosition();
180
+ const screenSize = getScreenSize();
181
+ const nextPos: [number, number] = [pos[0], pos[1]];
182
+ let relocated = false;
183
+ for (let i = 0; i < 2; i++) {
184
+ const leftBoundary = screenPos[i];
185
+ const rightBoundary = screenPos[i] + screenSize[i];
186
+ if (pos[i] < leftBoundary) {
187
+ nextPos[i] = leftBoundary;
188
+ relocated = true;
189
+ } else if (pos[i] + size[i] > rightBoundary) {
190
+ nextPos[i] = rightBoundary - size[i];
191
+ relocated = true;
192
+ }
193
+ }
194
+ return [relocated, nextPos];
195
+ };
196
+
197
+ // Start dragging the window
198
+ export const dragStartHandler = (event) => {
199
+ dragging = true;
200
+ dragPointOffset = vecSubtract(
201
+ [event.screenX, event.screenY],
202
+ getWindowPosition()
203
+ ) as [number, number];
204
+ // Focus click target
205
+ (event.target as HTMLElement)?.focus();
206
+ document.addEventListener('mousemove', dragMoveHandler);
207
+ document.addEventListener('mouseup', dragEndHandler);
208
+ dragMoveHandler(event);
209
+ };
210
+
211
+ // End dragging the window
212
+ const dragEndHandler = (event) => {
213
+ dragMoveHandler(event);
214
+ document.removeEventListener('mousemove', dragMoveHandler);
215
+ document.removeEventListener('mouseup', dragEndHandler);
216
+ dragging = false;
217
+ storeWindowGeometry();
218
+ };
219
+
220
+ // Move the window while dragging
221
+ const dragMoveHandler = (event: MouseEvent) => {
222
+ if (!dragging) {
223
+ return;
224
+ }
225
+ event.preventDefault();
226
+ setWindowPosition(
227
+ vecSubtract([event.screenX, event.screenY], dragPointOffset) as [
228
+ number,
229
+ number
230
+ ]
231
+ );
232
+ };
233
+
234
+ // Start resizing the window
235
+ export const resizeStartHandler =
236
+ (x: number, y: number) => (event: MouseEvent) => {
237
+ resizeMatrix = [x, y];
238
+ resizing = true;
239
+ dragPointOffset = vecSubtract(
240
+ [event.screenX, event.screenY],
241
+ getWindowPosition()
242
+ ) as [number, number];
243
+ initialSize = getWindowSize();
244
+ // Focus click target
245
+ (event.target as HTMLElement)?.focus();
246
+ document.addEventListener('mousemove', resizeMoveHandler);
247
+ document.addEventListener('mouseup', resizeEndHandler);
248
+ resizeMoveHandler(event);
249
+ };
250
+
251
+ // End resizing the window
252
+ const resizeEndHandler = (event: MouseEvent) => {
253
+ resizeMoveHandler(event);
254
+ document.removeEventListener('mousemove', resizeMoveHandler);
255
+ document.removeEventListener('mouseup', resizeEndHandler);
256
+ resizing = false;
257
+ storeWindowGeometry();
258
+ };
259
+
260
+ // Move the window while resizing
261
+ const resizeMoveHandler = (event: MouseEvent) => {
262
+ if (!resizing) {
263
+ return;
264
+ }
265
+ event.preventDefault();
266
+ const currentOffset = vecSubtract(
267
+ [event.screenX, event.screenY],
268
+ getWindowPosition()
269
+ );
270
+ const delta = vecSubtract(currentOffset, dragPointOffset);
271
+ // Extra 1x1 area is added to ensure the browser can see the cursor
272
+ size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]) as [
273
+ number,
274
+ number
275
+ ];
276
+ // Sane window size values
277
+ size[0] = Math.max(size[0], 150 * pixelRatio);
278
+ size[1] = Math.max(size[1], 50 * pixelRatio);
279
+ setWindowSize(size);
280
+ };