swing-ui 0.1.25 → 1.0.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 (116) hide show
  1. package/LICENSE +5 -4
  2. package/README.md +144 -7
  3. package/SwingUI.js +2611 -0
  4. package/classes/Arc.js +185 -0
  5. package/classes/Button.js +55 -0
  6. package/classes/CheckBox.js +33 -0
  7. package/classes/CheckBoxMenuItem.js +24 -0
  8. package/classes/ComboBox.js +329 -0
  9. package/classes/ComponentContainer.js +828 -0
  10. package/classes/ContextMenu.js +141 -0
  11. package/classes/DocumentEditor.js +168 -0
  12. package/classes/Ellipse.js +76 -0
  13. package/classes/InternalWindow.js +397 -0
  14. package/classes/InternalWindowContainer.js +93 -0
  15. package/classes/Label.js +52 -0
  16. package/classes/Line.js +105 -0
  17. package/classes/List.js +452 -0
  18. package/classes/MediaPlayer.js +504 -0
  19. package/classes/Menu.js +74 -0
  20. package/classes/MenuBar.js +56 -0
  21. package/classes/MenuItem.js +92 -0
  22. package/classes/Panel.js +50 -0
  23. package/classes/PasswordField.js +47 -0
  24. package/classes/Polygon.js +236 -0
  25. package/classes/Polyline.js +240 -0
  26. package/classes/ProgressBar.js +62 -0
  27. package/classes/RadioButton.js +110 -0
  28. package/classes/RadioButtonMenuItem.js +106 -0
  29. package/classes/Rectangle.js +199 -0
  30. package/classes/ScrollPane.js +53 -0
  31. package/classes/ScrollableComponent.js +141 -0
  32. package/classes/Separator.js +24 -0
  33. package/classes/Shape.js +584 -0
  34. package/classes/ShapeContainer.js +231 -0
  35. package/classes/Slider.js +172 -0
  36. package/classes/Spinner.js +172 -0
  37. package/classes/SplitView.js +82 -0
  38. package/classes/Stroke.js +115 -0
  39. package/classes/Table.js +982 -0
  40. package/classes/Tabs.js +228 -0
  41. package/classes/TextArea.js +125 -0
  42. package/classes/TextComponent.js +207 -0
  43. package/classes/TextField.js +128 -0
  44. package/classes/TextShape.js +123 -0
  45. package/classes/ToggleButton.js +147 -0
  46. package/classes/ToolBar.js +120 -0
  47. package/classes/TrayIcon.js +54 -0
  48. package/classes/Tree.js +569 -0
  49. package/classes/UIComponent.js +2113 -0
  50. package/classes/Window.js +609 -0
  51. package/enums.js +298 -0
  52. package/fonts.conf +32 -0
  53. package/functions.js +254 -0
  54. package/icon.png +0 -0
  55. package/index.js +109 -4
  56. package/jar/gui-11.jar +0 -0
  57. package/jar/lib/flatlaf.jar +0 -0
  58. package/jar/lib/jna.jar +0 -0
  59. package/package.json +58 -33
  60. package/typedefs.js +973 -0
  61. package/types/SwingUI.d.ts +727 -0
  62. package/types/classes/Arc.d.ts +111 -0
  63. package/types/classes/Button.d.ts +38 -0
  64. package/types/classes/CheckBox.d.ts +22 -0
  65. package/types/classes/CheckBoxMenuItem.d.ts +18 -0
  66. package/types/classes/ComboBox.d.ts +172 -0
  67. package/types/classes/ComponentContainer.d.ts +131 -0
  68. package/types/classes/ContextMenu.d.ts +74 -0
  69. package/types/classes/DocumentEditor.d.ts +95 -0
  70. package/types/classes/Ellipse.d.ts +34 -0
  71. package/types/classes/InternalWindow.d.ts +163 -0
  72. package/types/classes/InternalWindowContainer.d.ts +36 -0
  73. package/types/classes/Label.d.ts +33 -0
  74. package/types/classes/Line.d.ts +48 -0
  75. package/types/classes/List.d.ts +223 -0
  76. package/types/classes/MediaPlayer.d.ts +381 -0
  77. package/types/classes/Menu.d.ts +43 -0
  78. package/types/classes/MenuBar.d.ts +26 -0
  79. package/types/classes/MenuItem.d.ts +57 -0
  80. package/types/classes/Panel.d.ts +28 -0
  81. package/types/classes/PasswordField.d.ts +22 -0
  82. package/types/classes/Polygon.d.ts +93 -0
  83. package/types/classes/Polyline.d.ts +96 -0
  84. package/types/classes/ProgressBar.d.ts +36 -0
  85. package/types/classes/RadioButton.d.ts +70 -0
  86. package/types/classes/RadioButtonMenuItem.d.ts +71 -0
  87. package/types/classes/Rectangle.d.ts +82 -0
  88. package/types/classes/ScrollPane.d.ts +23 -0
  89. package/types/classes/ScrollableComponent.d.ts +81 -0
  90. package/types/classes/Separator.d.ts +14 -0
  91. package/types/classes/Shape.d.ts +223 -0
  92. package/types/classes/ShapeContainer.d.ts +92 -0
  93. package/types/classes/Slider.d.ts +107 -0
  94. package/types/classes/Spinner.d.ts +129 -0
  95. package/types/classes/SplitView.d.ts +47 -0
  96. package/types/classes/Stroke.d.ts +99 -0
  97. package/types/classes/Table.d.ts +652 -0
  98. package/types/classes/Tabs.d.ts +118 -0
  99. package/types/classes/TextArea.d.ts +45 -0
  100. package/types/classes/TextComponent.d.ts +136 -0
  101. package/types/classes/TextField.d.ts +94 -0
  102. package/types/classes/TextShape.d.ts +73 -0
  103. package/types/classes/ToggleButton.d.ts +84 -0
  104. package/types/classes/ToolBar.d.ts +82 -0
  105. package/types/classes/TrayIcon.d.ts +43 -0
  106. package/types/classes/Tree.d.ts +354 -0
  107. package/types/classes/UIComponent.d.ts +945 -0
  108. package/types/classes/Window.d.ts +334 -0
  109. package/types/enums.d.ts +437 -0
  110. package/types/functions.d.ts +109 -0
  111. package/types/index.d.ts +188 -0
  112. package/types/typedefs.d.ts +1626 -0
  113. package/ui-process-manager.js +317 -0
  114. package/video-devices-linux.sh +72 -0
  115. package/video-devices-mac.sh +33 -0
  116. package/CHANGES.md +0 -3
