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,43 +1,51 @@
|
|
|
1
|
-
[package]
|
|
2
|
-
cjc-version = "1.1.3"
|
|
3
|
-
compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION_ENTRY}\""
|
|
4
|
-
description = "{{PROJECT_NAME}} - cj-tauri 鸿蒙入 module"
|
|
5
|
-
name = "ohos_app_cangjie_entry"
|
|
6
|
-
output-type = "dynamic"
|
|
7
|
-
src-dir = "./src/main/cangjie"
|
|
8
|
-
target-dir = ""
|
|
9
|
-
version = "1.0.0"
|
|
10
|
-
|
|
11
|
-
[dependencies]
|
|
12
|
-
# 源码依赖:hvigor 的 CompileCangjie 会把框架各包按本模块(dynamic)需要的形态编进来。
|
|
13
|
-
# 路径由 `cj-tauri create --template ohos` 写成**本机绝对路径**;换机器 / 挪仓库后重跑一次
|
|
14
|
-
# create,或手改这一行(与桌面模板同一条规矩)。
|
|
15
|
-
cjTauri = { path = "{{FRAMEWORK_ROOT_TOML}}" }
|
|
16
|
-
|
|
17
|
-
[profile]
|
|
18
|
-
[profile.build]
|
|
19
|
-
incremental = true
|
|
20
|
-
lto = ""
|
|
21
|
-
[profile.customized-option]
|
|
22
|
-
debug = "-g -Woff all -Won apilevel-check"
|
|
23
|
-
release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
|
|
24
|
-
[profile.test]
|
|
25
|
-
|
|
26
|
-
[target.aarch64-linux-ohos]
|
|
27
|
-
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
[package]
|
|
2
|
+
cjc-version = "1.1.3"
|
|
3
|
+
compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION_ENTRY}\""
|
|
4
|
+
description = "{{PROJECT_NAME}} - cj-tauri 鸿蒙入 module"
|
|
5
|
+
name = "ohos_app_cangjie_entry"
|
|
6
|
+
output-type = "dynamic"
|
|
7
|
+
src-dir = "./src/main/cangjie"
|
|
8
|
+
target-dir = ""
|
|
9
|
+
version = "1.0.0"
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
# 源码依赖:hvigor 的 CompileCangjie 会把框架各包按本模块(dynamic)需要的形态编进来。
|
|
13
|
+
# 路径由 `cj-tauri create --template ohos` 写成**本机绝对路径**;换机器 / 挪仓库后重跑一次
|
|
14
|
+
# create,或手改这一行(与桌面模板同一条规矩)。
|
|
15
|
+
cjTauri = { path = "{{FRAMEWORK_ROOT_TOML}}" }
|
|
16
|
+
|
|
17
|
+
[profile]
|
|
18
|
+
[profile.build]
|
|
19
|
+
incremental = true
|
|
20
|
+
lto = ""
|
|
21
|
+
[profile.customized-option]
|
|
22
|
+
debug = "-g -Woff all -Won apilevel-check"
|
|
23
|
+
release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
|
|
24
|
+
[profile.test]
|
|
25
|
+
|
|
26
|
+
[target.aarch64-linux-ohos]
|
|
27
|
+
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
|
|
28
|
+
# -lpasteboard -ludmf:框架的剪贴板宿主能力(`src/clipboard_bridge.cj` 鸿蒙段)用原生
|
|
29
|
+
# `OH_Pasteboard_*` / `OH_Udmf*` / `OH_UdsPlainText_*` C API。两组符号分属两个库
|
|
30
|
+
# (`OH_Pasteboard_*` 在 libpasteboard、其余在 libudmf),所以两个都要给。
|
|
31
|
+
# 最终链接发生在**本模块**(output-type = dynamic),所以开关必须在这儿落地——
|
|
32
|
+
# 只写在框架自己的 cjpm.toml 里不生效,缺了模块里就留一堆 `U OH_Pasteboard_*`,
|
|
33
|
+
# 表现是应用启动即 SIGSEGV(模块加载期解析不到符号)。
|
|
34
|
+
# 库在同 sysroot 的 `usr/lib/aarch64-linux-ohos`,上面 compile-option 的 `-L` 已指着它。
|
|
35
|
+
link-option = "-lpasteboard -ludmf"
|
|
36
|
+
[target.aarch64-linux-ohos.bin-dependencies]
|
|
37
|
+
# 鸿蒙版 stdx:这里写**相对路径**,指向工程根上的 `stdx` 联接(build-hap.sh 用
|
|
38
|
+
# CANGJIE_STDX_OHOS 建,不入库)。两条实测依据:
|
|
39
|
+
# ① hvigor 只替换它自己的模板变量(${AARCH64_LIBS} 这组),写别的 ${...} 会变成空串,
|
|
40
|
+
# 而空串会让**整条 path-option 一起失效**,报错却是
|
|
41
|
+
# 「imports package 'stdx.encoding.json' ... not added as a dependency」,看着像漏配;
|
|
42
|
+
# ② 本机绝对路径能编过,但换机器就得改文件,而 DevEco 里点 Build 不经过脚本——
|
|
43
|
+
# 相对路径两处都通用。
|
|
44
|
+
path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}", "../stdx"]
|
|
45
|
+
package-option = {}
|
|
46
|
+
|
|
47
|
+
[target.x86_64-linux-ohos]
|
|
48
|
+
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
|
|
49
|
+
[target.x86_64-linux-ohos.bin-dependencies]
|
|
50
|
+
path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}", "../stdx"]
|
|
51
|
+
package-option = {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
|
5
|
-
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
|
|
6
|
-
}
|
|
1
|
+
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
|
5
|
+
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
|
|
6
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "entry",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "{{PROJECT_NAME}} - entry 模块",
|
|
5
|
-
"main": "",
|
|
6
|
-
"author": "",
|
|
7
|
-
"license": "",
|
|
8
|
-
"dependencies": {}
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "entry",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{PROJECT_NAME}} - entry 模块",
|
|
5
|
+
"main": "",
|
|
6
|
+
"author": "",
|
|
7
|
+
"license": "",
|
|
8
|
+
"dependencies": {}
|
|
9
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
package ohos_app_cangjie_entry
|
|
2
|
-
|
|
3
|
-
import kit.AbilityKit.AbilityStage
|
|
4
|
-
import kit.PerformanceAnalysisKit.Hilog
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* AbilityStage:模块级入口(module.json5 的 srcEntry 指向它)
|
|
8
|
-
*
|
|
9
|
-
* 只打一行 hilog。鸿蒙侧排查的第一句话是「AbilityStage 有没有被创建」——
|
|
10
|
-
* 应用启动即崩(比如框架包初始化链坏了)时,hilog 里连这一行都没有,
|
|
11
|
-
* 而 CANGJIE-RUNTIME 的行照常在,很容易被误读成「生命周期卡死」。
|
|
12
|
-
*/
|
|
13
|
-
class MyAbilityStage <: AbilityStage {
|
|
14
|
-
public override func onCreate(): Unit {
|
|
15
|
-
Hilog.info(1, "CjTauri", "MyAbilityStage onCreated.")
|
|
16
|
-
}
|
|
17
|
-
}
|
|
1
|
+
package ohos_app_cangjie_entry
|
|
2
|
+
|
|
3
|
+
import kit.AbilityKit.AbilityStage
|
|
4
|
+
import kit.PerformanceAnalysisKit.Hilog
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* AbilityStage:模块级入口(module.json5 的 srcEntry 指向它)
|
|
8
|
+
*
|
|
9
|
+
* 只打一行 hilog。鸿蒙侧排查的第一句话是「AbilityStage 有没有被创建」——
|
|
10
|
+
* 应用启动即崩(比如框架包初始化链坏了)时,hilog 里连这一行都没有,
|
|
11
|
+
* 而 CANGJIE-RUNTIME 的行照常在,很容易被误读成「生命周期卡死」。
|
|
12
|
+
*/
|
|
13
|
+
class MyAbilityStage <: AbilityStage {
|
|
14
|
+
public override func onCreate(): Unit {
|
|
15
|
+
Hilog.info(1, "CjTauri", "MyAbilityStage onCreated.")
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
package ohos_app_cangjie_entry
|
|
2
|
-
|
|
3
|
-
import cjTauri.*
|
|
4
|
-
import stdx.encoding.json.*
|
|
5
|
-
import kit.PerformanceAnalysisKit.Hilog
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
* {{PROJECT_NAME}} 的框架装配(鸿蒙侧)
|
|
9
|
-
*
|
|
10
|
-
* 桌面的 `main()` 在鸿蒙上不成立——鸿蒙应用没有「自己起进程跑一个 main」这回事,
|
|
11
|
-
* 窗口属于 ArkUI、页面由 ArkWeb 组件承载。所以装配被拆成三段:
|
|
12
|
-
* ① `MainAbility.onCreate`:定沙箱目录(`run(html)` 要把页面落到沙箱再 file:// 加载)
|
|
13
|
-
* ② `MainAbility.onCreate` 里 `spawn { startTauriApp(...) }`:**必须非 UI 线程**,
|
|
14
|
-
* `run()` 会阻塞到 quit(),而 UI 线程要留给 ArkUI 建树
|
|
15
|
-
* ③ `onWindowStageCreate`:等宿主登记 → `loadContent` → 组件里建 `Web`
|
|
16
|
-
*
|
|
17
|
-
* 页面与能力清单的来路:都来自 hap 的 `resources/rawfile/`,由 Ability 读成字符串传进来
|
|
18
|
-
* (设备上没有 `ui/` 与 `capabilities/` 目录,这两份是构建期打进 hap 的)。
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** greet 命令(对标 tauri command):前端 `tauri.invoke("greet", { name: "..." })` */
|
|
22
|
-
public class GreetCommand <: CommandHandler {
|
|
23
|
-
public init() {}
|
|
24
|
-
|
|
25
|
-
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
26
|
-
var name = "world"
|
|
27
|
-
if (let Some(n) <- args.get("name")) {
|
|
28
|
-
match (n.kind()) {
|
|
29
|
-
case JsonKind.JsString => name = n.asString().getValue()
|
|
30
|
-
case _ => ()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return JsonString("Hello, ${name}! 来自仓颉后端(鸿蒙 ArkWeb 宿主)")
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 定时器命令:起一个后台线程,每秒向后端推一个 tick 事件(验证 仓颉 → JS 通道)
|
|
39
|
-
*
|
|
40
|
-
* 鸿蒙侧的 `emit` 同样只入队,真正的投递在 `onProxyPost` 里由页面心跳带出去——
|
|
41
|
-
* 所以页面必须一直在跑(`setInterval` 的心跳由框架的桥脚本负责,业务代码不用管)。
|
|
42
|
-
*/
|
|
43
|
-
public class TimerCommand <: CommandHandler {
|
|
44
|
-
private var counter: Int64 = 0
|
|
45
|
-
private var started: Bool = false
|
|
46
|
-
|
|
47
|
-
public init() {}
|
|
48
|
-
|
|
49
|
-
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
50
|
-
if (!this.started) {
|
|
51
|
-
this.started = true
|
|
52
|
-
spawn {
|
|
53
|
-
while (true) {
|
|
54
|
-
sleep(Duration.second)
|
|
55
|
-
this.counter += 1
|
|
56
|
-
var payload = JsonObject()
|
|
57
|
-
payload.put("n", JsonInt(this.counter))
|
|
58
|
-
ipc.emit("tick", payload)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return JsonString("timer started")
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 装配并运行框架应用(**必须在非 UI 线程调用**:`run()` 阻塞到 `quit()`)
|
|
68
|
-
*
|
|
69
|
-
* @param windowTitle 窗口标题(ArkUI 侧不起作用,留着与桌面版对齐)
|
|
70
|
-
* @param pageHtml 页面原文(rawfile/index.html)
|
|
71
|
-
* @param capsJson 能力清单原文(rawfile/capabilities.json);空串表示不挂清单
|
|
72
|
-
*/
|
|
73
|
-
public func startTauriApp(windowTitle: String, pageHtml: String, capsJson: String): Unit {
|
|
74
|
-
Hilog.info(1, "CjTauri", "startTauriApp begin page=${pageHtml.size}B caps=${capsJson.size}B")
|
|
75
|
-
|
|
76
|
-
let app = TauriApp()
|
|
77
|
-
.window(WindowConfig(windowTitle, 1280, 820))
|
|
78
|
-
.register("greet", GreetCommand())
|
|
79
|
-
.register("timer", TimerCommand())
|
|
80
|
-
|
|
81
|
-
// 显式挂载能力清单即不自动扫描目录——hap 里本来也没有 capabilities/ 这个目录。
|
|
82
|
-
if (capsJson.size > 0) {
|
|
83
|
-
app.addCapabilityJson(JsonValue.fromStr(capsJson))
|
|
84
|
-
Hilog.info(1, "CjTauri", "capability embedded bytes=${capsJson.size}")
|
|
85
|
-
} else {
|
|
86
|
-
// 空清单 = 一条命令都不放行(含 system:*),页面会看到「command not allowed」。
|
|
87
|
-
// 这比静默放行安全,也比假装成功好排查。
|
|
88
|
-
Hilog.info(1, "CjTauri", "capability missing: rawfile/capabilities.json 没打进 hap,所有命令都会被拒")
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 阻塞到 quit():Ability 的生命周期仍归系统管,`run()` 返回不代表进程退出。
|
|
92
|
-
app.run(pageHtml)
|
|
93
|
-
Hilog.info(1, "CjTauri", "after run")
|
|
94
|
-
}
|
|
1
|
+
package ohos_app_cangjie_entry
|
|
2
|
+
|
|
3
|
+
import cjTauri.*
|
|
4
|
+
import stdx.encoding.json.*
|
|
5
|
+
import kit.PerformanceAnalysisKit.Hilog
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* {{PROJECT_NAME}} 的框架装配(鸿蒙侧)
|
|
9
|
+
*
|
|
10
|
+
* 桌面的 `main()` 在鸿蒙上不成立——鸿蒙应用没有「自己起进程跑一个 main」这回事,
|
|
11
|
+
* 窗口属于 ArkUI、页面由 ArkWeb 组件承载。所以装配被拆成三段:
|
|
12
|
+
* ① `MainAbility.onCreate`:定沙箱目录(`run(html)` 要把页面落到沙箱再 file:// 加载)
|
|
13
|
+
* ② `MainAbility.onCreate` 里 `spawn { startTauriApp(...) }`:**必须非 UI 线程**,
|
|
14
|
+
* `run()` 会阻塞到 quit(),而 UI 线程要留给 ArkUI 建树
|
|
15
|
+
* ③ `onWindowStageCreate`:等宿主登记 → `loadContent` → 组件里建 `Web`
|
|
16
|
+
*
|
|
17
|
+
* 页面与能力清单的来路:都来自 hap 的 `resources/rawfile/`,由 Ability 读成字符串传进来
|
|
18
|
+
* (设备上没有 `ui/` 与 `capabilities/` 目录,这两份是构建期打进 hap 的)。
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** greet 命令(对标 tauri command):前端 `tauri.invoke("greet", { name: "..." })` */
|
|
22
|
+
public class GreetCommand <: CommandHandler {
|
|
23
|
+
public init() {}
|
|
24
|
+
|
|
25
|
+
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
26
|
+
var name = "world"
|
|
27
|
+
if (let Some(n) <- args.get("name")) {
|
|
28
|
+
match (n.kind()) {
|
|
29
|
+
case JsonKind.JsString => name = n.asString().getValue()
|
|
30
|
+
case _ => ()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return JsonString("Hello, ${name}! 来自仓颉后端(鸿蒙 ArkWeb 宿主)")
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 定时器命令:起一个后台线程,每秒向后端推一个 tick 事件(验证 仓颉 → JS 通道)
|
|
39
|
+
*
|
|
40
|
+
* 鸿蒙侧的 `emit` 同样只入队,真正的投递在 `onProxyPost` 里由页面心跳带出去——
|
|
41
|
+
* 所以页面必须一直在跑(`setInterval` 的心跳由框架的桥脚本负责,业务代码不用管)。
|
|
42
|
+
*/
|
|
43
|
+
public class TimerCommand <: CommandHandler {
|
|
44
|
+
private var counter: Int64 = 0
|
|
45
|
+
private var started: Bool = false
|
|
46
|
+
|
|
47
|
+
public init() {}
|
|
48
|
+
|
|
49
|
+
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
|
|
50
|
+
if (!this.started) {
|
|
51
|
+
this.started = true
|
|
52
|
+
spawn {
|
|
53
|
+
while (true) {
|
|
54
|
+
sleep(Duration.second)
|
|
55
|
+
this.counter += 1
|
|
56
|
+
var payload = JsonObject()
|
|
57
|
+
payload.put("n", JsonInt(this.counter))
|
|
58
|
+
ipc.emit("tick", payload)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return JsonString("timer started")
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 装配并运行框架应用(**必须在非 UI 线程调用**:`run()` 阻塞到 `quit()`)
|
|
68
|
+
*
|
|
69
|
+
* @param windowTitle 窗口标题(ArkUI 侧不起作用,留着与桌面版对齐)
|
|
70
|
+
* @param pageHtml 页面原文(rawfile/index.html)
|
|
71
|
+
* @param capsJson 能力清单原文(rawfile/capabilities.json);空串表示不挂清单
|
|
72
|
+
*/
|
|
73
|
+
public func startTauriApp(windowTitle: String, pageHtml: String, capsJson: String): Unit {
|
|
74
|
+
Hilog.info(1, "CjTauri", "startTauriApp begin page=${pageHtml.size}B caps=${capsJson.size}B")
|
|
75
|
+
|
|
76
|
+
let app = TauriApp()
|
|
77
|
+
.window(WindowConfig(windowTitle, 1280, 820))
|
|
78
|
+
.register("greet", GreetCommand())
|
|
79
|
+
.register("timer", TimerCommand())
|
|
80
|
+
|
|
81
|
+
// 显式挂载能力清单即不自动扫描目录——hap 里本来也没有 capabilities/ 这个目录。
|
|
82
|
+
if (capsJson.size > 0) {
|
|
83
|
+
app.addCapabilityJson(JsonValue.fromStr(capsJson))
|
|
84
|
+
Hilog.info(1, "CjTauri", "capability embedded bytes=${capsJson.size}")
|
|
85
|
+
} else {
|
|
86
|
+
// 空清单 = 一条命令都不放行(含 system:*),页面会看到「command not allowed」。
|
|
87
|
+
// 这比静默放行安全,也比假装成功好排查。
|
|
88
|
+
Hilog.info(1, "CjTauri", "capability missing: rawfile/capabilities.json 没打进 hap,所有命令都会被拒")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 阻塞到 quit():Ability 的生命周期仍归系统管,`run()` 返回不代表进程退出。
|
|
92
|
+
app.run(pageHtml)
|
|
93
|
+
Hilog.info(1, "CjTauri", "after run")
|
|
94
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
package ohos_app_cangjie_entry
|
|
2
|
+
|
|
3
|
+
import kit.ArkUI.*
|
|
4
|
+
import ohos.arkui.state_macro_manage.*
|
|
5
|
+
import std.collection.HashMap
|
|
6
|
+
import cjTauri.*
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* 「每个窗口已回填的请求 id」——组件与父组件共享的一格状态(同包、UI 线程内)。
|
|
10
|
+
*
|
|
11
|
+
* 为什么需要它:父组件要判断「当前这条还在展示、不能再取下一条」,而展示中这件事
|
|
12
|
+
* 只有组件自己知道(`answered` 是它的私有 @State)。把答案放在这里,两边读同一份,
|
|
13
|
+
* 就不用把闭包当构造参数传给 @Component(那条路宏支持有限,且多窗口下容易串台)。
|
|
14
|
+
*
|
|
15
|
+
* 没登记过的窗口回 -1:与 `dialogRequestId` 的初值相等,所以**首次**允许取请求。
|
|
16
|
+
*/
|
|
17
|
+
let dialogAnswered: HashMap<String, Int64> = HashMap<String, Int64>()
|
|
18
|
+
|
|
19
|
+
/** 取某窗口「已回填的请求 id」,没记录过返回 -1(= 还没有任何一条被答过) */
|
|
20
|
+
func dialogAnsweredIdOf(label: String): Int64 {
|
|
21
|
+
match (dialogAnswered.get(label)) {
|
|
22
|
+
case Some(v) => return v
|
|
23
|
+
case None => return -1
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 记下某窗口已回填的 id(回填发生在 UI 线程,与读取同线程,不需要锁) */
|
|
28
|
+
func markDialogAnswered(label: String, id: Int64): Unit {
|
|
29
|
+
dialogAnswered[label] = id
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* 对话框 overlay 的外观常量(0xAARRGGBB,Int64 就是 ResourceColor)。
|
|
34
|
+
*
|
|
35
|
+
* **遮罩必须带 alpha**:早先这里是 `Color.Black`(= 0xFF000000,完全不透明),
|
|
36
|
+
* 弹出时整页被一块黑板盖住——用户真机实测的原话是「背景全黑了」。
|
|
37
|
+
* 现在 0x66 ≈ 40% 不透明,页面内容透得出来,才像对话框盖在页面上。
|
|
38
|
+
*
|
|
39
|
+
* 这几个常量就是业务侧的定制位:改配色改这里,改布局改下面 `CjDialogHost.build()`。
|
|
40
|
+
* 想彻底换成自己的样式(比如页内 HTML 弹窗)就不要调 `dialog:message`——
|
|
41
|
+
* overlay 只是「零配置可用」的默认实现,不是唯一通路。
|
|
42
|
+
*/
|
|
43
|
+
let DIALOG_SCRIM: Int64 = 0x66000000
|
|
44
|
+
|
|
45
|
+
let DIALOG_CARD_BG: Int64 = 0xFFFFFFFF
|
|
46
|
+
|
|
47
|
+
let DIALOG_TITLE_TEXT: Int64 = 0xFF1A1A1A
|
|
48
|
+
|
|
49
|
+
let DIALOG_BODY_TEXT: Int64 = 0xFF44474F
|
|
50
|
+
|
|
51
|
+
/* 取消键的浅灰底:中性动作不抢主色,桌面惯例里主色只给「确定」 */
|
|
52
|
+
let DIALOG_BTN_NEUTRAL: Int64 = 0xFFF1F3F5
|
|
53
|
+
|
|
54
|
+
/* 确定键底色按 kind 区分:info 蓝 / warning 橙 / error 红;单键与双键共用这一支 */
|
|
55
|
+
let DIALOG_ACCENT_INFO: Int64 = 0xFF0A59F7
|
|
56
|
+
|
|
57
|
+
let DIALOG_ACCENT_WARNING: Int64 = 0xFFED6F21
|
|
58
|
+
|
|
59
|
+
let DIALOG_ACCENT_ERROR: Int64 = 0xFFE84026
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* kind → 强调色(`normalizeDialogKind` 是框架共享层的归一,认不出的 kind 已回落成 info)
|
|
63
|
+
*
|
|
64
|
+
* 只影响「确定键底色」这一个可视点:警告与错误在桌面惯例里都用颜色区分,
|
|
65
|
+
* 而单键/双键的**语义**判定在框架侧(`dialogShapeOf`),这里不重复判一次。
|
|
66
|
+
*/
|
|
67
|
+
func dialogAccentColor(kind: String): Int64 {
|
|
68
|
+
match (normalizeDialogKind(kind)) {
|
|
69
|
+
case "warning" => return DIALOG_ACCENT_WARNING
|
|
70
|
+
case "error" => return DIALOG_ACCENT_ERROR
|
|
71
|
+
case _ => return DIALOG_ACCENT_INFO
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* 宿主对话框的默认呈现(声明式 overlay)。
|
|
77
|
+
*
|
|
78
|
+
* ## 为什么必须是声明式
|
|
79
|
+
*
|
|
80
|
+
* 页面经 `dialog:message` 要求弹框时,命令是从 `javaScriptProxy` 的 `post` 回调同步栈上回来的;
|
|
81
|
+
* 在那条栈上调 `showAlertDialog` / `getPromptAction().showDialog`(含 `workerthread: true`)
|
|
82
|
+
* 会被 ArkUI **静默丢弃**——不抛异常、窗口不画、回调不来(取证见 docs/鸿蒙ArkWeb接入踩坑.md §10.11)。
|
|
83
|
+
* 所以唯一的落法是:回调里只置状态,`build()` 里按状态把 overlay 画成组件树的最后一个孩子。
|
|
84
|
+
*
|
|
85
|
+
* ## 为什么做成组件
|
|
86
|
+
*
|
|
87
|
+
* 内联在 `CjTauriWebView` 里时,业务想换个样子只能改模板——而 `targets/` 是 `cj-tauri sync`
|
|
88
|
+
* 的派生物,改了下次 sync 就没了。抽成组件后业务只需在根 `Stack` 里放一个实例:
|
|
89
|
+
*
|
|
90
|
+
* ```
|
|
91
|
+
* if (this.dialogActive) {
|
|
92
|
+
* CjDialogHost(windowLabel: this.windowLabel, requestId: this.dialogRequestId,
|
|
93
|
+
* title: this.dialogTitle, message: this.dialogMessage,
|
|
94
|
+
* twoKey: this.dialogIsConfirm, kind: this.dialogKind)
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* 调用点必须保持**纯调用**:挂链式修饰(`.width(...)` 之类)会让 `@Component` 宏展开时
|
|
99
|
+
* 补不上 `__gen_parent`,报的是「expected N arguments」(见 AGENTS §4)。
|
|
100
|
+
*
|
|
101
|
+
* ## 点完为什么自己会消失
|
|
102
|
+
*
|
|
103
|
+
* 按钮回填走模块级 `answerArkWebDialog`(框架队列),而父组件的 `dialogActive` 不会因为
|
|
104
|
+
* 一次点击而变——所以收起由组件自己记:`answered` 记住已回填的请求 id,`requestId`
|
|
105
|
+
* 与它不等时才画。下一轮请求进来父组件赋一个新的 `requestId`,卡片自然重新出现。
|
|
106
|
+
* 这条不依赖父组件回调,也就不需要把闭包当构造参数传进来。
|
|
107
|
+
*
|
|
108
|
+
* ## 收起后必须 visibility(None),不能只把子内容裁掉
|
|
109
|
+
*
|
|
110
|
+
* 组件要挂在外层 `Stack` 里就得始终返回一个根节点,所以外层 `Column` 收起来之后**仍然在**——
|
|
111
|
+
* 它是 100%×100% 的透明容器,在 ArkUI 里照样参与命中测试、照样吃掉落在页面上的点击。
|
|
112
|
+
* 真机第二轮反馈的原话是「点完对话框后,点其他没反应了」:卡片确实没了,整页却点不动,
|
|
113
|
+
* 凶手就是这层看不见的壳。
|
|
114
|
+
*
|
|
115
|
+
* `Visibility.Hidden` 不够——它只是不画,**布局占位与命中区都还在**;只有
|
|
116
|
+
* `Visibility.None` 才既不占位也不响应事件。所以这里两分:该画时 Visible,收起时 None。
|
|
117
|
+
* (同类风险:任何「常驻但条件内空的 overlay 容器」都要这么写,别只依赖背景透明。)
|
|
118
|
+
*/
|
|
119
|
+
@Component
|
|
120
|
+
class CjDialogHost {
|
|
121
|
+
/* label 决定回填给哪个窗口的宿主(多窗口下不能串台) */
|
|
122
|
+
@Prop
|
|
123
|
+
var windowLabel: String
|
|
124
|
+
|
|
125
|
+
/* 请求 id:父组件每来一条新请求就换一个值,组件据此判断该不该重新显示 */
|
|
126
|
+
@Prop
|
|
127
|
+
var requestId: Int64
|
|
128
|
+
|
|
129
|
+
@Prop
|
|
130
|
+
var title: String
|
|
131
|
+
|
|
132
|
+
@Prop
|
|
133
|
+
var message: String
|
|
134
|
+
|
|
135
|
+
/* true = 双键(取消 + 确定),false = 单键(只有确定);判定在框架侧,这里只消费 */
|
|
136
|
+
@Prop
|
|
137
|
+
var twoKey: Bool
|
|
138
|
+
|
|
139
|
+
/* info / warning / error,只用于确定键配色 */
|
|
140
|
+
@Prop
|
|
141
|
+
var kind: String
|
|
142
|
+
|
|
143
|
+
/* 已回填过的请求 id(-1 = 还没有)。@State 不允许与 private 同用,宏会拒绝。 */
|
|
144
|
+
@State
|
|
145
|
+
var answered: Int64 = -1
|
|
146
|
+
|
|
147
|
+
func build() {
|
|
148
|
+
/* 外层这一层 **不设背景**:遮罩只在「确实该画」的那一支里,否则收起的瞬间
|
|
149
|
+
整页会被一层灰蒙住不散(内联版第一版就有这个毛病)。 */
|
|
150
|
+
Column {
|
|
151
|
+
if (this.requestId != this.answered) {
|
|
152
|
+
Column {
|
|
153
|
+
Column(space: 14) {
|
|
154
|
+
Text(this.title)
|
|
155
|
+
.fontSize(19)
|
|
156
|
+
.fontWeight(FontWeight.Bold)
|
|
157
|
+
.fontColor(DIALOG_TITLE_TEXT)
|
|
158
|
+
.width(100.percent)
|
|
159
|
+
Text(this.message)
|
|
160
|
+
.fontSize(15)
|
|
161
|
+
.fontColor(DIALOG_BODY_TEXT)
|
|
162
|
+
.width(100.percent)
|
|
163
|
+
Row(space: 12) {
|
|
164
|
+
if (this.twoKey) {
|
|
165
|
+
Button("取消")
|
|
166
|
+
.fontSize(15)
|
|
167
|
+
.fontColor(DIALOG_BODY_TEXT)
|
|
168
|
+
.backgroundColor(DIALOG_BTN_NEUTRAL)
|
|
169
|
+
.onClick({ _ => this.answer(false) })
|
|
170
|
+
}
|
|
171
|
+
Button("确定")
|
|
172
|
+
.fontSize(15)
|
|
173
|
+
.fontColor(Color.White)
|
|
174
|
+
.backgroundColor(dialogAccentColor(this.kind))
|
|
175
|
+
.onClick({ _ => this.answer(true) })
|
|
176
|
+
}
|
|
177
|
+
.width(100.percent)
|
|
178
|
+
.justifyContent(FlexAlign.End)
|
|
179
|
+
}
|
|
180
|
+
.width(86.percent)
|
|
181
|
+
.constraintSize(maxWidth: 420)
|
|
182
|
+
.padding(20)
|
|
183
|
+
.backgroundColor(DIALOG_CARD_BG)
|
|
184
|
+
.borderRadius(16)
|
|
185
|
+
}
|
|
186
|
+
.width(100.percent)
|
|
187
|
+
.height(100.percent)
|
|
188
|
+
.justifyContent(FlexAlign.Center)
|
|
189
|
+
.backgroundColor(DIALOG_SCRIM)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
.width(100.percent)
|
|
193
|
+
.height(100.percent)
|
|
194
|
+
/* 收起后整块退出命中测试(详见类头注释):只裁子内容会留下 100% 的透明层,
|
|
195
|
+
真机上表现为「点完对话框后整页点不动」。 */
|
|
196
|
+
.visibility(if (this.requestId != this.answered) {
|
|
197
|
+
Visibility.Visible
|
|
198
|
+
} else {
|
|
199
|
+
Visibility.None
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 回填用户的选择并收起自己。
|
|
205
|
+
*
|
|
206
|
+
* 先记 `answered` 再回填:回填会进框架的队列锁,连点时第二下不该再压在同一个 id 上
|
|
207
|
+
* (那次回填会被宿主的 `awaitingDialogs` 校验拒掉,日志里看得见)。
|
|
208
|
+
*/
|
|
209
|
+
private func answer(confirmed: Bool): Unit {
|
|
210
|
+
let id = this.requestId
|
|
211
|
+
this.answered = id
|
|
212
|
+
/* 同时登记到共享状态:父组件靠它判断「上一条已答完、可以取下一条」,
|
|
213
|
+
否则 overlay 收起后父组件永远以为还在展示,第二次点菜单就不弹了。 */
|
|
214
|
+
markDialogAnswered(this.windowLabel, id)
|
|
215
|
+
answerArkWebDialog(this.windowLabel, id, confirmed)
|
|
216
|
+
}
|
|
217
|
+
}
|