@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.
- package/README.md +4 -53
- package/dist/alt-screen-search.d.ts +1 -21
- package/dist/alt-screen-search.d.ts.map +1 -1
- package/dist/alt-screen-search.js +42 -199
- package/dist/alt-screen-search.js.map +1 -1
- package/dist/components/box.d.ts +1 -3
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +0 -28
- package/dist/components/box.js.map +1 -1
- package/dist/components/editor.d.ts +1 -6
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +18 -98
- package/dist/components/editor.js.map +1 -1
- package/dist/components/input.d.ts +1 -12
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +6 -40
- package/dist/components/input.js.map +1 -1
- package/dist/components/latex.d.ts.map +1 -1
- package/dist/components/latex.js +0 -7
- package/dist/components/latex.js.map +1 -1
- package/dist/components/loader.d.ts +0 -3
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js +8 -20
- package/dist/components/loader.js.map +1 -1
- package/dist/components/scroll-view.d.ts +3 -6
- package/dist/components/scroll-view.d.ts.map +1 -1
- package/dist/components/scroll-view.js +1 -6
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/select-list.d.ts +1 -4
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +2 -48
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts +1 -5
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +5 -54
- package/dist/components/settings-list.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +1 -3
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +9 -32
- package/dist/layout.js.map +1 -1
- package/dist/native-modifiers.d.ts +1 -2
- package/dist/native-modifiers.d.ts.map +1 -1
- package/dist/native-modifiers.js +44 -3
- package/dist/native-modifiers.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +20 -2
- package/dist/terminal.js.map +1 -1
- package/dist/tui-alt-screen.d.ts +1 -31
- package/dist/tui-alt-screen.d.ts.map +1 -1
- package/dist/tui-alt-screen.js +41 -335
- package/dist/tui-alt-screen.js.map +1 -1
- package/dist/tui.d.ts +5 -83
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +4 -118
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts +0 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +0 -9
- package/dist/utils.js.map +1 -1
- package/native/darwin/README.md +6 -8
- package/native/darwin/build.sh +5 -12
- package/native/darwin/prebuilds/darwin-arm64/{darwin-platform.node → darwin-modifiers.node} +0 -0
- package/native/darwin/prebuilds/darwin-x64/{darwin-platform.node → darwin-modifiers.node} +0 -0
- package/native/darwin/src/darwin-modifiers.c +70 -0
- package/native/win32/README.md +7 -18
- package/native/win32/build.mjs +4 -7
- package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/native/win32/src/win32-console-mode.c +119 -0
- package/package.json +3 -10
- package/dist/components/mouse-region.d.ts +0 -12
- package/dist/components/mouse-region.d.ts.map +0 -1
- package/dist/components/mouse-region.js +0 -19
- package/dist/components/mouse-region.js.map +0 -1
- package/dist/native-platform.d.ts +0 -18
- package/dist/native-platform.d.ts.map +0 -1
- package/dist/native-platform.js +0 -42
- package/dist/native-platform.js.map +0 -1
- package/native/clipboard.h +0 -170
- package/native/darwin/src/darwin-platform.m +0 -93
- package/native/linux/README.md +0 -23
- package/native/linux/build.sh +0 -65
- package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/native/linux/src/clipboard-worker.h +0 -70
- package/native/linux/src/linux-platform-x11.c +0 -274
- package/native/napi.h +0 -89
- package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/native/win32/src/win32-platform.c +0 -241
package/native/win32/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Windows native prebuilds
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`.
|
package/native/win32/build.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
];
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
+
"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/**/*.
|
|
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.
|
|
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"}
|
package/dist/native-platform.js
DELETED
|
@@ -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"]}
|
package/native/clipboard.h
DELETED
|
@@ -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
|
-
}
|