cj-tauri 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -99
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +63 -52
- package/cli/cjpm.toml +22 -22
- package/cli/src/doctor.cj +308 -306
- package/cli/src/main.cj +129 -129
- package/cli/src/manifest.cj +632 -558
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -770
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +369 -369
- package/cli/src/sync.cj +526 -526
- package/cli/src/util.cj +168 -168
- package/cli/templates/app/README.md +51 -48
- package/cli/templates/app/assets/capabilities/default.json +7 -7
- package/cli/templates/app/assets/ui/index.html +120 -118
- package/cli/templates/app/cj-tauri.toml +32 -22
- package/cli/templates/app/cjpm.toml +34 -34
- package/cli/templates/app/src/main.cj +101 -90
- package/cli/templates/app-react/README.md +56 -56
- package/cli/templates/app-react/assets/capabilities/default.json +7 -7
- package/cli/templates/app-react/assets/ui/index.html +25 -25
- package/cli/templates/app-react/assets/ui/package.json +19 -19
- package/cli/templates/app-react/assets/ui/src/App.css +67 -67
- package/cli/templates/app-react/assets/ui/src/App.jsx +124 -124
- package/cli/templates/app-react/assets/ui/src/main.jsx +6 -6
- package/cli/templates/app-react/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-react/cj-tauri.toml +33 -23
- package/cli/templates/app-react/cjpm.toml +34 -34
- package/cli/templates/app-react/src/main.cj +91 -91
- package/cli/templates/app-vue/README.md +56 -56
- package/cli/templates/app-vue/assets/capabilities/default.json +7 -7
- package/cli/templates/app-vue/assets/ui/index.html +25 -25
- package/cli/templates/app-vue/assets/ui/package.json +18 -18
- package/cli/templates/app-vue/assets/ui/src/App.vue +176 -176
- package/cli/templates/app-vue/assets/ui/src/main.js +4 -4
- package/cli/templates/app-vue/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-vue/cj-tauri.toml +33 -23
- package/cli/templates/app-vue/cjpm.toml +34 -34
- package/cli/templates/app-vue/src/main.cj +91 -91
- package/cli/templates/ohos-skeleton/AppScope/app.json5 +13 -13
- package/cli/templates/ohos-skeleton/AppScope/resources/base/element/string.json +8 -8
- package/cli/templates/ohos-skeleton/AppScope/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/README.md +84 -79
- package/cli/templates/ohos-skeleton/build-hap.sh +15 -0
- package/cli/templates/ohos-skeleton/build-profile.json5 +46 -46
- package/cli/templates/ohos-skeleton/entry/build-profile.json5 +26 -26
- package/cli/templates/ohos-skeleton/entry/cjpm.toml +51 -43
- package/cli/templates/ohos-skeleton/entry/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/entry/oh-package.json5 +9 -9
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/ability_stage.cj +17 -17
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/app_host.cj +94 -94
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -167
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/main_ability.cj +139 -139
- package/cli/templates/ohos-skeleton/entry/src/main/module.json5 +57 -49
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/color.json +8 -8
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/string.json +16 -16
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/profile/main_pages.json +2 -2
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/capabilities.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/index.html +92 -92
- package/cli/templates/ohos-skeleton/hvigor/hvigor-config.json5 +5 -5
- package/cli/templates/ohos-skeleton/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/oh-package.json5 +10 -10
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3740 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -53
- package/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -969
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -76
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +142 -142
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -607
- package/src/host_arkweb.cj +924 -778
- package/src/host_webkit.cj +663 -570
- package/src/host_webview2.cj +663 -570
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +337 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -62
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -77
- package/src/platform/platform_assets.cj +33 -33
- package/src/platform/platform_discover.cj +176 -176
- package/src/platform/platform_env.cj +29 -29
- package/src/platform/platform_feature.cj +31 -31
- package/src/platform/platform_page_source.cj +87 -87
- package/src/platform/platform_target_info.cj +32 -32
- package/src/platform_host_provider.cj +87 -87
- package/src/plugin.cj +333 -333
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -152
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +158 -158
- package/src/plugins/notification/plugin_notification.cj +154 -154
- package/src/plugins/serial/plugin_serial.cj +458 -454
- package/src/plugins/serial/serial_bridge.cj +194 -194
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +258 -258
- package/src/plugins/sqlite/plugin_sqlite.cj +705 -710
- package/src/plugins/sqlite/sqlite_ffi.cj +206 -211
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +126 -127
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -201
- package/src/tests/ipc_hub_test.cj +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -251
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +605 -585
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/plugins/serial/serial_ohos_stub.cj +0 -61
- package/src/plugins/sqlite/sqlite_ohos_stub.cj +0 -91
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
package cjTauri.plugins.clipboard
|
|
2
|
-
|
|
3
|
-
import cjTauri.*
|
|
4
|
-
|
|
5
|
-
import std.collection.*
|
|
6
|
-
import stdx.encoding.json.*
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 官方插件 clipboard:系统剪贴板(对标 Tauri 的 clipboard-manager 插件,RFC-003 第 3 期)
|
|
10
|
-
*
|
|
11
|
-
* 命令:clipboard:write / clipboard:read
|
|
12
|
-
* 权限:**不自动放行**,应用要在 capabilities 里显式声明,两种写法等价:
|
|
13
|
-
* "commands": ["clipboard:read"] // 明文逐条
|
|
14
|
-
* "permissions": ["clipboard:readonly"] // 命名权限集(只放行 read)
|
|
15
|
-
* "permissions": ["clipboard:default"] // 命名权限集(读写全量)
|
|
16
|
-
* 前端:window.__CJ_TAURI__.clipboard.writeText("...") / readText()
|
|
17
|
-
*
|
|
18
|
-
* 真正的读写落在 C 桥(`cj_bridge_clipboard_*`):GTK / Win32 的剪贴板调用都必须在
|
|
19
|
-
* 宿主 UI 线程上,core 那边做「UI 线程分流 + 单槽等待」。插件只做参数校验与命令分发,
|
|
20
|
-
* 平台差异不进插件(AGENTS §2 的架构契约)。
|
|
21
|
-
*
|
|
22
|
-
* 读写都要宿主已就绪(start 之后);更早调用得到 false / None。
|
|
23
|
-
*/
|
|
24
|
-
public class ClipboardPlugin <: Plugin {
|
|
25
|
-
|
|
26
|
-
/** 自描述:原生桥仅桌面两端实现(ohos 侧按 P-1 落桩,批次 3-PR3) */
|
|
27
|
-
public func describe(): PluginManifest {
|
|
28
|
-
let hosts = ArrayList<String>()
|
|
29
|
-
hosts.add("webkitgtk")
|
|
30
|
-
hosts.add("webview2")
|
|
31
|
-
return PluginManifest(hosts, ArrayList<String>())
|
|
32
|
-
}
|
|
33
|
-
private let handler: ClipboardCommands = ClipboardCommands()
|
|
34
|
-
|
|
35
|
-
public init() {}
|
|
36
|
-
|
|
37
|
-
public func name(): String {
|
|
38
|
-
return "clipboard"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public func commands(): HashMap<String, CommandHandler> {
|
|
42
|
-
let m = HashMap<String, CommandHandler>()
|
|
43
|
-
m.add("write", this.handler)
|
|
44
|
-
m.add("read", this.handler)
|
|
45
|
-
return m
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 命名权限集(v1.1):readonly 只放行读——剪贴板里可能有用户敏感内容,
|
|
50
|
-
* 「只写不读」是合理的最小授权面。
|
|
51
|
-
*/
|
|
52
|
-
public func permissions(): HashMap<String, ArrayList<String>> {
|
|
53
|
-
let m = HashMap<String, ArrayList<String>>()
|
|
54
|
-
let ro = ArrayList<String>()
|
|
55
|
-
ro.add("read")
|
|
56
|
-
m.add("readonly", ro)
|
|
57
|
-
let all = ArrayList<String>()
|
|
58
|
-
all.add("read")
|
|
59
|
-
all.add("write")
|
|
60
|
-
m.add("default", all)
|
|
61
|
-
return m
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 前端 shim:往桥对象上挂一个 clipboard 命名空间(与 dialog 同款)
|
|
66
|
-
*/
|
|
67
|
-
public func jsShim(): String {
|
|
68
|
-
return "(function () { var t = window.__CJ_TAURI__ || (window.__CJ_TAURI__ = {});" +
|
|
69
|
-
" t.clipboard = {" +
|
|
70
|
-
" writeText: function (text) { return window.__CJ_TAURI__.invoke('clipboard:write', { text: text }); }," +
|
|
71
|
-
" readText: function () { return window.__CJ_TAURI__.invoke('clipboard:read', {}); }" +
|
|
72
|
-
" }; })();\n"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public func setup(app: TauriApp): Unit {
|
|
76
|
-
// 多窗口按报文里的 label 解析宿主(与 dialog 插件同一口径)。固定 `app.webviewHost()`
|
|
77
|
-
// 会让第二扇窗的剪贴板命令全部落到默认窗那一个宿主上(2026-10-20 dialog 实机取证同款)。
|
|
78
|
-
this.handler.attachHostResolver({ label => app.hostOf(label) })
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 剪贴板命令处理器:write 收 { text },read 无参。
|
|
84
|
-
*/
|
|
85
|
-
public class ClipboardCommands <: CommandHandler {
|
|
86
|
-
private var host: Option<WebViewHost> = None
|
|
87
|
-
|
|
88
|
-
/* 按 label 解析宿主(多窗口的正路):单槽 `host` 只记着装配时那一个宿主,
|
|
89
|
-
多窗口下用它就是「所有窗的剪贴板命令都发给默认窗那一个宿主」。 */
|
|
90
|
-
private var hostByWindow: Option<(String) -> Option<WebViewHost>> = None
|
|
91
|
-
|
|
92
|
-
public init() {}
|
|
93
|
-
|
|
94
|
-
/** 由 ClipboardPlugin.setup() 调用(单窗口 / 单测用;这个槽不带窗口身份) */
|
|
95
|
-
public func attachHost(h: WebViewHost): Unit {
|
|
96
|
-
this.host = Some(h)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** 装配「label → 宿主」解析器;由 ClipboardPlugin.setup() 传入 `app.hostOf` */
|
|
100
|
-
public func attachHostResolver(resolve: (String) -> Option<WebViewHost>): Unit {
|
|
101
|
-
this.hostByWindow = Some(resolve)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
105
|
-
match (cmd) {
|
|
106
|
-
case "clipboard:write" =>
|
|
107
|
-
let text = this.requireString(args, "text")
|
|
108
|
-
if (this.requireHost(ipc).clipboardWriteText(text)) {
|
|
109
|
-
return JsonNull()
|
|
110
|
-
}
|
|
111
|
-
throw CommandException("clipboard: 写入失败(宿主未就绪 / 平台拒绝)")
|
|
112
|
-
case "clipboard:read" =>
|
|
113
|
-
match (this.requireHost(ipc).clipboardReadText()) {
|
|
114
|
-
case Some(text) => return JsonString(text)
|
|
115
|
-
case None => return JsonNull() // 空 / 非文本:合法结果,不是错误
|
|
116
|
-
}
|
|
117
|
-
case _ => throw CommandException("unknown clipboard command: ${cmd}")
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* 取**发起这条命令的窗口**的宿主
|
|
123
|
-
*
|
|
124
|
-
* 多窗口下必须按 `ipc.window` 解析:用装配时那一个宿主,第二扇窗的命令会落到默认窗的宿主上。
|
|
125
|
-
* 解析器在场而窗口没登记时**如实报错**,不回落到单槽(与 dialog 插件同一口径)。
|
|
126
|
-
*/
|
|
127
|
-
private func requireHost(ipc: IpcContext): WebViewHost {
|
|
128
|
-
match (this.hostByWindow) {
|
|
129
|
-
case Some(resolve) => match (resolve(ipc.window)) {
|
|
130
|
-
case Some(h) => return h
|
|
131
|
-
case None => throw CommandException(
|
|
132
|
-
"clipboard: 窗口 \"${ipc.window}\" 没有登记宿主(addWindow(); 默认窗在 run() 之前也可用)")
|
|
133
|
-
}
|
|
134
|
-
case None => ()
|
|
135
|
-
}
|
|
136
|
-
match (this.host) {
|
|
137
|
-
case Some(h) => h
|
|
138
|
-
case None => throw CommandException(
|
|
139
|
-
"clipboard: 宿主未装配(用 .plugin(ClipboardPlugin()) 装配插件后才可用)")
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private func requireString(args: JsonObject, key: String): String {
|
|
144
|
-
match (args.get(key)) {
|
|
145
|
-
case Some(v) => match (v.kind()) {
|
|
146
|
-
case JsonKind.JsString => return v.asString().getValue()
|
|
147
|
-
case _ => throw CommandException("clipboard: argument \"${key}\" must be a string")
|
|
148
|
-
}
|
|
149
|
-
case None => throw CommandException("clipboard: missing argument \"${key}\"")
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
1
|
+
package cjTauri.plugins.clipboard
|
|
2
|
+
|
|
3
|
+
import cjTauri.*
|
|
4
|
+
|
|
5
|
+
import std.collection.*
|
|
6
|
+
import stdx.encoding.json.*
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 官方插件 clipboard:系统剪贴板(对标 Tauri 的 clipboard-manager 插件,RFC-003 第 3 期)
|
|
10
|
+
*
|
|
11
|
+
* 命令:clipboard:write / clipboard:read
|
|
12
|
+
* 权限:**不自动放行**,应用要在 capabilities 里显式声明,两种写法等价:
|
|
13
|
+
* "commands": ["clipboard:read"] // 明文逐条
|
|
14
|
+
* "permissions": ["clipboard:readonly"] // 命名权限集(只放行 read)
|
|
15
|
+
* "permissions": ["clipboard:default"] // 命名权限集(读写全量)
|
|
16
|
+
* 前端:window.__CJ_TAURI__.clipboard.writeText("...") / readText()
|
|
17
|
+
*
|
|
18
|
+
* 真正的读写落在 C 桥(`cj_bridge_clipboard_*`):GTK / Win32 的剪贴板调用都必须在
|
|
19
|
+
* 宿主 UI 线程上,core 那边做「UI 线程分流 + 单槽等待」。插件只做参数校验与命令分发,
|
|
20
|
+
* 平台差异不进插件(AGENTS §2 的架构契约)。
|
|
21
|
+
*
|
|
22
|
+
* 读写都要宿主已就绪(start 之后);更早调用得到 false / None。
|
|
23
|
+
*/
|
|
24
|
+
public class ClipboardPlugin <: Plugin {
|
|
25
|
+
|
|
26
|
+
/** 自描述:原生桥仅桌面两端实现(ohos 侧按 P-1 落桩,批次 3-PR3) */
|
|
27
|
+
public func describe(): PluginManifest {
|
|
28
|
+
let hosts = ArrayList<String>()
|
|
29
|
+
hosts.add("webkitgtk")
|
|
30
|
+
hosts.add("webview2")
|
|
31
|
+
return PluginManifest(hosts, ArrayList<String>())
|
|
32
|
+
}
|
|
33
|
+
private let handler: ClipboardCommands = ClipboardCommands()
|
|
34
|
+
|
|
35
|
+
public init() {}
|
|
36
|
+
|
|
37
|
+
public func name(): String {
|
|
38
|
+
return "clipboard"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public func commands(): HashMap<String, CommandHandler> {
|
|
42
|
+
let m = HashMap<String, CommandHandler>()
|
|
43
|
+
m.add("write", this.handler)
|
|
44
|
+
m.add("read", this.handler)
|
|
45
|
+
return m
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 命名权限集(v1.1):readonly 只放行读——剪贴板里可能有用户敏感内容,
|
|
50
|
+
* 「只写不读」是合理的最小授权面。
|
|
51
|
+
*/
|
|
52
|
+
public func permissions(): HashMap<String, ArrayList<String>> {
|
|
53
|
+
let m = HashMap<String, ArrayList<String>>()
|
|
54
|
+
let ro = ArrayList<String>()
|
|
55
|
+
ro.add("read")
|
|
56
|
+
m.add("readonly", ro)
|
|
57
|
+
let all = ArrayList<String>()
|
|
58
|
+
all.add("read")
|
|
59
|
+
all.add("write")
|
|
60
|
+
m.add("default", all)
|
|
61
|
+
return m
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 前端 shim:往桥对象上挂一个 clipboard 命名空间(与 dialog 同款)
|
|
66
|
+
*/
|
|
67
|
+
public func jsShim(): String {
|
|
68
|
+
return "(function () { var t = window.__CJ_TAURI__ || (window.__CJ_TAURI__ = {});" +
|
|
69
|
+
" t.clipboard = {" +
|
|
70
|
+
" writeText: function (text) { return window.__CJ_TAURI__.invoke('clipboard:write', { text: text }); }," +
|
|
71
|
+
" readText: function () { return window.__CJ_TAURI__.invoke('clipboard:read', {}); }" +
|
|
72
|
+
" }; })();\n"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public func setup(app: TauriApp): Unit {
|
|
76
|
+
// 多窗口按报文里的 label 解析宿主(与 dialog 插件同一口径)。固定 `app.webviewHost()`
|
|
77
|
+
// 会让第二扇窗的剪贴板命令全部落到默认窗那一个宿主上(2026-10-20 dialog 实机取证同款)。
|
|
78
|
+
this.handler.attachHostResolver({ label => app.hostOf(label) })
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 剪贴板命令处理器:write 收 { text },read 无参。
|
|
84
|
+
*/
|
|
85
|
+
public class ClipboardCommands <: CommandHandler {
|
|
86
|
+
private var host: Option<WebViewHost> = None
|
|
87
|
+
|
|
88
|
+
/* 按 label 解析宿主(多窗口的正路):单槽 `host` 只记着装配时那一个宿主,
|
|
89
|
+
多窗口下用它就是「所有窗的剪贴板命令都发给默认窗那一个宿主」。 */
|
|
90
|
+
private var hostByWindow: Option<(String) -> Option<WebViewHost>> = None
|
|
91
|
+
|
|
92
|
+
public init() {}
|
|
93
|
+
|
|
94
|
+
/** 由 ClipboardPlugin.setup() 调用(单窗口 / 单测用;这个槽不带窗口身份) */
|
|
95
|
+
public func attachHost(h: WebViewHost): Unit {
|
|
96
|
+
this.host = Some(h)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 装配「label → 宿主」解析器;由 ClipboardPlugin.setup() 传入 `app.hostOf` */
|
|
100
|
+
public func attachHostResolver(resolve: (String) -> Option<WebViewHost>): Unit {
|
|
101
|
+
this.hostByWindow = Some(resolve)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
105
|
+
match (cmd) {
|
|
106
|
+
case "clipboard:write" =>
|
|
107
|
+
let text = this.requireString(args, "text")
|
|
108
|
+
if (this.requireHost(ipc).clipboardWriteText(text)) {
|
|
109
|
+
return JsonNull()
|
|
110
|
+
}
|
|
111
|
+
throw CommandException("clipboard: 写入失败(宿主未就绪 / 平台拒绝)")
|
|
112
|
+
case "clipboard:read" =>
|
|
113
|
+
match (this.requireHost(ipc).clipboardReadText()) {
|
|
114
|
+
case Some(text) => return JsonString(text)
|
|
115
|
+
case None => return JsonNull() // 空 / 非文本:合法结果,不是错误
|
|
116
|
+
}
|
|
117
|
+
case _ => throw CommandException("unknown clipboard command: ${cmd}")
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 取**发起这条命令的窗口**的宿主
|
|
123
|
+
*
|
|
124
|
+
* 多窗口下必须按 `ipc.window` 解析:用装配时那一个宿主,第二扇窗的命令会落到默认窗的宿主上。
|
|
125
|
+
* 解析器在场而窗口没登记时**如实报错**,不回落到单槽(与 dialog 插件同一口径)。
|
|
126
|
+
*/
|
|
127
|
+
private func requireHost(ipc: IpcContext): WebViewHost {
|
|
128
|
+
match (this.hostByWindow) {
|
|
129
|
+
case Some(resolve) => match (resolve(ipc.window)) {
|
|
130
|
+
case Some(h) => return h
|
|
131
|
+
case None => throw CommandException(
|
|
132
|
+
"clipboard: 窗口 \"${ipc.window}\" 没有登记宿主(addWindow(); 默认窗在 run() 之前也可用)")
|
|
133
|
+
}
|
|
134
|
+
case None => ()
|
|
135
|
+
}
|
|
136
|
+
match (this.host) {
|
|
137
|
+
case Some(h) => h
|
|
138
|
+
case None => throw CommandException(
|
|
139
|
+
"clipboard: 宿主未装配(用 .plugin(ClipboardPlugin()) 装配插件后才可用)")
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private func requireString(args: JsonObject, key: String): String {
|
|
144
|
+
match (args.get(key)) {
|
|
145
|
+
case Some(v) => match (v.kind()) {
|
|
146
|
+
case JsonKind.JsString => return v.asString().getValue()
|
|
147
|
+
case _ => throw CommandException("clipboard: argument \"${key}\" must be a string")
|
|
148
|
+
}
|
|
149
|
+
case None => throw CommandException("clipboard: missing argument \"${key}\"")
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|