@code-yeongyu/senpi-tui 2026.9.12-3 → 2026.9.12

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 (94) hide show
  1. package/README.md +4 -53
  2. package/dist/alt-screen-search.d.ts +1 -21
  3. package/dist/alt-screen-search.d.ts.map +1 -1
  4. package/dist/alt-screen-search.js +42 -199
  5. package/dist/alt-screen-search.js.map +1 -1
  6. package/dist/components/box.d.ts +1 -3
  7. package/dist/components/box.d.ts.map +1 -1
  8. package/dist/components/box.js +0 -28
  9. package/dist/components/box.js.map +1 -1
  10. package/dist/components/editor.d.ts +1 -6
  11. package/dist/components/editor.d.ts.map +1 -1
  12. package/dist/components/editor.js +18 -98
  13. package/dist/components/editor.js.map +1 -1
  14. package/dist/components/input.d.ts +1 -12
  15. package/dist/components/input.d.ts.map +1 -1
  16. package/dist/components/input.js +6 -40
  17. package/dist/components/input.js.map +1 -1
  18. package/dist/components/latex.d.ts.map +1 -1
  19. package/dist/components/latex.js +0 -7
  20. package/dist/components/latex.js.map +1 -1
  21. package/dist/components/loader.d.ts +0 -3
  22. package/dist/components/loader.d.ts.map +1 -1
  23. package/dist/components/loader.js +8 -20
  24. package/dist/components/loader.js.map +1 -1
  25. package/dist/components/scroll-view.d.ts +3 -6
  26. package/dist/components/scroll-view.d.ts.map +1 -1
  27. package/dist/components/scroll-view.js +1 -6
  28. package/dist/components/scroll-view.js.map +1 -1
  29. package/dist/components/select-list.d.ts +1 -4
  30. package/dist/components/select-list.d.ts.map +1 -1
  31. package/dist/components/select-list.js +2 -48
  32. package/dist/components/select-list.js.map +1 -1
  33. package/dist/components/settings-list.d.ts +1 -5
  34. package/dist/components/settings-list.d.ts.map +1 -1
  35. package/dist/components/settings-list.js +5 -54
  36. package/dist/components/settings-list.js.map +1 -1
  37. package/dist/index.d.ts +1 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +0 -3
  40. package/dist/index.js.map +1 -1
  41. package/dist/layout.d.ts +1 -3
  42. package/dist/layout.d.ts.map +1 -1
  43. package/dist/layout.js +9 -32
  44. package/dist/layout.js.map +1 -1
  45. package/dist/native-modifiers.d.ts +1 -2
  46. package/dist/native-modifiers.d.ts.map +1 -1
  47. package/dist/native-modifiers.js +44 -3
  48. package/dist/native-modifiers.js.map +1 -1
  49. package/dist/terminal.d.ts.map +1 -1
  50. package/dist/terminal.js +20 -2
  51. package/dist/terminal.js.map +1 -1
  52. package/dist/tui-alt-screen.d.ts +1 -31
  53. package/dist/tui-alt-screen.d.ts.map +1 -1
  54. package/dist/tui-alt-screen.js +41 -335
  55. package/dist/tui-alt-screen.js.map +1 -1
  56. package/dist/tui.d.ts +5 -83
  57. package/dist/tui.d.ts.map +1 -1
  58. package/dist/tui.js +4 -118
  59. package/dist/tui.js.map +1 -1
  60. package/dist/utils.d.ts +0 -2
  61. package/dist/utils.d.ts.map +1 -1
  62. package/dist/utils.js +0 -9
  63. package/dist/utils.js.map +1 -1
  64. package/native/darwin/README.md +6 -8
  65. package/native/darwin/build.sh +5 -12
  66. package/native/darwin/prebuilds/darwin-arm64/{darwin-platform.node → darwin-modifiers.node} +0 -0
  67. package/native/darwin/prebuilds/darwin-x64/{darwin-platform.node → darwin-modifiers.node} +0 -0
  68. package/native/darwin/src/darwin-modifiers.c +70 -0
  69. package/native/win32/README.md +7 -18
  70. package/native/win32/build.mjs +4 -7
  71. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  72. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  73. package/native/win32/src/win32-console-mode.c +119 -0
  74. package/package.json +3 -10
  75. package/dist/components/mouse-region.d.ts +0 -12
  76. package/dist/components/mouse-region.d.ts.map +0 -1
  77. package/dist/components/mouse-region.js +0 -19
  78. package/dist/components/mouse-region.js.map +0 -1
  79. package/dist/native-platform.d.ts +0 -18
  80. package/dist/native-platform.d.ts.map +0 -1
  81. package/dist/native-platform.js +0 -42
  82. package/dist/native-platform.js.map +0 -1
  83. package/native/clipboard.h +0 -170
  84. package/native/darwin/src/darwin-platform.m +0 -93
  85. package/native/linux/README.md +0 -23
  86. package/native/linux/build.sh +0 -65
  87. package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  88. package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  89. package/native/linux/src/clipboard-worker.h +0 -70
  90. package/native/linux/src/linux-platform-x11.c +0 -274
  91. package/native/napi.h +0 -89
  92. package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  93. package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  94. package/native/win32/src/win32-platform.c +0 -241
@@ -1,16 +1,16 @@
1
1
  # Windows native prebuilds
2
2
 
