plusui-native-core 0.1.3 → 0.1.5
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/Core/CMakeLists.txt +190 -7
- package/Core/Features/App/app.cpp +129 -0
- package/Core/Features/App/app.ts +126 -0
- package/Core/Features/Browser/browser.cpp +181 -0
- package/Core/Features/Browser/browser.ts +182 -0
- package/Core/Features/Clipboard/clipboard.cpp +234 -0
- package/Core/Features/Clipboard/clipboard.ts +113 -0
- package/Core/Features/Display/display.cpp +209 -0
- package/Core/Features/Display/display.ts +104 -0
- package/Core/Features/Event/Events.ts +166 -0
- package/Core/Features/Event/events.cpp +200 -0
- package/Core/Features/Keyboard/keyboard.cpp +186 -0
- package/Core/Features/Keyboard/keyboard.ts +175 -0
- package/Core/Features/Menu/context-menu.css +293 -0
- package/Core/Features/Menu/menu.cpp +481 -0
- package/Core/Features/Menu/menu.ts +439 -0
- package/Core/Features/Tray/tray.cpp +310 -0
- package/Core/Features/Tray/tray.ts +68 -0
- package/Core/Features/WebGPU/webgpu.cpp +937 -0
- package/Core/Features/WebGPU/webgpu.ts +1013 -0
- package/Core/Features/WebView/webview.cpp +1052 -0
- package/Core/Features/WebView/webview.ts +510 -0
- package/Core/Features/Window/window.cpp +664 -0
- package/Core/Features/Window/window.ts +142 -0
- package/Core/Features/WindowManager/window_manager.cpp +341 -0
- package/Core/include/plusui/app.hpp +73 -0
- package/Core/include/plusui/browser.hpp +66 -0
- package/Core/include/plusui/clipboard.hpp +41 -0
- package/Core/include/plusui/events.hpp +58 -0
- package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
- package/Core/include/plusui/menu.hpp +153 -0
- package/Core/include/plusui/tray.hpp +93 -0
- package/Core/include/plusui/webgpu.hpp +434 -0
- package/Core/include/plusui/webview.hpp +142 -0
- package/Core/include/plusui/window.hpp +111 -0
- package/Core/include/plusui/window_manager.hpp +57 -0
- package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
- package/Core/vendor/stb_image.h +7988 -0
- package/Core/vendor/webview.h +618 -510
- package/Core/vendor/webview2.h +52079 -0
- package/README.md +19 -0
- package/package.json +12 -15
- package/Core/include/app.hpp +0 -121
- package/Core/include/menu.hpp +0 -79
- package/Core/include/tray.hpp +0 -81
- package/Core/include/window.hpp +0 -106
- package/Core/src/app.cpp +0 -311
- package/Core/src/display.cpp +0 -424
- package/Core/src/tray.cpp +0 -275
- package/Core/src/window.cpp +0 -528
- package/dist/index.d.ts +0 -205
- package/dist/index.js +0 -198
- package/src/index.ts +0 -574
- /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export interface BrowserState {
|
|
2
|
+
url: string;
|
|
3
|
+
title: string;
|
|
4
|
+
canGoBack: boolean;
|
|
5
|
+
canGoForward: boolean;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type NavigateCallback = (url: string) => void;
|
|
10
|
+
export type StateCallback = (state: BrowserState) => void;
|
|
11
|
+
export type LoadCallback = () => void;
|
|
12
|
+
export type ErrorCallback = (error: string) => void;
|
|
13
|
+
|
|
14
|
+
let _invoke: ((method: string, args?: unknown[]) => Promise<unknown>) | null = null;
|
|
15
|
+
|
|
16
|
+
export function setInvokeFn(fn: (method: string, args?: unknown[]) => Promise<unknown>) {
|
|
17
|
+
_invoke = fn;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function invoke(method: string, args?: unknown[]): Promise<unknown> {
|
|
21
|
+
if (!_invoke) {
|
|
22
|
+
if (typeof window !== 'undefined' && (window as any).__invoke__) {
|
|
23
|
+
_invoke = (window as any).__invoke__;
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Browser API not initialized');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return _invoke(method, args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _navigateCallbacks: NavigateCallback[] = [];
|
|
32
|
+
const _stateCallbacks: StateCallback[] = [];
|
|
33
|
+
const _loadStartCallbacks: LoadCallback[] = [];
|
|
34
|
+
const _loadEndCallbacks: LoadCallback[] = [];
|
|
35
|
+
const _loadErrorCallbacks: ErrorCallback[] = [];
|
|
36
|
+
let _currentState: BrowserState = { url: '', title: '', canGoBack: false, canGoForward: false, isLoading: false };
|
|
37
|
+
let _routes: Record<string, string> = {};
|
|
38
|
+
let _currentRoute: string = '/';
|
|
39
|
+
|
|
40
|
+
// Setup event listeners from native backend
|
|
41
|
+
if (typeof window !== 'undefined') {
|
|
42
|
+
(window as any).__onNavigate__ = (url: string) => {
|
|
43
|
+
_currentState.url = url;
|
|
44
|
+
_navigateCallbacks.forEach(cb => cb(url));
|
|
45
|
+
};
|
|
46
|
+
(window as any).__onBrowserState__ = (state: BrowserState) => {
|
|
47
|
+
_currentState = state;
|
|
48
|
+
_stateCallbacks.forEach(cb => cb(state));
|
|
49
|
+
};
|
|
50
|
+
(window as any).__onLoadStart__ = () => {
|
|
51
|
+
_currentState.isLoading = true;
|
|
52
|
+
_loadStartCallbacks.forEach(cb => cb());
|
|
53
|
+
};
|
|
54
|
+
(window as any).__onLoadEnd__ = () => {
|
|
55
|
+
_currentState.isLoading = false;
|
|
56
|
+
_loadEndCallbacks.forEach(cb => cb());
|
|
57
|
+
};
|
|
58
|
+
(window as any).__onLoadError__ = (error: string) => {
|
|
59
|
+
_currentState.isLoading = false;
|
|
60
|
+
_loadErrorCallbacks.forEach(cb => cb(error));
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const browser = {
|
|
65
|
+
async navigate(url: string): Promise<void> {
|
|
66
|
+
await invoke('browser.navigate', [url]);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async goBack(): Promise<void> {
|
|
70
|
+
await invoke('browser.goBack', []);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async goForward(): Promise<void> {
|
|
74
|
+
await invoke('browser.goForward', []);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async reload(): Promise<void> {
|
|
78
|
+
await invoke('browser.reload', []);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async stop(): Promise<void> {
|
|
82
|
+
await invoke('browser.stop', []);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async getUrl(): Promise<string> {
|
|
86
|
+
return await invoke('browser.getUrl', []) as Promise<string>;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async getTitle(): Promise<string> {
|
|
90
|
+
return await invoke('browser.getTitle', []) as Promise<string>;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
getState(): BrowserState {
|
|
94
|
+
return { ..._currentState };
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
async canGoBack(): Promise<boolean> {
|
|
98
|
+
return await invoke('browser.canGoBack', []) as Promise<boolean>;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
async canGoForward(): Promise<boolean> {
|
|
102
|
+
return await invoke('browser.canGoForward', []) as Promise<boolean>;
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async isLoading(): Promise<boolean> {
|
|
106
|
+
return _currentState.isLoading;
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
onNavigate(callback: NavigateCallback): () => void {
|
|
110
|
+
_navigateCallbacks.push(callback);
|
|
111
|
+
return () => {
|
|
112
|
+
const idx = _navigateCallbacks.indexOf(callback);
|
|
113
|
+
if (idx > -1) _navigateCallbacks.splice(idx, 1);
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
onStateChange(callback: StateCallback): () => void {
|
|
118
|
+
_stateCallbacks.push(callback);
|
|
119
|
+
return () => {
|
|
120
|
+
const idx = _stateCallbacks.indexOf(callback);
|
|
121
|
+
if (idx > -1) _stateCallbacks.splice(idx, 1);
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
onLoadStart(callback: LoadCallback): () => void {
|
|
126
|
+
_loadStartCallbacks.push(callback);
|
|
127
|
+
return () => {
|
|
128
|
+
const idx = _loadStartCallbacks.indexOf(callback);
|
|
129
|
+
if (idx > -1) _loadStartCallbacks.splice(idx, 1);
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
onLoadEnd(callback: LoadCallback): () => void {
|
|
134
|
+
_loadEndCallbacks.push(callback);
|
|
135
|
+
return () => {
|
|
136
|
+
const idx = _loadEndCallbacks.indexOf(callback);
|
|
137
|
+
if (idx > -1) _loadEndCallbacks.splice(idx, 1);
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
onLoadError(callback: ErrorCallback): () => void {
|
|
142
|
+
_loadErrorCallbacks.push(callback);
|
|
143
|
+
return () => {
|
|
144
|
+
const idx = _loadErrorCallbacks.indexOf(callback);
|
|
145
|
+
if (idx > -1) _loadErrorCallbacks.splice(idx, 1);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const router = {
|
|
151
|
+
setRoutes(routes: Record<string, string>): void {
|
|
152
|
+
_routes = routes;
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
getRoutes(): Record<string, string> {
|
|
156
|
+
return { ..._routes };
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
async push(route: string): Promise<void> {
|
|
160
|
+
_currentRoute = route;
|
|
161
|
+
const url = _routes[route] || route;
|
|
162
|
+
await browser.navigate(url);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async replace(route: string): Promise<void> {
|
|
166
|
+
_currentRoute = route;
|
|
167
|
+
const url = _routes[route] || route;
|
|
168
|
+
await browser.navigate(url);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
getCurrentRoute(): string {
|
|
172
|
+
return _currentRoute;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
async setInitialRoute(route: string): Promise<void> {
|
|
176
|
+
_currentRoute = route;
|
|
177
|
+
const url = _routes[route] || route;
|
|
178
|
+
await browser.navigate(url);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export default { browser, router };
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#include <memory>
|
|
2
|
+
#include <mutex>
|
|
3
|
+
#include <plusui/clipboard.hpp>
|
|
4
|
+
|
|
5
|
+
#ifdef _WIN32
|
|
6
|
+
#define WIN32_LEAN_AND_MEAN
|
|
7
|
+
#include <windows.h>
|
|
8
|
+
#elif defined(__APPLE__)
|
|
9
|
+
#include <AppKit/AppKit.h>
|
|
10
|
+
#include <CoreFoundation/CoreFoundation.h>
|
|
11
|
+
#else
|
|
12
|
+
// Linux/GTK - would use GTK clipboard APIs
|
|
13
|
+
#include <gtk/gtk.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
namespace PlusUI {
|
|
17
|
+
|
|
18
|
+
struct Clipboard::Impl {
|
|
19
|
+
std::mutex mtx;
|
|
20
|
+
std::function<void(const std::string &)> changeCallback;
|
|
21
|
+
|
|
22
|
+
#ifdef _WIN32
|
|
23
|
+
HWND hwnd = nullptr;
|
|
24
|
+
#endif
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
Clipboard::Clipboard() : pImpl(new Impl()) {
|
|
28
|
+
#ifdef _WIN32
|
|
29
|
+
// Create hidden window for clipboard monitoring
|
|
30
|
+
// This would be done properly in a full implementation
|
|
31
|
+
#endif
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Clipboard::~Clipboard() { delete pImpl; }
|
|
35
|
+
|
|
36
|
+
std::string Clipboard::getText() {
|
|
37
|
+
std::lock_guard<std::mutex> lock(pImpl->mtx);
|
|
38
|
+
|
|
39
|
+
#ifdef _WIN32
|
|
40
|
+
if (!OpenClipboard(nullptr)) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
|
|
45
|
+
if (hData == nullptr) {
|
|
46
|
+
CloseClipboard();
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
wchar_t *pszText = static_cast<wchar_t *>(GlobalLock(hData));
|
|
51
|
+
if (pszText == nullptr) {
|
|
52
|
+
CloseClipboard();
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Convert wide string to UTF-8
|
|
57
|
+
int size = WideCharToMultiByte(CP_UTF8, 0, pszText, -1, nullptr, 0, nullptr,
|
|
58
|
+
nullptr);
|
|
59
|
+
std::string text(size - 1, '\0');
|
|
60
|
+
WideCharToMultiByte(CP_UTF8, 0, pszText, -1, &text[0], size, nullptr,
|
|
61
|
+
nullptr);
|
|
62
|
+
|
|
63
|
+
GlobalUnlock(hData);
|
|
64
|
+
CloseClipboard();
|
|
65
|
+
|
|
66
|
+
return text;
|
|
67
|
+
|
|
68
|
+
#elif defined(__APPLE__)
|
|
69
|
+
@autoreleasepool {
|
|
70
|
+
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
71
|
+
NSString *str = [pasteboard stringForType:NSPasteboardTypeString];
|
|
72
|
+
if (str) {
|
|
73
|
+
return std::string([str UTF8String]);
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#else
|
|
79
|
+
// Linux/GTK implementation
|
|
80
|
+
if (!gtk_init_check(nullptr, nullptr)) {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
85
|
+
gchar *text = gtk_clipboard_wait_for_text(clipboard);
|
|
86
|
+
|
|
87
|
+
std::string result;
|
|
88
|
+
if (text) {
|
|
89
|
+
result = text;
|
|
90
|
+
g_free(text);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
#endif
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
void Clipboard::setText(const std::string &text) {
|
|
98
|
+
std::lock_guard<std::mutex> lock(pImpl->mtx);
|
|
99
|
+
|
|
100
|
+
#ifdef _WIN32
|
|
101
|
+
if (!OpenClipboard(nullptr)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
EmptyClipboard();
|
|
106
|
+
|
|
107
|
+
// Convert UTF-8 to wide string
|
|
108
|
+
int size = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, nullptr, 0);
|
|
109
|
+
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, size * sizeof(wchar_t));
|
|
110
|
+
if (hGlob) {
|
|
111
|
+
wchar_t *pGlob = static_cast<wchar_t *>(GlobalLock(hGlob));
|
|
112
|
+
MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, pGlob, size);
|
|
113
|
+
GlobalUnlock(hGlob);
|
|
114
|
+
SetClipboardData(CF_UNICODETEXT, hGlob);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
CloseClipboard();
|
|
118
|
+
|
|
119
|
+
#elif defined(__APPLE__)
|
|
120
|
+
@autoreleasepool {
|
|
121
|
+
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
122
|
+
[pasteboard clearContents];
|
|
123
|
+
NSString *str = [NSString stringWithUTF8String:text.c_str()];
|
|
124
|
+
[pasteboard setString:str forType:NSPasteboardTypeString];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#else
|
|
128
|
+
// Linux/GTK implementation
|
|
129
|
+
if (!gtk_init_check(nullptr, nullptr)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
134
|
+
gtk_clipboard_set_text(clipboard, text.c_str(), -1);
|
|
135
|
+
#endif
|
|
136
|
+
|
|
137
|
+
// Trigger change callback
|
|
138
|
+
if (pImpl->changeCallback) {
|
|
139
|
+
pImpl->changeCallback(text);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
bool Clipboard::hasText() {
|
|
144
|
+
#ifdef _WIN32
|
|
145
|
+
return IsClipboardFormatAvailable(CF_UNICODETEXT) != 0;
|
|
146
|
+
|
|
147
|
+
#elif defined(__APPLE__)
|
|
148
|
+
@autoreleasepool {
|
|
149
|
+
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
150
|
+
return
|
|
151
|
+
[pasteboard availableTypeFromArray:@[ NSPasteboardTypeString ]] != nil;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#else
|
|
155
|
+
// Linux/GTK implementation
|
|
156
|
+
if (!gtk_init_check(nullptr, nullptr)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
161
|
+
return gtk_clipboard_wait_is_text_available(clipboard);
|
|
162
|
+
#endif
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
std::string Clipboard::getImage() {
|
|
166
|
+
// Image support would require base64 encoding/decoding
|
|
167
|
+
// Platform-specific image clipboard handling
|
|
168
|
+
// This is a placeholder for now
|
|
169
|
+
return "";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
void Clipboard::setImage(const std::string &base64Data) {
|
|
173
|
+
// Image support would require base64 decoding
|
|
174
|
+
// Platform-specific image clipboard handling
|
|
175
|
+
// This is a placeholder for now
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
bool Clipboard::hasImage() {
|
|
179
|
+
#ifdef _WIN32
|
|
180
|
+
return IsClipboardFormatAvailable(CF_DIB) != 0 ||
|
|
181
|
+
IsClipboardFormatAvailable(CF_BITMAP) != 0;
|
|
182
|
+
|
|
183
|
+
#elif defined(__APPLE__)
|
|
184
|
+
@autoreleasepool {
|
|
185
|
+
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
186
|
+
return [pasteboard availableTypeFromArray:@[
|
|
187
|
+
NSPasteboardTypePNG, NSPasteboardTypeTIFF
|
|
188
|
+
]] != nil;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#else
|
|
192
|
+
// Linux/GTK implementation
|
|
193
|
+
if (!gtk_init_check(nullptr, nullptr)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
198
|
+
return gtk_clipboard_wait_is_image_available(clipboard);
|
|
199
|
+
#endif
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
void Clipboard::clear() {
|
|
203
|
+
std::lock_guard<std::mutex> lock(pImpl->mtx);
|
|
204
|
+
|
|
205
|
+
#ifdef _WIN32
|
|
206
|
+
if (OpenClipboard(nullptr)) {
|
|
207
|
+
EmptyClipboard();
|
|
208
|
+
CloseClipboard();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#elif defined(__APPLE__)
|
|
212
|
+
@autoreleasepool {
|
|
213
|
+
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
214
|
+
[pasteboard clearContents];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#else
|
|
218
|
+
// Linux/GTK implementation
|
|
219
|
+
if (!gtk_init_check(nullptr, nullptr)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
224
|
+
gtk_clipboard_clear(clipboard);
|
|
225
|
+
#endif
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
void Clipboard::onClipboardChange(
|
|
229
|
+
std::function<void(const std::string &)> callback) {
|
|
230
|
+
std::lock_guard<std::mutex> lock(pImpl->mtx);
|
|
231
|
+
pImpl->changeCallback = callback;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
} // namespace PlusUI
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard API - Cross-platform clipboard management
|
|
3
|
+
*
|
|
4
|
+
* Provides access to system clipboard for text and images.
|
|
5
|
+
* Supports clipboard change events.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface ClipboardAPI {
|
|
9
|
+
// Text operations
|
|
10
|
+
getText(): Promise<string>;
|
|
11
|
+
setText(text: string): Promise<void>;
|
|
12
|
+
hasText(): Promise<boolean>;
|
|
13
|
+
|
|
14
|
+
// Image operations (base64 encoded)
|
|
15
|
+
getImage(): Promise<string>;
|
|
16
|
+
setImage(base64Data: string): Promise<void>;
|
|
17
|
+
hasImage(): Promise<boolean>;
|
|
18
|
+
|
|
19
|
+
// Clear clipboard
|
|
20
|
+
clear(): Promise<void>;
|
|
21
|
+
|
|
22
|
+
// Events
|
|
23
|
+
onClipboardChange(callback: (text: string) => void): () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class Clipboard implements ClipboardAPI {
|
|
27
|
+
private invokeFn: (name: string, args?: unknown[]) => Promise<unknown>;
|
|
28
|
+
private onFn: (event: string, callback: (data: unknown) => void) => () => void;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
invokeFn: (name: string, args?: unknown[]) => Promise<unknown>,
|
|
32
|
+
onFn: (event: string, callback: (data: unknown) => void) => () => void
|
|
33
|
+
) {
|
|
34
|
+
this.invokeFn = invokeFn;
|
|
35
|
+
this.onFn = onFn;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get text from clipboard
|
|
40
|
+
* @returns Current clipboard text content
|
|
41
|
+
*/
|
|
42
|
+
async getText(): Promise<string> {
|
|
43
|
+
return (await this.invokeFn('clipboard.getText')) as string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set clipboard text
|
|
48
|
+
* @param text - Text to copy to clipboard
|
|
49
|
+
*/
|
|
50
|
+
async setText(text: string): Promise<void> {
|
|
51
|
+
await this.invokeFn('clipboard.setText', [text]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if clipboard has text
|
|
56
|
+
* @returns True if clipboard contains text
|
|
57
|
+
*/
|
|
58
|
+
async hasText(): Promise<boolean> {
|
|
59
|
+
return (await this.invokeFn('clipboard.hasText')) as boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get image from clipboard as base64
|
|
64
|
+
* @returns Base64 encoded image data
|
|
65
|
+
*/
|
|
66
|
+
async getImage(): Promise<string> {
|
|
67
|
+
return (await this.invokeFn('clipboard.getImage')) as string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set clipboard image from base64 data
|
|
72
|
+
* @param base64Data - Base64 encoded image data
|
|
73
|
+
*/
|
|
74
|
+
async setImage(base64Data: string): Promise<void> {
|
|
75
|
+
await this.invokeFn('clipboard.setImage', [base64Data]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if clipboard has image
|
|
80
|
+
* @returns True if clipboard contains image
|
|
81
|
+
*/
|
|
82
|
+
async hasImage(): Promise<boolean> {
|
|
83
|
+
return (await this.invokeFn('clipboard.hasImage')) as boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Clear clipboard contents
|
|
88
|
+
*/
|
|
89
|
+
async clear(): Promise<void> {
|
|
90
|
+
await this.invokeFn('clipboard.clear');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Listen for clipboard changes
|
|
95
|
+
* @param callback - Function called when clipboard changes
|
|
96
|
+
* @returns Cleanup function to remove listener
|
|
97
|
+
*/
|
|
98
|
+
onClipboardChange(callback: (text: string) => void): () => void {
|
|
99
|
+
return this.onFn('clipboard:change', (data) => {
|
|
100
|
+
callback(data as string);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Factory function to create Clipboard instance
|
|
107
|
+
*/
|
|
108
|
+
export function createClipboard(
|
|
109
|
+
invokeFn: (name: string, args?: unknown[]) => Promise<unknown>,
|
|
110
|
+
onFn: (event: string, callback: (data: unknown) => void) => () => void
|
|
111
|
+
): Clipboard {
|
|
112
|
+
return new Clipboard(invokeFn, onFn);
|
|
113
|
+
}
|