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.
Files changed (158) hide show
  1. package/README.md +113 -99
  2. package/bin/cj-tauri.js +287 -287
  3. package/cjpm.toml +63 -52
  4. package/cli/cjpm.toml +22 -22
  5. package/cli/src/doctor.cj +308 -306
  6. package/cli/src/main.cj +129 -129
  7. package/cli/src/manifest.cj +632 -558
  8. package/cli/src/manifest_test.cj +87 -0
  9. package/cli/src/project.cj +770 -770
  10. package/cli/src/resolve.cj +348 -348
  11. package/cli/src/scaffold.cj +369 -369
  12. package/cli/src/sync.cj +526 -526
  13. package/cli/src/util.cj +168 -168
  14. package/cli/templates/app/README.md +51 -48
  15. package/cli/templates/app/assets/capabilities/default.json +7 -7
  16. package/cli/templates/app/assets/ui/index.html +120 -118
  17. package/cli/templates/app/cj-tauri.toml +32 -22
  18. package/cli/templates/app/cjpm.toml +34 -34
  19. package/cli/templates/app/src/main.cj +101 -90
  20. package/cli/templates/app-react/README.md +56 -56
  21. package/cli/templates/app-react/assets/capabilities/default.json +7 -7
  22. package/cli/templates/app-react/assets/ui/index.html +25 -25
  23. package/cli/templates/app-react/assets/ui/package.json +19 -19
  24. package/cli/templates/app-react/assets/ui/src/App.css +67 -67
  25. package/cli/templates/app-react/assets/ui/src/App.jsx +124 -124
  26. package/cli/templates/app-react/assets/ui/src/main.jsx +6 -6
  27. package/cli/templates/app-react/assets/ui/vite.config.js +24 -24
  28. package/cli/templates/app-react/cj-tauri.toml +33 -23
  29. package/cli/templates/app-react/cjpm.toml +34 -34
  30. package/cli/templates/app-react/src/main.cj +91 -91
  31. package/cli/templates/app-vue/README.md +56 -56
  32. package/cli/templates/app-vue/assets/capabilities/default.json +7 -7
  33. package/cli/templates/app-vue/assets/ui/index.html +25 -25
  34. package/cli/templates/app-vue/assets/ui/package.json +18 -18
  35. package/cli/templates/app-vue/assets/ui/src/App.vue +176 -176
  36. package/cli/templates/app-vue/assets/ui/src/main.js +4 -4
  37. package/cli/templates/app-vue/assets/ui/vite.config.js +24 -24
  38. package/cli/templates/app-vue/cj-tauri.toml +33 -23
  39. package/cli/templates/app-vue/cjpm.toml +34 -34
  40. package/cli/templates/app-vue/src/main.cj +91 -91
  41. package/cli/templates/ohos-skeleton/AppScope/app.json5 +13 -13
  42. package/cli/templates/ohos-skeleton/AppScope/resources/base/element/string.json +8 -8
  43. package/cli/templates/ohos-skeleton/AppScope/resources/base/media/layered_image.json +6 -6
  44. package/cli/templates/ohos-skeleton/README.md +84 -79
  45. package/cli/templates/ohos-skeleton/build-hap.sh +15 -0
  46. package/cli/templates/ohos-skeleton/build-profile.json5 +46 -46
  47. package/cli/templates/ohos-skeleton/entry/build-profile.json5 +26 -26
  48. package/cli/templates/ohos-skeleton/entry/cjpm.toml +51 -43
  49. package/cli/templates/ohos-skeleton/entry/hvigorfile.ts +6 -6
  50. package/cli/templates/ohos-skeleton/entry/oh-package.json5 +9 -9
  51. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/ability_stage.cj +17 -17
  52. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/app_host.cj +94 -94
  53. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
  54. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -167
  55. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/main_ability.cj +139 -139
  56. package/cli/templates/ohos-skeleton/entry/src/main/module.json5 +57 -49
  57. package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/color.json +8 -8
  58. package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/string.json +16 -16
  59. package/cli/templates/ohos-skeleton/entry/src/main/resources/base/media/layered_image.json +6 -6
  60. package/cli/templates/ohos-skeleton/entry/src/main/resources/base/profile/main_pages.json +2 -2
  61. package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/capabilities.json +6 -6
  62. package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/index.html +92 -92
  63. package/cli/templates/ohos-skeleton/hvigor/hvigor-config.json5 +5 -5
  64. package/cli/templates/ohos-skeleton/hvigorfile.ts +6 -6
  65. package/cli/templates/ohos-skeleton/oh-package.json5 +10 -10
  66. package/native/build_win.bat +62 -62
  67. package/native/core/bridge_core.c +1681 -1626
  68. package/native/core/bridge_core.h +675 -618
  69. package/native/core/bridge_js.h +108 -108
  70. package/native/platforms/harmonyos/README.md +115 -6
  71. package/native/platforms/linux/bridge_linux.c +2787 -2485
  72. package/native/platforms/windows/bridge_win.c +3740 -3248
  73. package/native/tests/gtk_threading_probe.c +143 -143
  74. package/native/tests/test_bridge_core.c +1339 -1228
  75. package/package.json +53 -53
  76. package/prebuilt/linux-x64/cj-tauri +0 -0
  77. package/src/api_system.cj +67 -67
  78. package/src/app.cj +325 -969
  79. package/src/app_events.cj +166 -0
  80. package/src/app_host_ops.cj +287 -0
  81. package/src/app_run.cj +283 -0
  82. package/src/app_spec.cj +76 -76
  83. package/src/arkweb_menu.cj +216 -0
  84. package/src/arkweb_script.cj +142 -142
  85. package/src/capability.cj +409 -409
  86. package/src/clipboard_bridge.cj +178 -0
  87. package/src/dialog_spec.cj +79 -0
  88. package/src/dragdrop.cj +26 -26
  89. package/src/embedded_assets.cj +98 -98
  90. package/src/host.cj +752 -607
  91. package/src/host_arkweb.cj +924 -778
  92. package/src/host_webkit.cj +663 -570
  93. package/src/host_webview2.cj +663 -570
  94. package/src/hotkey.cj +66 -66
  95. package/src/ipc_hub.cj +337 -337
  96. package/src/ipc_message.cj +286 -286
  97. package/src/launch.cj +62 -62
  98. package/src/logging.cj +127 -127
  99. package/src/menu.cj +197 -197
  100. package/src/platform/platform_app_context.cj +77 -77
  101. package/src/platform/platform_assets.cj +33 -33
  102. package/src/platform/platform_discover.cj +176 -176
  103. package/src/platform/platform_env.cj +29 -29
  104. package/src/platform/platform_feature.cj +31 -31
  105. package/src/platform/platform_page_source.cj +87 -87
  106. package/src/platform/platform_target_info.cj +32 -32
  107. package/src/platform_host_provider.cj +87 -87
  108. package/src/plugin.cj +333 -333
  109. package/src/plugins/clipboard/plugin_clipboard.cj +152 -152
  110. package/src/plugins/dialog/plugin_dialog.cj +209 -209
  111. package/src/plugins/fs/plugin_fs.cj +105 -1
  112. package/src/plugins/http/http_backend.cj +131 -0
  113. package/src/plugins/http/http_desktop.cj +195 -0
  114. package/src/plugins/http/http_ohos.cj +204 -0
  115. package/src/plugins/http/plugin_http.cj +310 -459
  116. package/src/plugins/log/plugin_log.cj +105 -105
  117. package/src/plugins/menu/plugin_menu.cj +158 -158
  118. package/src/plugins/notification/plugin_notification.cj +154 -154
  119. package/src/plugins/serial/plugin_serial.cj +458 -454
  120. package/src/plugins/serial/serial_bridge.cj +194 -194
  121. package/src/plugins/serial/serial_manager_ohos.cj +92 -0
  122. package/src/plugins/serial/serial_ohos.cj +529 -0
  123. package/src/plugins/shell/plugin_shell.cj +258 -258
  124. package/src/plugins/sqlite/plugin_sqlite.cj +705 -710
  125. package/src/plugins/sqlite/sqlite_ffi.cj +206 -211
  126. package/src/plugins/ws/plugin_ws.cj +488 -470
  127. package/src/process_hidden.cj +174 -174
  128. package/src/scope.cj +391 -391
  129. package/src/shell_event.cj +69 -69
  130. package/src/tests/app_spec_test.cj +126 -127
  131. package/src/tests/capability_test.cj +243 -243
  132. package/src/tests/dialog_spec_test.cj +73 -0
  133. package/src/tests/fake_host.cj +201 -201
  134. package/src/tests/ipc_hub_test.cj +487 -487
  135. package/src/tests/logging_test.cj +62 -62
  136. package/src/tests/platform_contract_test.cj +251 -251
  137. package/src/tests/plugin_clipboard_test.cj +222 -222
  138. package/src/tests/plugin_dialog_test.cj +237 -237
  139. package/src/tests/plugin_fs_test.cj +207 -0
  140. package/src/tests/plugin_http_test.cj +124 -124
  141. package/src/tests/plugin_notification_test.cj +205 -205
  142. package/src/tests/plugin_serial_test.cj +48 -0
  143. package/src/tests/plugin_test.cj +605 -585
  144. package/src/tests/plugin_ws_test.cj +40 -40
  145. package/src/tests/process_hidden_test.cj +129 -129
  146. package/src/tests/scope_test.cj +350 -350
  147. package/src/tests/system_host_test.cj +99 -81
  148. package/src/tests/urlutil_test.cj +53 -53
  149. package/src/tests/version_test.cj +75 -75
  150. package/src/tests/window_registry_test.cj +123 -123
  151. package/src/tray.cj +90 -90
  152. package/src/urlutil.cj +159 -159
  153. package/src/version.cj +44 -44
  154. package/src/window.cj +51 -51
  155. package/src/window_registry.cj +116 -116
  156. package/prebuilt/win32-x64/cj-tauri.exe +0 -0
  157. package/src/plugins/serial/serial_ohos_stub.cj +0 -61
  158. 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
- [target.aarch64-linux-ohos.bin-dependencies]
29
- # 鸿蒙版 stdx:这里写**相对路径**,指向工程根上的 `stdx` 联接(build-hap.sh 用
30
- # CANGJIE_STDX_OHOS 建,不入库)。两条实测依据:
31
- # ① hvigor 只替换它自己的模板变量(${AARCH64_LIBS} 这组),写别的 ${...} 会变成空串,
32
- # 而空串会让**整条 path-option 一起失效**,报错却是
33
- # 「imports package 'stdx.encoding.json' ... not added as a dependency」,看着像漏配;
34
- # ② 本机绝对路径能编过,但换机器就得改文件,而 DevEco 里点 Build 不经过脚本——
35
- # 相对路径两处都通用。
36
- path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}", "../stdx"]
37
- package-option = {}
38
-
39
- [target.x86_64-linux-ohos]
40
- 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\""
41
- [target.x86_64-linux-ohos.bin-dependencies]
42
- path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}", "../stdx"]
43
- package-option = {}
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
+ }