3
- Provides console input setup, modifier-key state, and asynchronous text/image clipboard access. Links to `kernel32` and `user32`; no Node headers are required.
4
-
5
- ## Building
6
-
7
- On Windows, install Visual Studio's "Desktop development with C++" workload, then run from the repository root to build x64 and arm64:
3
+ Build both Windows architectures from the repository root:
8
4
 
9
5
  ```sh
10
6
  bun run --cwd packages/tui build:native:win32
11
7
  ```
12
8
 
13
- For cross-builds or custom toolchains, provide MinGW-compatible compilers:
9
+ On Windows, the build uses the Microsoft C++ Build Tools from Visual Studio. `build.mjs` locates `VsDevCmd.bat`, initializes a developer environment for `amd64` and `arm64`, and builds the addon with `cl.exe`/`link.exe`.
10
+
11
+ Install the "Desktop development with C++" workload, or at minimum the MSVC toolset and Windows SDK components. No Node headers are required; the addon resolves N-API symbols from the host process.
12
+
13
+ For non-Windows cross-builds, or for custom Windows toolchains, set `PI_TUI_WIN32_TOOLCHAIN=mingw` and provide MinGW-compatible compilers:
14
14
 
15
15
  ```sh
16
16
  PI_TUI_WIN32_TOOLCHAIN=mingw \
@@ -19,15 +19,4 @@ CC_ARM64=/path/to/aarch64-w64-mingw32-gcc \
19
19
  bun run --cwd packages/tui build:native:win32
20
20
  ```
21
21
 
22
- ## Testing clipboard writes
23
-
24
- On a Windows test desktop, run from `packages/tui` in PowerShell:
25
-
26
- ```powershell
27
- $env:PI_TEST_NATIVE_CLIPBOARD = "1"
28
- node --test test/native-platform.test.ts
29
- ```
30
-
31
- This opt-in test checks native text writes and reads. **It replaces the system clipboard contents.**
32
-
33
- The opt-in clipboard test only runs on a real Windows desktop session. Cross-builds from macOS or Linux hosts produce the addon but cannot exercise it, and the `windows-latest` runners in `.github/workflows/native-prebuilds.yml` build the prebuilds without running this clipboard test. Treat Windows clipboard behavior as verified only when someone has run the test above on Windows.
22
+ The addon intentionally avoids the C runtime and links only against `kernel32`.
@@ -5,7 +5,7 @@ import * as path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
8
- const sourceFile = path.join(scriptDir, "src", "win32-platform.c");
8
+ const sourceFile = path.join(scriptDir, "src", "win32-console-mode.c");
9
9
  const temporaryDir = mkdtempSync(path.join(tmpdir(), "pi-tui-win32-"));
10
10
 