@@ -0,0 +1,317 @@
1
+ /**
2
+ * IPC Proxy worker
3
+ */
4
+
5
+ import {parentPort, workerData} from "node:worker_threads";
6
+ import path from "node:path";
7
+
8
+ const env = workerData.runtime === "deno" ? Deno.env.toObject() : process.env;
9
+ const responsePort = workerData.responsePort;
10
+ const sharedBuffSignal = workerData.signalBuffer;
11
+
12
+ const WorkerSignalIndex = {ExecutionControl: 0, WorkerControl: 1, DataLength: 2, ReceivedBytes: 3, SentBytes: 4};
13
+ const WorkerSignal = {Resume: 0, Pause: 1, SendLine: 2, Block: 3, Unblock: 4, SendBytes: 5};
14
+
15
+ const decoder = new TextDecoder("utf-8");
16
+ const encoder = new TextEncoder();
17
+
18
+ let jarProcess = null;
19
+ let stdinWriter = null; // writer needed for Deno spawned process
20
+ let ready = false;
21
+ let lineBuffer = "";
22
+
23
+ let syncMode = false;
24
+ let exiting = false;
25
+
26
+ if(workerData.runtime === "bun")
27
+ {
28
+ console.log = (...args) => {
29
+ if(typeof args[0] === "object")
30
+ {
31
+ if(args[0] instanceof Error)
32
+ {
33
+ args[0] = args[0].stack;
34
+ }
35
+ else
36
+ {
37
+ args[0] = args[0].toString();
38
+ }
39
+ }
40
+ process.stdout.write(`${args[0]}\n`);
41
+ };
42
+ console.error = console.log;
43
+ }
44
+ else
45
+ {
46
+ // Override console.log, console.error, etc. for logging to the main thread
47
+ ["log", "error", "warn", "info", "debug"].forEach(method => {
48
+ console[method] = (...args) => {
49
+ parentPort.postMessage({type: `worker_${method}`, data: args[0]});
50
+ };
51
+ });
52
+ }
53
+
54
+ async function startJar()
55
+ {
56
+ let JAR_PATH = path.join(workerData.moduleDir, "jar/gui-11.jar");
57
+ let JAVA_BIN = env.JAVA_HOME ? path.join(env.JAVA_HOME, workerData.os === "windows" ? "java.exe" : "java") : "java"; // build custom path from specified dir for each OS, or just use default command.
58
+ let JAVA_OPTS = (env.JAVA_OPTS ?? "").split(" ").filter(Boolean);
59
+ if(JAVA_OPTS.indexOf("-Dfile.encoding=UTF-8") === -1) JAVA_OPTS.push("-Dfile.encoding=UTF-8"); // important
60
+ if(workerData.debugJava) JAVA_OPTS.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"); // for debugging JAR
61
+ if(workerData.os === "linux") JAVA_OPTS.push("--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED"); // to be able to set WM_CLASS to project name in Java GUI
62
+
63
+ const _opts = [...JAVA_OPTS, "-jar", JAR_PATH, "--root-dir", workerData.rootDir, "--module-dir", workerData.moduleDir];
64
+
65
+ if(workerData.verbose) _opts.push("--v");
66
+
67
+ try
68
+ {
69
+ if(workerData.runtime === "bun")
70
+ {
71
+ _opts.unshift(JAVA_BIN);
72
+
73
+ try
74
+ {
75
+ jarProcess = Bun.spawn({
76
+ cmd: _opts,
77
+ stdin: "pipe",
78
+ stdout: "pipe",
79
+ stderr: "pipe",
80
+ env: env
81
+ });
82
+ }catch(ex)
83
+ {
84
+ console.error("Process Spawn Failed: ");
85
+ console.error(ex);
86
+ return;
87
+ }
88
+
89
+ jarProcess.exited.then((code) => {
90
+ if(!exiting) console.error(`[jar-worker] GUI exited with code ${code}`);
91
+ ready = false;
92
+ parentPort.postMessage({type: "exit", code});
93
+ });
94
+
95
+ // Read stdout
96
+ (async () => {
97
+ for await (const chunk of jarProcess.stdout)
98
+ {
99
+ lineBuffer += decoder.decode(chunk);
100
+ let i;
101
+ while((i = lineBuffer.indexOf("\n")) !== -1)
102
+ {
103
+ const line = lineBuffer.slice(0, i).trim();
104
+ lineBuffer = lineBuffer.slice(i + 1);
105
+ if(line.length > 0)
106
+ {
107
+ if(line.startsWith("eyJ")) // line.startsWith("{\"")
108
+ {
109
+ handleJarLine(line);
110
+ }
111
+ else
112
+ {
113
+ console.log(`GUI Process: ${line}`);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ })();
119
+
120
+ // Read stderr
121
+ (async () => {
122
+ for await (const chunk of jarProcess.stderr)
123
+ {
124
+ console.error(decoder.decode(chunk));
125
+ }
126
+ })();
127
+ }
128
+ else if(workerData.runtime === "node")
129
+ {
130
+ const {spawn} = await import("node:child_process");
131
+
132
+ jarProcess = spawn(
133
+ JAVA_BIN,
134
+ _opts,
135
+ {
136
+ stdin: "pipe",
137
+ stdout: "pipe",
138
+ stderr: "pipe",
139
+ env: env
140
+ });
141
+
142
+ jarProcess.stdout.setEncoding("utf8");
143
+ jarProcess.stderr.setEncoding("utf8");
144
+
145
+ // Receive data from JAVA process
146
+ jarProcess.stdout.on("data", data => {
147
+ lineBuffer += data.toString();
148
+ let i;
149
+ while((i = lineBuffer.indexOf("\n")) !== -1)
150
+ {
151
+ const line = lineBuffer.slice(0, i).trim();
152
+ lineBuffer = lineBuffer.slice(i + 1);
153
+ if(line.length > 0)
154
+ {
155
+ if(line.startsWith("eyJ")) //line.startsWith("{\"")
156
+ {
157
+ handleJarLine(line);
158
+ }
159
+ else
160
+ {
161
+ console.log(`GUI Process: ${line}`);
162
+ }
163
+ }
164
+ }
165
+ });
166
+
167
+ jarProcess.stderr.on("data", data => {
168
+ console.error(data.toString());
169
+ });
170
+
171
+ jarProcess.on("close", (code) => {
172
+ console.error(`[jar-worker] GUI exited with code ${code ?? 0}`);
173
+ jarProcess = null;
174
+ ready = false;
175
+ parentPort.postMessage({type: "exit", code: code ?? 0});
176
+ });
177
+ }
178
+ else if(workerData.runtime === "deno")
179
+ {
180
+ const command = new Deno.Command(JAVA_BIN, {
181
+ args: _opts,
182
+ stdin: "piped",
183
+ stdout: "piped",
184
+ stderr: "piped",
185
+ env: env
186
+ });
187
+
188
+ jarProcess = command.spawn();
189
+ stdinWriter = jarProcess.stdin.getWriter();
190
+
191
+ jarProcess.status.then((status) => {
192
+ console.error(`[jar-worker] GUI exited with code ${status.code}`);
193
+ jarProcess = null;
194
+ ready = false;
195
+ parentPort.postMessage({type: "exit", code: status.code});
196
+ });
197
+
198
+ // Read stdout
199
+ (async () => {
200
+ const reader = jarProcess.stdout.getReader();
201
+ while(true)
202
+ {
203
+ const {done, value} = await reader.read();
204
+ if(done) break;
205
+
206
+ lineBuffer += decoder.decode(value);
207
+ let i;
208
+ while((i = lineBuffer.indexOf("\n")) !== -1)
209
+ {
210
+ const line = lineBuffer.slice(0, i).trim();
211
+ lineBuffer = lineBuffer.slice(i + 1);
212
+ if(line.length > 0)
213
+ {
214
+ if(line.startsWith("eyJ")) //line.startsWith("{\"")
215
+ {
216
+ handleJarLine(line);
217
+ }
218
+ else
219
+ {
220
+ console.log(`GUI Process: ${line}`);
221
+ }
222
+ }
223
+ }
224
+ }
225
+ })();
226
+
227
+ // Read stderr
228
+ (async () => {
229
+ const reader = jarProcess.stderr.getReader();
230
+ while(true)
231
+ {
232
+ const {done, value} = await reader.read();
233
+ if(done) break;
234
+ console.error(decoder.decode(value));
235
+ }
236
+ })();
237
+ }
238
+ }catch(ex)
239
+ {
240
+ console.error(ex);
241
+ }
242
+
243
+ ready = true;
244
+ }
245
+
246
+ function sendLineToJar(line)
247
+ {
248
+ switch(workerData.runtime)
249
+ {
250
+ case "deno":
251
+ {
252
+ stdinWriter.write(encoder.encode(line));
253
+ stdinWriter.write(encoder.encode("\n"));
254
+ break;
255
+ }
256
+ default:
257
+ {
258
+ jarProcess.stdin.write(line);
259
+ jarProcess.stdin.write("\n");
260
+ }
261
+ }
262
+ }
263
+
264
+ function handleJarLine(line)
265
+ {
266
+ if(line.trim().length === 0) return; // ignore empty lines
267
+
268
+ line = Buffer.from(line, "base64").toString("utf8");
269
+
270
+ // send it as a response if requested
271
+ if(syncMode && (line.indexOf("\"response\":") > 0 || line.indexOf("%22response%22%3A") > 0)) // check if response JSON object string has response property in it "\"response\":"
272
+ {
273
+ responsePort.postMessage(line);
274
+ syncMode = false;
275
+
276
+ // unblock main thread
277
+ Atomics.store(sharedBuffSignal, WorkerSignalIndex.ExecutionControl, WorkerSignal.Unblock);
278
+ Atomics.notify(sharedBuffSignal, WorkerSignalIndex.ExecutionControl);
279
+ }
280
+ else
281
+ {
282
+ parentPort.postMessage({type: "data", data: line});
283
+ }
284
+ }
285
+
286
+ parentPort.on("message", (msg) => {
287
+ if(!msg) return;
288
+
289
+ if("data" in msg)
290
+ {
291
+ // Enter sync mode before writing so no incoming lines are missed
292
+ if(msg.respond) syncMode = true;
293
+ sendLineToJar(msg.data);
294
+ }
295
+ // Config override: { type: "__config", jarPath?, javaBin?, javaOpts? }
296
+ else if(msg.type === "__config")
297
+ {
298
+ if(msg.jarPath) JAR_PATH = msg.jarPath;
299
+ if(msg.javaBin) JAVA_BIN = msg.javaBin;
300
+ if(msg.javaOpts) JAVA_OPTS = msg.javaOpts;
301
+ console.log(`[jar-worker] Config updated — jar: ${JAR_PATH}`);
302
+ }
303
+ // Shutdown: { type: "__shutdown" }
304
+ else if(msg.type === "__shutdown")
305
+ {
306
+ exiting = true;
307
+ console.log("[jar-worker] Shutdown requested");
308
+ jarProcess?.kill("SIGTERM");
309
+ }
310
+ });
311
+
312
+ await startJar();
313
+ ready = true;
314
+
315
+ // unblock main thread
316
+ Atomics.store(sharedBuffSignal, WorkerSignalIndex.ExecutionControl, WorkerSignal.Unblock);
317
+ Atomics.notify(sharedBuffSignal, WorkerSignalIndex.ExecutionControl);
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+
3
+ # Gets video devices and their formats (requires ffmpeg installed)
4
+
5
+ get_fps() {
6
+ local dev=$1
7
+ local res=$2
8
+ local fmt=$3
9
+ local fps=""
10
+
11
+ if [ "$fmt" = "yuyv422" ]; then
12
+ stream_line=$(ffmpeg -hide_banner -f v4l2 -video_size "$res" -pix_fmt "$fmt" -i "$dev" -t 1 -y /dev/null 2>&1 | grep "Stream #0:0")
13
+ else
14
+ stream_line=$(ffmpeg -hide_banner -f v4l2 -video_size "$res" -codec "$fmt" -i "$dev" -t 1 -y /dev/null 2>&1 | grep "Stream #0:0")
15
+ fi
16
+
17
+ fps=$(echo "$stream_line" | grep -oP ', \K[0-9.]+(?= fps)')
18
+ echo "$fps"
19
+ }
20
+
21
+ echo "["
22
+
23
+ for dev in /dev/video*; do
24
+ [ -e "$dev" ] || continue
25
+ dev_id=$(basename "$dev")
26
+ dev_name=$(cat "/sys/class/video4linux/$dev_id/name")
27
+ ffmpeg_output=$(ffmpeg -hide_banner -list_formats all -f v4l2 -i "$dev" 2>&1)
28
+
29
+ # Include both Raw and Compressed lines
30
+ format_lines=$(echo "$ffmpeg_output" | grep -E "^\[video4linux2.*(Raw|Compressed)")
31
+
32
+ # Remove the [ ... ] part first
33
+ clean_lines=$(echo "$format_lines" | sed -E 's/\[[^]]*\]//g')
34
+
35
+ [ -z "$clean_lines" ] && continue
36
+
37
+ echo " {\"id\":\"$dev\",\"name\":\"$dev_name\",\"formats\":["
38
+
39
+ while IFS= read -r line; do
40
+ # Determine if it's Raw or Compressed
41
+ format_type=""
42
+ if echo "$line" | grep -q "Raw"; then
43
+ format_type="raw"
44
+ elif echo "$line" | grep -q "Compressed"; then
45
+ format_type="compressed"
46
+ fi
47
+
48
+ # Extract format (2nd field after removing brackets)
49
+ fmt=$(echo "$line" | cut -d':' -f2 | xargs)
50
+ # Extract resolutions
51
+ resolutions=$(echo "$line" | grep -oP '\d+x\d+')
52
+
53
+ for res in $resolutions; do
54
+ fps=$(get_fps "$dev" "$res" "$fmt")
55
+ [ -z "$fps" ] && fps="?"
56
+
57
+ # Output with appropriate prefix based on format type
58
+ if [ "$format_type" = "raw" ]; then
59
+ echo " {\"pixel_format\":\"$fmt\",\"size\":\"$res\",\"min_fps\":$fps,\"max_fps\":$fps},"
60
+ elif [ "$format_type" = "compressed" ]; then
61
+ echo " {\"vcodec\":\"$fmt\",\"size\":\"$res\",\"min_fps\":$fps,\"max_fps\":$fps},"
62
+ else
63
+ echo " {\"format\":\"UNKNOWN\",\"size\":\"$res\",\"min_fps\":$fps,\"max_fps\":$fps},"
64
+ fi
65
+ done
66
+ done <<< "$clean_lines"
67
+
68
+ echo " ]"
69
+ echo " },"
70
+ done
71
+
72
+ echo "]"
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ # Common resolutions to test
4
+ resolutions=("1920x1080" "1280x720" "1024x576" "960x540" "848x480" "640x480" "640x360" "320x240")
5
+
6
+ # Common framerates to test
7
+ framerates=("60" "30" "24", "15", "10", "5", "3", "2", "1")
8
+
9
+ echo "Listing macOS video devices..."
10
+
11
+ # Get device list
12
+ device_list=$(ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep "AVFoundation video devices" -A 50 | grep -E "^\[.*\] \[[0-9]+\]" | sed -E 's/.*\[(.*)\] (.*)/\1|\2/')
13
+
14
+ # Loop through devices
15
+ echo "$device_list" | while IFS="|" read -r id name; do
16
+ # Skip empty lines
17
+ [ -z "$id" ] && continue
18
+ [ -z "$name" ] && continue
19
+
20
+ echo "DEVICE|video$id|$name"
21
+
22
+ for res in "${resolutions[@]}"; do
23
+ for fps in "${framerates[@]}"; do
24
+ # Try ffmpeg capture for 1 second
25
+ result=$(ffmpeg -f avfoundation -framerate "$fps" -video_size "$res" -i "$id" -t 1 -y /dev/null 2>&1 | grep "Stream #0:0")
26
+ if [ -n "$result" ]; then
27
+ echo "RES|avfoundation|$res|$fps"
28
+ fi
29
+ done
30
+ done
31
+
32
+ echo "---"
33
+ done
package/CHANGES.md DELETED
@@ -1,3 +0,0 @@
1
- 0.1.25
2
-
3
- - Preparing for release