11
11
  const targets = [
@@ -121,7 +121,7 @@ function resolveMingwCompiler(target) {
121
121
  function installOutput(temporaryOutput, target) {
122
122
  const outputDir = path.join(scriptDir, "prebuilds", `win32-${target.arch}`);
123
123
  mkdirSync(outputDir, { recursive: true });
124
- const output = path.join(outputDir, "win32-platform.node");
124
+ const output = path.join(outputDir, "win32-console-mode.node");
125
125
  copyFileSync(temporaryOutput, output);
126
126
  chmodSync(output, 0o755);
127
127
  console.log(`Built ${path.relative(process.cwd(), output)}`);
@@ -129,7 +129,7 @@ function installOutput(temporaryOutput, target) {
129
129
 
130
130
  function buildWithMsvc(target, vsDevCmd) {
131
131
  const buildDir = path.join(temporaryDir, `msvc-${target.arch}`);
132
- const temporaryOutput = path.join(buildDir, "win32-platform.node");
132
+ const temporaryOutput = path.join(buildDir, "win32-console-mode.node");
133
133
  mkdirSync(buildDir, { recursive: true });
134
134
 
135
135
  const batchFile = path.join(buildDir, "build.cmd");
@@ -154,7 +154,6 @@ function buildWithMsvc(target, vsDevCmd) {
154
154
  "/NOENTRY",
155
155
  "/NODEFAULTLIB",
156
156
  "kernel32.lib",
157
- "user32.lib",
158
157
  "/OPT:REF",
159
158
  "/OPT:ICF",
160
159
  ].join(" "),
@@ -175,7 +174,7 @@ function buildWithMingw(target) {
175
174
  }
176
175
 
177
176
  const buildDir = path.join(temporaryDir, `mingw-${target.arch}`);
178
- const temporaryOutput = path.join(buildDir, "win32-platform.node");
177
+ const temporaryOutput = path.join(buildDir, "win32-console-mode.node");
179
178
  mkdirSync(buildDir, { recursive: true });
180
179
 
181
180
  const args = [
@@ -184,14 +183,12 @@ function buildWithMingw(target) {
184
183
  "-Wall",
185
184
  "-Wextra",
186
185
  "-Oz",
187
- "-fno-builtin",
188
186
  "-shared",
189
187
  "-nostdlib",
190
188
  "-Wl,--no-entry",
191
189
  "-Wl,--strip-all",
192
190
  sourceFile,
193
191
  "-lkernel32",
194
- "-luser32",
195
192
  "-o",
196
193
  temporaryOutput,
197
194
  ];
@@ -0,0 +1,119 @@
1
+ #include <windows.h>
2
+
3
+ #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
4
+ #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
5
+ #endif
6
+
7
+ #define NAPI_AUTO_LENGTH ((unsigned long long)-1)
8
+ #define KEY_PRESSED_MASK 0x8000
9
+
10
+ typedef void* napi_env;
11
+ typedef void* napi_value;
12
+ typedef void* napi_callback_info;
13
+ typedef napi_value (__cdecl *napi_callback)(napi_env, napi_callback_info);
14
+ typedef int (__cdecl *napi_create_function_fn)(napi_env, const char*, unsigned long long, napi_callback, void*, napi_value*);
15
+ typedef int (__cdecl *napi_set_named_property_fn)(napi_env, napi_value, const char*, napi_value);
16
+ typedef int (__cdecl *napi_get_boolean_fn)(napi_env, int, napi_value*);
17
+ typedef int (__cdecl *napi_get_cb_info_fn)(napi_env, napi_callback_info, unsigned long long*, napi_value*, napi_value*, void**);
18
+ typedef int (__cdecl *napi_get_value_string_utf8_fn)(napi_env, napi_value, char*, unsigned long long, unsigned long long*);
19
+ typedef SHORT (WINAPI *get_async_key_state_fn)(int);
20
+
21
+ static void* node_symbol(const char* name) {
22
+ HMODULE module = GetModuleHandleA(0);
23
+ void* proc = module ? (void*)GetProcAddress(module, name) : 0;
24
+ if (proc) return proc;
25
+
26
+ module = GetModuleHandleA("node.dll");
27
+ return module ? (void*)GetProcAddress(module, name) : 0;
28
+ }
29
+
30
+ static int string_equals(const char* left, const char* right) {
31
+ while (*left && *right && *left == *right) {
32
+ left++;
33
+ right++;
34
+ }
35
+ return *left == 0 && *right == 0;
36
+ }
37
+
38
+ static get_async_key_state_fn get_async_key_state_symbol(void) {
39
+ static int loaded = 0;
40
+ static get_async_key_state_fn get_async_key_state = 0;
41
+
42
+ if (!loaded) {
43
+ HMODULE module = GetModuleHandleA("user32.dll");
44
+ if (!module) module = LoadLibraryA("user32.dll");
45
+ get_async_key_state = module ? (get_async_key_state_fn)GetProcAddress(module, "GetAsyncKeyState") : 0;
46
+ loaded = 1;
47
+ }
48
+
49
+ return get_async_key_state;
50
+ }
51
+
52
+ static int is_key_pressed(int virtual_key) {
53
+ get_async_key_state_fn get_async_key_state = get_async_key_state_symbol();
54
+ return get_async_key_state && (((unsigned short)get_async_key_state(virtual_key)) & KEY_PRESSED_MASK) != 0;
55
+ }
56
+
57
+ static int is_modifier_name_pressed(const char* name) {
58
+ if (string_equals(name, "shift")) return is_key_pressed(VK_SHIFT) || is_key_pressed(VK_LSHIFT) || is_key_pressed(VK_RSHIFT);
59
+ if (string_equals(name, "control")) return is_key_pressed(VK_CONTROL) || is_key_pressed(VK_LCONTROL) || is_key_pressed(VK_RCONTROL);
60
+ if (string_equals(name, "option") || string_equals(name, "alt")) return is_key_pressed(VK_MENU) || is_key_pressed(VK_LMENU) || is_key_pressed(VK_RMENU);
61
+ if (string_equals(name, "command") || string_equals(name, "super") || string_equals(name, "win")) return is_key_pressed(VK_LWIN) || is_key_pressed(VK_RWIN);
62
+ return 0;
63
+ }
64
+
65
+ static napi_value __cdecl enable_virtual_terminal_input(napi_env env, napi_callback_info info) {
66
+ (void)info;
67
+
68
+ HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
69
+ DWORD mode = 0;
70
+ int enabled = handle != INVALID_HANDLE_VALUE &&
71
+ GetConsoleMode(handle, &mode) &&
72
+ SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_INPUT);
73
+
74
+ napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean");
75
+ napi_value result = 0;
76
+ if (napi_get_boolean) napi_get_boolean(env, enabled, &result);
77
+ return result;
78
+ }
79
+
80
+ static napi_value __cdecl is_modifier_pressed(napi_env env, napi_callback_info info) {
81
+ napi_get_cb_info_fn napi_get_cb_info = (napi_get_cb_info_fn)node_symbol("napi_get_cb_info");
82
+ napi_get_value_string_utf8_fn napi_get_value_string_utf8 = (napi_get_value_string_utf8_fn)node_symbol("napi_get_value_string_utf8");
83
+ napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean");
84
+
85
+ int pressed = 0;
86
+ if (napi_get_cb_info && napi_get_value_string_utf8) {
87
+ unsigned long long argc = 1;
88
+ napi_value args[1] = {0};
89
+ if (napi_get_cb_info(env, info, &argc, args, 0, 0) == 0 && argc >= 1 && args[0]) {
90
+ char name[16] = {0};
91
+ unsigned long long copied = 0;
92
+ if (napi_get_value_string_utf8(env, args[0], name, sizeof(name), &copied) == 0) {
93
+ pressed = is_modifier_name_pressed(name);
94
+ }
95
+ }
96
+ }
97
+
98
+ napi_value result = 0;
99
+ if (napi_get_boolean) napi_get_boolean(env, pressed, &result);
100
+ return result;
101
+ }
102
+
103
+ static void set_function_export(napi_env env, napi_value exports, const char* name, napi_callback callback) {
104
+ napi_create_function_fn napi_create_function = (napi_create_function_fn)node_symbol("napi_create_function");
105
+ napi_set_named_property_fn napi_set_named_property = (napi_set_named_property_fn)node_symbol("napi_set_named_property");
106
+
107
+ napi_value fn = 0;
108
+ if (napi_create_function &&
109
+ napi_set_named_property &&
110
+ napi_create_function(env, name, NAPI_AUTO_LENGTH, callback, 0, &fn) == 0) {
111
+ napi_set_named_property(env, exports, name, fn);
112
+ }
113
+ }
114
+
115
+ __declspec(dllexport) napi_value __cdecl napi_register_module_v1(napi_env env, napi_value exports) {
116
+ set_function_export(env, exports, "enableVirtualTerminalInput", enable_virtual_terminal_input);
117
+ set_function_export(env, exports, "isModifierPressed", is_modifier_pressed);
118
+ return exports;
119
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-tui",
3
- "version": "2026.9.12-3",
3
+ "version": "2026.9.12",
4
4
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,6 @@
8
8
  "clean": "shx rm -rf dist",
9
9
  "build": "tsgo -p tsconfig.build.json",
10
10
  "build:native:darwin": "native/darwin/build.sh",
11
- "build:native:linux": "native/linux/build.sh",
12
11
  "build:native:win32": "node native/win32/build.mjs",
13
12
  "test": "node --test --import tsx --import ./test/setup-multiplexer-env.mjs --test-reporter=dot --test-reporter-destination=stdout test/*.test.ts",
14
13
  "prepublishOnly": "npm run clean && npm run build",
@@ -16,20 +15,14 @@
16
15
  },
17
16
  "files": [
18
17
  "dist/**/*",
19
- "native/napi.h",
20
- "native/clipboard.h",
21
18
  "native/win32/prebuilds/**/*.node",
22
19
  "native/win32/src/**/*.c",
23
20
  "native/win32/build.mjs",
24
21
  "native/win32/README.md",
25
22
  "native/darwin/prebuilds/**/*.node",
26
- "native/darwin/src/**/*.m",
23
+ "native/darwin/src/**/*.c",
27
24
  "native/darwin/build.sh",
28
25
  "native/darwin/README.md",
29
- "native/linux/prebuilds/**/*.node",
30
- "native/linux/src/**/*.{c,h}",
31
- "native/linux/build.sh",
32
- "native/linux/README.md",
33
26
  "README.md"
34
27
  ],
35
28
  "keywords": [
@@ -54,7 +47,7 @@
54
47
  "types": "./dist/index.d.ts",
55
48
  "dependencies": {
56
49
  "get-east-asian-width": "1.6.0",
57
- "marked": "18.0.11"
50
+ "marked": "18.0.10"
58
51
  },
59
52
  "devDependencies": {
60
53
  "@xterm/headless": "6.0.0",
@@ -1,12 +0,0 @@
1
- import { type Component, type TuiMouseDispatchResult, type TuiMouseEvent, type TuiMouseEventResult } from "../tui.ts";
2
- export type MouseRegionHandler = (event: TuiMouseEvent) => TuiMouseEventResult | undefined;
3
- /** Adds mouse handling to an existing component without changing its rendering. */
4
- export declare class MouseRegion implements Component {
5
- private readonly child;
6
- private readonly onMouse;
7
- constructor(child: Component, onMouse: MouseRegionHandler);
8
- render(width: number): string[];
9
- handleMouse(event: TuiMouseEvent): TuiMouseDispatchResult | TuiMouseEventResult | undefined;
10
- invalidate(): void;
11
- }
12
- //# sourceMappingURL=mouse-region.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mouse-region.d.ts","sourceRoot":"","sources":["../../src/components/mouse-region.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EAEd,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,MAAM,WAAW,CAAC;AAEnB,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,mBAAmB,GAAG,SAAS,CAAC;AAE3F,mFAAmF;AACnF,qBAAa,WAAY,YAAW,SAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,YAAY,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAGxD;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;IAED,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,sBAAsB,GAAG,mBAAmB,GAAG,SAAS,CAG1F;IAED,UAAU,IAAI,IAAI,CAEjB;CACD"}
@@ -1,19 +0,0 @@
1
- import { dispatchMouseEvent, } from "../tui.js";
2
- /** Adds mouse handling to an existing component without changing its rendering. */
3
- export class MouseRegion {
4
- constructor(child, onMouse) {
5
- this.child = child;
6
- this.onMouse = onMouse;
7
- }
8
- render(width) {
9
- return this.child.render(width);
10
- }
11
- handleMouse(event) {
12
- const childResult = dispatchMouseEvent(this.child, event);
13
- return childResult ?? this.onMouse(event);
14
- }
15
- invalidate() {
16
- this.child.invalidate();
17
- }
18
- }
19
- //# sourceMappingURL=mouse-region.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mouse-region.js","sourceRoot":"","sources":["../../src/components/mouse-region.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,kBAAkB,GAIlB,MAAM,WAAW,CAAC;AAInB,mFAAmF;AACnF,MAAM,OAAO,WAAW;IAIvB,YAAY,KAAgB,EAAE,OAA2B;QACxD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,WAAW,CAAC,KAAoB;QAC/B,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,OAAO,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,UAAU;QACT,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IACzB,CAAC;CACD","sourcesContent":["import {\n\ttype Component,\n\tdispatchMouseEvent,\n\ttype TuiMouseDispatchResult,\n\ttype TuiMouseEvent,\n\ttype TuiMouseEventResult,\n} from \"../tui.ts\";\n\nexport type MouseRegionHandler = (event: TuiMouseEvent) => TuiMouseEventResult | undefined;\n\n/** Adds mouse handling to an existing component without changing its rendering. */\nexport class MouseRegion implements Component {\n\tprivate readonly child: Component;\n\tprivate readonly onMouse: MouseRegionHandler;\n\n\tconstructor(child: Component, onMouse: MouseRegionHandler) {\n\t\tthis.child = child;\n\t\tthis.onMouse = onMouse;\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.child.render(width);\n\t}\n\n\thandleMouse(event: TuiMouseEvent): TuiMouseDispatchResult | TuiMouseEventResult | undefined {\n\t\tconst childResult = dispatchMouseEvent(this.child, event);\n\t\treturn childResult ?? this.onMouse(event);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate();\n\t}\n}\n"]}
@@ -1,18 +0,0 @@
1
- export type ModifierKey = "shift" | "command" | "control" | "option";
2
- export interface NativeClipboard {
3
- /** Undefined means unavailable, null means no text; transfer failures reject. */
4
- getText(): Promise<string | null | undefined>;
5
- /** Undefined means unavailable, null means no image; transfer failures reject. */
6
- getImage(): Promise<Uint8Array | null | undefined>;
7
- /** Linux uses command-line tools to retain clipboard ownership instead. */
8
- setText?(text: string): Promise<void>;
9
- }
10
- type NativePlatformHelper = NativeClipboard & {
11
- enableVirtualTerminalInput?: () => boolean;
12
- isModifierPressed?: (name: ModifierKey) => boolean;
13
- };
14
- export declare function getNativePlatformHelper(): NativePlatformHelper | undefined;
15
- /** Load a clipboard helper without opening the display until a read is requested. */
16
- export declare function getNativeClipboard(): NativeClipboard | undefined;
17
- export {};
18
- //# sourceMappingURL=native-platform.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"native-platform.d.ts","sourceRoot":"","sources":["../src/native-platform.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,eAAe;IAC/B,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAC9C,kFAAkF;IAClF,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACnD,2EAA2E;IAC3E,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,KAAK,oBAAoB,GAAG,eAAe,GAAG;IAC7C,0BAA0B,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;CACnD,CAAC;AAgCF,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,SAAS,CAG1E;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAIhE"}
@@ -1,42 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import * as path from "node:path";
3
- import { getNativeModuleCandidates } from "./native-module-path.js";
4
- const cjsRequire = createRequire(import.meta.url);
5
- // Cache module loading, not display availability: a disconnected display can recover.
6
- const helpers = new Map();
7
- function loadNativePlatformHelper(platform, suffix = "") {
8
- const arch = process.arch;
9
- if (arch !== "x64" && arch !== "arm64")
10
- return undefined;
11
- const nativePath = path.join("native", platform, "prebuilds", `${platform}-${arch}`, `${platform}-platform${suffix}.node`);
12
- if (helpers.has(nativePath))
13
- return helpers.get(nativePath);
14
- for (const modulePath of getNativeModuleCandidates(nativePath)) {
15
- try {
16
- const helper = cjsRequire(modulePath);
17
- if (typeof helper?.getText === "function" && typeof helper.getImage === "function") {
18
- helpers.set(nativePath, helper);
19
- return helper;
20
- }
21
- }
22
- catch {
23
- // Try the next possible packaging location.
24
- }
25
- }
26
- helpers.set(nativePath, undefined);
27
- return undefined;
28
- }
29
- export function getNativePlatformHelper() {
30
- if (process.platform !== "darwin" && process.platform !== "win32")
31
- return undefined;
32
- return loadNativePlatformHelper(process.platform);
33
- }
34
- /** Load a clipboard helper without opening the display until a read is requested. */
35
- export function getNativeClipboard() {
36
- if (process.platform !== "linux")
37
- return getNativePlatformHelper();
38
- if (!process.env.DISPLAY)
39
- return undefined;
40
- return loadNativePlatformHelper("linux", "-x11");
41
- }
42
- //# sourceMappingURL=native-platform.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"native-platform.js","sourceRoot":"","sources":["../src/native-platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAkBlD,sFAAsF;AACtF,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4C,CAAC;AAEpE,SAAS,wBAAwB,CAAC,QAAgB,EAAE,MAAM,GAAG,EAAE;IAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAC3B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,GAAG,QAAQ,IAAI,IAAI,EAAE,EACrB,GAAG,QAAQ,YAAY,MAAM,OAAO,CACpC,CAAC;IACF,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAE5D,KAAK,MAAM,UAAU,IAAI,yBAAyB,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAyC,CAAC;YAC9E,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAA8B,CAAC,CAAC;gBACxD,OAAO,MAA8B,CAAC;YACvC,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,4CAA4C;QAC7C,CAAC;IACF,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACnC,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB;IACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACpF,OAAO,wBAAwB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,kBAAkB;IACjC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,uBAAuB,EAAE,CAAC;IACnE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { getNativeModuleCandidates } from \"./native-module-path.ts\";\n\nconst cjsRequire = createRequire(import.meta.url);\n\nexport type ModifierKey = \"shift\" | \"command\" | \"control\" | \"option\";\n\nexport interface NativeClipboard {\n\t/** Undefined means unavailable, null means no text; transfer failures reject. */\n\tgetText(): Promise<string | null | undefined>;\n\t/** Undefined means unavailable, null means no image; transfer failures reject. */\n\tgetImage(): Promise<Uint8Array | null | undefined>;\n\t/** Linux uses command-line tools to retain clipboard ownership instead. */\n\tsetText?(text: string): Promise<void>;\n}\n\ntype NativePlatformHelper = NativeClipboard & {\n\tenableVirtualTerminalInput?: () => boolean;\n\tisModifierPressed?: (name: ModifierKey) => boolean;\n};\n\n// Cache module loading, not display availability: a disconnected display can recover.\nconst helpers = new Map<string, NativePlatformHelper | undefined>();\n\nfunction loadNativePlatformHelper(platform: string, suffix = \"\"): NativePlatformHelper | undefined {\n\tconst arch = process.arch;\n\tif (arch !== \"x64\" && arch !== \"arm64\") return undefined;\n\tconst nativePath = path.join(\n\t\t\"native\",\n\t\tplatform,\n\t\t\"prebuilds\",\n\t\t`${platform}-${arch}`,\n\t\t`${platform}-platform${suffix}.node`,\n\t);\n\tif (helpers.has(nativePath)) return helpers.get(nativePath);\n\n\tfor (const modulePath of getNativeModuleCandidates(nativePath)) {\n\t\ttry {\n\t\t\tconst helper = cjsRequire(modulePath) as Partial<NativePlatformHelper> | null;\n\t\t\tif (typeof helper?.getText === \"function\" && typeof helper.getImage === \"function\") {\n\t\t\t\thelpers.set(nativePath, helper as NativePlatformHelper);\n\t\t\t\treturn helper as NativePlatformHelper;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try the next possible packaging location.\n\t\t}\n\t}\n\thelpers.set(nativePath, undefined);\n\treturn undefined;\n}\n\nexport function getNativePlatformHelper(): NativePlatformHelper | undefined {\n\tif (process.platform !== \"darwin\" && process.platform !== \"win32\") return undefined;\n\treturn loadNativePlatformHelper(process.platform);\n}\n\n/** Load a clipboard helper without opening the display until a read is requested. */\nexport function getNativeClipboard(): NativeClipboard | undefined {\n\tif (process.platform !== \"linux\") return getNativePlatformHelper();\n\tif (!process.env.DISPLAY) return undefined;\n\treturn loadNativePlatformHelper(\"linux\", \"-x11\");\n}\n"]}
@@ -1,170 +0,0 @@
1
- #ifndef PI_CLIPBOARD_H
2
- #define PI_CLIPBOARD_H
3
-
4
- #include "napi.h"
5
- #ifndef _WIN32
6
- #include <stdlib.h>
7
- #include <string.h>
8
- #endif
9
-
10
- typedef enum { CLIPBOARD_TEXT, CLIPBOARD_IMAGE, CLIPBOARD_WRITE } clipboard_operation;
11
- typedef enum { CLIPBOARD_UNAVAILABLE, CLIPBOARD_EMPTY, CLIPBOARD_UTF8, CLIPBOARD_LATIN1, CLIPBOARD_UTF16, CLIPBOARD_BUFFER } clipboard_format;
12
-
13
- typedef struct {
14
- napi_async_work work;
15
- napi_deferred deferred;
16
- clipboard_operation operation;
17
- clipboard_format format;
18
- void* data;
19
- size_t length;
20
- const char* error;
21
- } clipboard_job;
22
-
23
- // Only this function runs on the worker thread. It must not call N-API.
24
- static void clipboard_execute(clipboard_job* job);
25
-
26
- static void* clipboard_alloc(size_t size) {
27
- #ifdef _WIN32
28
- return GlobalAlloc(GPTR, size);
29
- #else
30
- return calloc(1, size);
31
- #endif
32
- }
33
-
34
- static void clipboard_free(void* data) {
35
- #ifdef _WIN32
36
- if (data) GlobalFree(data);
37
- #else
38
- free(data);
39
- #endif
40
- }
41
-
42
- #ifdef PI_CLIPBOARD_WRITE
43
- static void clipboard_copy(clipboard_job* job, const void* data, size_t length, clipboard_format format) {
44
- job->data = clipboard_alloc(length ? length : 1);
45
- if (!job->data) {
46
- job->error = "Out of memory";
47
- return;
48
- }
49
- #ifdef _WIN32
50
- volatile uint8_t* target = job->data;
51
- const volatile uint8_t* source = data;
52
- for (size_t index = 0; index < length; index++) target[index] = source[index];
53
- #else
54
- memcpy(job->data, data, length);
55
- #endif
56
- job->length = format == CLIPBOARD_UTF16 ? length / sizeof(uint16_t) : length;
57
- job->format = format;
58
- }
59
- #endif
60
-
61
- static void PI_NAPI_CALL execute_clipboard_work(napi_env env, void* data) {
62
- (void)env;
63
- clipboard_execute(data);
64
- }
65
-
66
- static void PI_NAPI_CALL complete_clipboard_work(napi_env env, int status, void* data) {
67
- clipboard_job* job = data;
68
- napi_create_string_utf8_fn create_string = (napi_create_string_utf8_fn)node_symbol("napi_create_string_utf8");
69
- napi_value result = undefined_value(env);
70
- if (status != 0) job->error = "Clipboard operation cancelled";
71
- if (!job->error && job->operation != CLIPBOARD_WRITE) {
72
- if (job->format == CLIPBOARD_EMPTY) {
73
- result = null_value(env);
74
- } else if (job->format == CLIPBOARD_BUFFER) {
75
- napi_create_buffer_copy_fn create_buffer = (napi_create_buffer_copy_fn)node_symbol("napi_create_buffer_copy");
76
- status = create_buffer(env, job->length, job->data, 0, &result);
77
- } else if (job->format == CLIPBOARD_UTF16) {
78
- napi_create_string_utf16_fn create_utf16 = (napi_create_string_utf16_fn)node_symbol("napi_create_string_utf16");
79
- status = create_utf16(env, job->data, job->length, &result);
80
- } else if (job->format == CLIPBOARD_UTF8 || job->format == CLIPBOARD_LATIN1) {
81
- napi_create_string_utf8_fn create_text = job->format == CLIPBOARD_LATIN1
82
- ? (napi_create_string_utf8_fn)node_symbol("napi_create_string_latin1") : create_string;
83
- status = create_text(env, job->data, job->length, &result);
84
- }
85
- if (status != 0) job->error = "Could not create clipboard value";
86
- }
87
- if (job->error) {
88
- napi_create_error_fn create_error = (napi_create_error_fn)node_symbol("napi_create_error");
89
- napi_value message;
90
- create_string(env, job->error, NAPI_AUTO_LENGTH, &message);
91
- create_error(env, 0, message, &result);
92
- }
93
- napi_settle_deferred_fn settle = (napi_settle_deferred_fn)node_symbol(
94
- job->error ? "napi_reject_deferred" : "napi_resolve_deferred"
95
- );
96
- settle(env, job->deferred, result);
97
- napi_async_work_fn delete_work = (napi_async_work_fn)node_symbol("napi_delete_async_work");
98
- delete_work(env, job->work);
99
- clipboard_free(job->data);
100
- clipboard_free(job);
101
- }
102
-
103
- static napi_value queue_clipboard(napi_env env, napi_callback_info info, clipboard_operation operation) {
104
- napi_create_async_work_fn create_work = (napi_create_async_work_fn)node_symbol("napi_create_async_work");
105
- napi_async_work_fn queue_work = (napi_async_work_fn)node_symbol("napi_queue_async_work");
106
- napi_async_work_fn delete_work = (napi_async_work_fn)node_symbol("napi_delete_async_work");
107
- napi_create_promise_fn create_promise = (napi_create_promise_fn)node_symbol("napi_create_promise");
108
- napi_create_string_utf8_fn create_string = (napi_create_string_utf8_fn)node_symbol("napi_create_string_utf8");
109
- if (!create_work || !queue_work || !delete_work || !create_promise || !create_string) {
110
- return fail(env, "Async clipboard API unavailable");
111
- }
112
- clipboard_job* job = clipboard_alloc(sizeof(*job));
113
- if (!job) return fail(env, "Out of memory");
114
- job->operation = operation;
115
- #ifdef PI_CLIPBOARD_WRITE
116
- if (operation == CLIPBOARD_WRITE) {
117
- napi_get_cb_info_fn get_info = (napi_get_cb_info_fn)node_symbol("napi_get_cb_info");
118
- #ifdef _WIN32
119
- napi_get_value_string_utf16_fn get_string = (napi_get_value_string_utf16_fn)node_symbol("napi_get_value_string_utf16");
120
- size_t unit_size = sizeof(uint16_t);
121
- #else
122
- napi_get_value_string_utf8_fn get_string = (napi_get_value_string_utf8_fn)node_symbol("napi_get_value_string_utf8");
123
- size_t unit_size = 1;
124
- #endif
125
- size_t argc = 1;
126
- napi_value argument = 0;
127
- if (!get_info || !get_string || get_info(env, info, &argc, &argument, 0, 0) != 0 || argc == 0 ||
128
- get_string(env, argument, 0, 0, &job->length) != 0) {
129
- clipboard_free(job);
130
- return fail(env, "setText requires a string");
131
- }
132
- job->data = job->length < SIZE_MAX / unit_size
133
- ? clipboard_alloc((job->length + 1) * unit_size) : 0;
134
- if (!job->data || get_string(env, argument, job->data, job->length + 1, &job->length) != 0) {
135
- clipboard_free(job->data);
136
- clipboard_free(job);
137
- return fail(env, "Could not read clipboard text");
138
- }
139
- }
140
- #else
141
- (void)info;
142
- #endif
143
- napi_value name = 0;
144
- napi_value promise = 0;
145
- if (create_string(env, "pi.clipboard", NAPI_AUTO_LENGTH, &name) != 0 ||
146
- create_work(env, 0, name, execute_clipboard_work, complete_clipboard_work, job, &job->work) != 0 ||
147
- create_promise(env, &job->deferred, &promise) != 0 || queue_work(env, job->work) != 0) {
148
- if (job->work) delete_work(env, job->work);
149
- clipboard_free(job->data);
150
- clipboard_free(job);
151
- return fail(env, "Could not queue clipboard operation");
152
- }
153
- return promise;
154
- }
155
-
156
- static napi_value PI_NAPI_CALL get_clipboard_text(napi_env env, napi_callback_info info) {
157
- return queue_clipboard(env, info, CLIPBOARD_TEXT);
158
- }
159
-
160
- static napi_value PI_NAPI_CALL get_clipboard_image(napi_env env, napi_callback_info info) {
161
- return queue_clipboard(env, info, CLIPBOARD_IMAGE);
162
- }
163
-
164
- #ifdef PI_CLIPBOARD_WRITE
165
- static napi_value PI_NAPI_CALL set_clipboard_text(napi_env env, napi_callback_info info) {
166
- return queue_clipboard(env, info, CLIPBOARD_WRITE);
167
- }
168
- #endif
169
-
170
- #endif
@@ -1,93 +0,0 @@
1
- #import <AppKit/AppKit.h>
2
- #import <CoreGraphics/CoreGraphics.h>
3
- #import <Foundation/Foundation.h>
4
- #include <stdlib.h>
5
- #include <string.h>
6
- #define PI_CLIPBOARD_WRITE
7
- #include "../../clipboard.h"
8
-
9
- static CGEventFlags modifier_mask_for_name(const char* name) {
10
- if (strcmp(name, "shift") == 0) return kCGEventFlagMaskShift;
11
- if (strcmp(name, "command") == 0) return kCGEventFlagMaskCommand;
12
- if (strcmp(name, "control") == 0) return kCGEventFlagMaskControl;
13
- if (strcmp(name, "option") == 0) return kCGEventFlagMaskAlternate;
14
- return 0;
15
- }
16
-
17
- static napi_value is_modifier_pressed(napi_env env, napi_callback_info info) {
18
- napi_get_cb_info_fn napi_get_cb_info = (napi_get_cb_info_fn)node_symbol("napi_get_cb_info");
19
- napi_get_value_string_utf8_fn napi_get_value_string_utf8 =
20
- (napi_get_value_string_utf8_fn)node_symbol("napi_get_value_string_utf8");
21
- napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean");
22
-
23
- bool pressed = false;
24
- if (napi_get_cb_info && napi_get_value_string_utf8) {
25
- size_t argc = 1;
26
- napi_value args[1] = {0};
27
- if (napi_get_cb_info(env, info, &argc, args, 0, 0) == 0 && argc >= 1 && args[0]) {
28
- char name[16] = {0};
29
- size_t copied = 0;
30
- if (napi_get_value_string_utf8(env, args[0], name, sizeof(name), &copied) == 0) {
31
- CGEventFlags mask = modifier_mask_for_name(name);
32
- if (mask != 0) {
33
- CGEventFlags flags = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
34
- pressed = (flags & mask) != 0;
35
- }
36
- }
37
- }
38
- }
39
-
40
- napi_value result = 0;
41
- if (!napi_get_boolean || napi_get_boolean(env, pressed, &result) != 0) {
42
- return fail(env, "Could not inspect modifier state");
43
- }
44
- return result;
45
- }
46
-
47
- static void clipboard_execute(clipboard_job* job) {
48
- @autoreleasepool {
49
- NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
50
- if (!pasteboard) {
51
- job->error = "Could not open clipboard";
52
- } else if (job->operation == CLIPBOARD_WRITE) {
53
- NSString* text = [[NSString alloc] initWithBytes:job->data length:job->length encoding:NSUTF8StringEncoding];
54
- if (!text) {
55
- job->error = "Clipboard text is not valid UTF-8";
56
- return;
57
- }
58
- [pasteboard clearContents];
59
- if (![pasteboard setString:text forType:NSPasteboardTypeString]) job->error = "Could not set clipboard text";
60
- } else if (job->operation == CLIPBOARD_TEXT) {
61
- NSString* text = [pasteboard stringForType:NSPasteboardTypeString];
62
- if (!text) {
63
- job->format = CLIPBOARD_EMPTY;
64
- } else if (!text.UTF8String) {
65
- job->error = "Could not encode clipboard text";
66
- } else {
67
- clipboard_copy(job, text.UTF8String, [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding], CLIPBOARD_UTF8);
68
- }
69
- } else {
70
- if (![pasteboard availableTypeFromArray:@[ NSPasteboardTypePNG, NSPasteboardTypeTIFF ]]) {
71
- job->format = CLIPBOARD_EMPTY;
72
- return;
73
- }
74
- NSData* png = [pasteboard dataForType:NSPasteboardTypePNG];
75
- if (!png) {
76
- NSImage* image = [[NSImage alloc] initWithPasteboard:pasteboard];
77
- NSData* tiff = image.TIFFRepresentation;
78
- NSBitmapImageRep* bitmap = tiff ? [NSBitmapImageRep imageRepWithData:tiff] : nil;
79
- png = bitmap ? [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}] : nil;
80
- }
81
- if (png) clipboard_copy(job, png.bytes, png.length, CLIPBOARD_BUFFER);
82
- else job->error = "Clipboard does not contain an image";
83
- }
84
- }
85
- }
86
-
87
- PI_NAPI_EXPORT napi_value napi_register_module_v1(napi_env env, napi_value exports) {
88
- set_function_export(env, exports, "isModifierPressed", is_modifier_pressed);
89
- set_function_export(env, exports, "getText", get_clipboard_text);
90
- set_function_export(env, exports, "setText", set_clipboard_text);
91
- set_function_export(env, exports, "getImage", get_clipboard_image);
92
- return exports;
93
- }