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,56 +1,56 @@
1
- # {{PROJECT_NAME}}
2
-
3
- 基于 [cj-tauri]({{FRAMEWORK_ROOT}})(仓颉版 Tauri)+ React 18 + Vite 的应用:**仓颉后端 + 系统 WebView 前端**,
4
- 通过 `window.__CJ_TAURI__` 做 invoke/listen 双向 IPC,并由 capabilities 清单做最小权限管控。
5
-
6
- ## 目录结构(一份业务代码,骨架按目标派生)
7
-
8
- ```
9
- {{PROJECT_NAME}}/
10
- ├── cj-tauri.toml # 应用清单——单一真相(id/version/窗口/资产/能力目录)
11
- ├── cjpm.toml # 业务包(static 库;骨架以 path 依赖引用)
12
- ├── src/main.cj # 业务装配点 build(): AppSpec(零平台知识)
13
- ├── assets/
14
- │ ├── ui/ # React 18 + Vite 前端工程(package.json / vite.config.js / src/)
15
- │ └── capabilities/default.json # 能力白名单(命令 / 事件 / 权限集)
16
- └── targets/ # cj-tauri sync 生成的骨架(勿手改;check 判漂移)
17
- ├── desktop/ # 桌面可执行(launcher 调 build())
18
- └── ohos/ # 鸿蒙 hap 工程(DevEco + build-hap.sh)
19
- ```
20
-
21
- ## 首次运行(dev,热更新)
22
-
23
- ```bash
24
- cd assets/ui && npm install # 前端依赖(需要 Node.js)
25
- cd ../.. && cj-tauri sync # 派生 targets/ 骨架
26
- cj-tauri dev # 起 Vite dev server(改 .jsx 保存即刷新)+ 编译 + 启动窗口
27
- ```
28
-
29
- ## 发布构建
30
-
31
- ```bash
32
- cj-tauri build # 先在 assets/ui 跑 npm run build,再编后端
33
- cj-tauri run
34
- ```
35
-
36
- `vite.config.js` 用 vite-plugin-singlefile:产物是 `assets/ui/dist/index.html`
37
- (js/css 内联、无外部依赖)——正是清单 `[assets] page` 指向的页面。
38
-
39
- ## 鸿蒙端(PC / 2in1)
40
-
41
- ```bash
42
- export CANGJIE_STDX_OHOS=<鸿蒙版 stdx>/linux_ohos_aarch64_cjnative/dynamic/stdx
43
- cd targets/ohos && bash build-hap.sh # 编 hap
44
- cd targets/ohos && bash build-hap.sh --install # 编完装到设备并拉起
45
- ```
46
-
47
- 提示:模板不带签名证书——首次在 DevEco 里 File → Project Structure → Signing Configs
48
- 勾「Automatically generate signature」(要登录华为账号);不签名只能编出
49
- *-unsigned.hap,装不进设备。
50
-
51
- ## 命令与能力
52
-
53
- 命令在 `src/main.cj`(`greet` / `timer` + 演示菜单),白名单在 `assets/capabilities/default.json`——
54
- 两边必须对齐:`cj-tauri check` 的三方比对会交叉核对「能力声明 × 插件自描述 × 实际装配」。
55
- 插件在某端没有实现时(如 shell/sqlite 在鸿蒙端),骨架照样编得过,运行期调用得到
56
- 可照做的「端不支持」错误(RFC-002 §4.6.4 的 P-1 桩)。
1
+ # {{PROJECT_NAME}}
2
+
3
+ 基于 [cj-tauri]({{FRAMEWORK_ROOT}})(仓颉版 Tauri)+ React 18 + Vite 的应用:**仓颉后端 + 系统 WebView 前端**,
4
+ 通过 `window.__CJ_TAURI__` 做 invoke/listen 双向 IPC,并由 capabilities 清单做最小权限管控。
5
+
6
+ ## 目录结构(一份业务代码,骨架按目标派生)
7
+
8
+ ```
9
+ {{PROJECT_NAME}}/
10
+ ├── cj-tauri.toml # 应用清单——单一真相(id/version/窗口/资产/能力目录)
11
+ ├── cjpm.toml # 业务包(static 库;骨架以 path 依赖引用)
12
+ ├── src/main.cj # 业务装配点 build(): AppSpec(零平台知识)
13
+ ├── assets/
14
+ │ ├── ui/ # React 18 + Vite 前端工程(package.json / vite.config.js / src/)
15
+ │ └── capabilities/default.json # 能力白名单(命令 / 事件 / 权限集)
16
+ └── targets/ # cj-tauri sync 生成的骨架(勿手改;check 判漂移)
17
+ ├── desktop/ # 桌面可执行(launcher 调 build())
18
+ └── ohos/ # 鸿蒙 hap 工程(DevEco + build-hap.sh)
19
+ ```
20
+
21
+ ## 首次运行(dev,热更新)
22
+
23
+ ```bash
24
+ cd assets/ui && npm install # 前端依赖(需要 Node.js)
25
+ cd ../.. && cj-tauri sync # 派生 targets/ 骨架
26
+ cj-tauri dev # 起 Vite dev server(改 .jsx 保存即刷新)+ 编译 + 启动窗口
27
+ ```
28
+
29
+ ## 发布构建
30
+
31
+ ```bash
32
+ cj-tauri build # 先在 assets/ui 跑 npm run build,再编后端
33
+ cj-tauri run
34
+ ```
35
+
36
+ `vite.config.js` 用 vite-plugin-singlefile:产物是 `assets/ui/dist/index.html`
37
+ (js/css 内联、无外部依赖)——正是清单 `[assets] page` 指向的页面。
38
+
39
+ ## 鸿蒙端(PC / 2in1)
40
+
41
+ ```bash
42
+ export CANGJIE_STDX_OHOS=<鸿蒙版 stdx>/linux_ohos_aarch64_cjnative/dynamic/stdx
43
+ cd targets/ohos && bash build-hap.sh # 编 hap
44
+ cd targets/ohos && bash build-hap.sh --install # 编完装到设备并拉起
45
+ ```
46
+
47
+ 提示:模板不带签名证书——首次在 DevEco 里 File → Project Structure → Signing Configs
48
+ 勾「Automatically generate signature」(要登录华为账号);不签名只能编出
49
+ *-unsigned.hap,装不进设备。
50
+
51
+ ## 命令与能力
52
+
53
+ 命令在 `src/main.cj`(`greet` / `timer` + 演示菜单),白名单在 `assets/capabilities/default.json`——
54
+ 两边必须对齐:`cj-tauri check` 的三方比对会交叉核对「能力声明 × 插件自描述 × 实际装配」。
55
+ 插件在某端没有实现时(如 shell/sqlite 在鸿蒙端),骨架照样编得过,运行期调用得到
56
+ 可照做的「端不支持」错误(RFC-002 §4.6.4 的 P-1 桩)。
@@ -1,7 +1,7 @@
1
- {
2
- "identifier": "default",
3
- "windows": ["main"],
4
- "commands": ["greet", "timer", "system:version", "system:ping", "system:echo", "system:devtools"],
5
- "permissions": ["menu:state"],
6
- "events": ["tick", "menu:click"]
7
- }
1
+ {
2
+ "identifier": "default",
3
+ "windows": ["main"],
4
+ "commands": ["greet", "timer", "system:version", "system:ping", "system:echo", "system:devtools"],
5
+ "permissions": ["menu:state"],
6
+ "events": ["tick", "menu:click"]
7
+ }
@@ -1,25 +1,25 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>{{PROJECT_NAME}}</title>
7
- <style>
8
- /* 整页基础样式:组件样式在 App.css,这里给页面底色 + 让内容整列居中,
9
- 调色板与零 Node 模板、examples/hello 一致(#16161d 深色底)。 */
10
- :root { color-scheme: dark; }
11
- /* min-height(不是 height)+ 居中:内容比视口高时 body 跟着长,顶部不会被裁掉。 */
12
- body { box-sizing: border-box; margin: 0; padding: 24px; min-height: 100vh;
13
- display: flex; flex-direction: column; align-items: center; justify-content: center;
14
- background: #16161d; color: #e6e6e6;
15
- font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
16
- /* 标题 / 提示 / 卡片同一列居中:光靠 body 的 align-items 不够——挂载点只有一个子节点,
17
- 提示行(页面来源、桥状态)很长时会把整列撑宽,卡片反而贴在左边。 */
18
- #root { width: 100%; display: flex; flex-direction: column; align-items: center; }
19
- </style>
20
- </head>
21
- <body>
22
- <div id="root"></div>
23
- <script type="module" src="/src/main.jsx"></script>
24
- </body>
25
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{PROJECT_NAME}}</title>
7
+ <style>
8
+ /* 整页基础样式:组件样式在 App.css,这里给页面底色 + 让内容整列居中,
9
+ 调色板与零 Node 模板、examples/hello 一致(#16161d 深色底)。 */
10
+ :root { color-scheme: dark; }
11
+ /* min-height(不是 height)+ 居中:内容比视口高时 body 跟着长,顶部不会被裁掉。 */
12
+ body { box-sizing: border-box; margin: 0; padding: 24px; min-height: 100vh;
13
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
14
+ background: #16161d; color: #e6e6e6;
15
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
16
+ /* 标题 / 提示 / 卡片同一列居中:光靠 body 的 align-items 不够——挂载点只有一个子节点,
17
+ 提示行(页面来源、桥状态)很长时会把整列撑宽,卡片反而贴在左边。 */
18
+ #root { width: 100%; display: flex; flex-direction: column; align-items: center; }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div id="root"></div>
23
+ <script type="module" src="/src/main.jsx"></script>
24
+ </body>
25
+ </html>
@@ -1,19 +1,19 @@
1
- {
2
- "name": "cj-tauri-ui",
3
- "private": true,
4
- "version": "0.1.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build"
9
- },
10
- "dependencies": {
11
- "react": "^18.3.1",
12
- "react-dom": "^18.3.1"
13
- },
14
- "devDependencies": {
15
- "@vitejs/plugin-react": "^4.3.4",
16
- "vite": "^6.0.11",
17
- "vite-plugin-singlefile": "^2.1.0"
18
- }
19
- }
1
+ {
2
+ "name": "cj-tauri-ui",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build"
9
+ },
10
+ "dependencies": {
11
+ "react": "^18.3.1",
12
+ "react-dom": "^18.3.1"
13
+ },
14
+ "devDependencies": {
15
+ "@vitejs/plugin-react": "^4.3.4",
16
+ "vite": "^6.0.11",
17
+ "vite-plugin-singlefile": "^2.1.0"
18
+ }
19
+ }
@@ -1,67 +1,67 @@
1
- /* React 模板的示例样式:与 app-vue 模板的 <style scoped> 逐条对齐(class 名相同)。
2
- Vue 靠 SFC 自带样式块,React 没有 SFC,所以走「JS 里 import 一个 css」——Vite 原生支持。 */
3
- h1 {
4
- color: #7ce7ff;
5
- }
6
- .hint {
7
- font-size: 13px;
8
- opacity: 0.8;
9
- }
10
- .card {
11
- background: #16213e;
12
- border-radius: 12px;
13
- padding: 20px 24px;
14
- width: 560px;
15
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
16
- }
17
- .row {
18
- display: flex;
19
- gap: 8px;
20
- }
21
- input {
22
- flex: 1;
23
- padding: 8px 12px;
24
- border: none;
25
- border-radius: 8px;
26
- background: #0f3460;
27
- color: #eee;
28
- }
29
- button {
30
- padding: 8px 14px;
31
- border: none;
32
- border-radius: 8px;
33
- background: #e94560;
34
- color: #fff;
35
- cursor: pointer;
36
- }
37
- button.ghost {
38
- background: #24476e;
39
- }
40
- button:hover {
41
- opacity: 0.85;
42
- }
43
- .out {
44
- margin-top: 14px;
45
- font-size: 16px;
46
- }
47
- .tick {
48
- margin-top: 6px;
49
- font-size: 13px;
50
- color: #8be9fd;
51
- }
52
- .chk {
53
- margin-top: 12px;
54
- font-size: 13px;
55
- opacity: 0.85;
56
- display: flex;
57
- align-items: center;
58
- gap: 6px;
59
- }
60
- .chk input {
61
- flex: none;
62
- }
63
- .tip {
64
- margin-top: 14px;
65
- font-size: 12px;
66
- opacity: 0.7;
67
- }
1
+ /* React 模板的示例样式:与 app-vue 模板的 <style scoped> 逐条对齐(class 名相同)。
2
+ Vue 靠 SFC 自带样式块,React 没有 SFC,所以走「JS 里 import 一个 css」——Vite 原生支持。 */
3
+ h1 {
4
+ color: #7ce7ff;
5
+ }
6
+ .hint {
7
+ font-size: 13px;
8
+ opacity: 0.8;
9
+ }
10
+ .card {
11
+ background: #16213e;
12
+ border-radius: 12px;
13
+ padding: 20px 24px;
14
+ width: 560px;
15
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
16
+ }
17
+ .row {
18
+ display: flex;
19
+ gap: 8px;
20
+ }
21
+ input {
22
+ flex: 1;
23
+ padding: 8px 12px;
24
+ border: none;
25
+ border-radius: 8px;
26
+ background: #0f3460;
27
+ color: #eee;
28
+ }
29
+ button {
30
+ padding: 8px 14px;
31
+ border: none;
32
+ border-radius: 8px;
33
+ background: #e94560;
34
+ color: #fff;
35
+ cursor: pointer;
36
+ }
37
+ button.ghost {
38
+ background: #24476e;
39
+ }
40
+ button:hover {
41
+ opacity: 0.85;
42
+ }
43
+ .out {
44
+ margin-top: 14px;
45
+ font-size: 16px;
46
+ }
47
+ .tick {
48
+ margin-top: 6px;
49
+ font-size: 13px;
50
+ color: #8be9fd;
51
+ }
52
+ .chk {
53
+ margin-top: 12px;
54
+ font-size: 13px;
55
+ opacity: 0.85;
56
+ display: flex;
57
+ align-items: center;
58
+ gap: 6px;
59
+ }
60
+ .chk input {
61
+ flex: none;
62
+ }
63
+ .tip {
64
+ margin-top: 14px;
65
+ font-size: 12px;
66
+ opacity: 0.7;
67
+ }
@@ -1,124 +1,124 @@
1
- import { useEffect, useState } from 'react';
2
- import './App.css'; // 与 app-vue 模板等价的样式(React 没有 SFC,样式走独立的 css)
3
-
4
- // 桥(window.__CJ_TAURI__)由宿主注入,注入时机随平台/页面来源而异:Linux 宿主注入在文档开始,
5
- // 但发行态单文件里的内联 <script type="module"> 解析完就执行,谁先谁后与加载方式有关。
6
- // 所以统一「等桥出现再初始化」,不要在模块作用域直接读。
7
- let bridge = null;
8
-
9
- function waitForBridge(timeoutMs = 3000) {
10
- return new Promise((resolve) => {
11
- const t0 = Date.now();
12
- const tick = () => {
13
- if (window.__CJ_TAURI__) return resolve(window.__CJ_TAURI__);
14
- if (Date.now() - t0 >= timeoutMs) return resolve(null);
15
- setTimeout(tick, 20);
16
- };
17
- tick();
18
- });
19
- }
20
-
21
- export default function App() {
22
- const [name, setName] = useState('world');
23
- const [result, setResult] = useState('等待 invoke…');
24
- const [tick, setTick] = useState('');
25
- const [menuMsg, setMenuMsg] = useState('');
26
- const [sidebar, setSidebar] = useState(true);
27
- const [href, setHref] = useState('');
28
- const [bridgeOk, setBridgeOk] = useState(false);
29
-
30
- useEffect(() => {
31
- let alive = true; // 等桥是异步的:组件卸载后不要再回写状态
32
- (async () => {
33
- setHref(location.href);
34
- const t = await waitForBridge();
35
- if (!alive) return;
36
- bridge = t;
37
- if (!t) {
38
- setResult('未找到 window.__CJ_TAURI__');
39
- return;
40
- }
41
- setBridgeOk(true);
42
- t.listen('tick', (payload) => {
43
- setTick('事件 tick #' + payload.n + ' 来自仓颉后端');
44
- });
45
- // 原生菜单 → 页面(capabilities 里要声明 menu:click)
46
- t.listen('menu:click', (p) => {
47
- setMenuMsg('菜单被点: ' + p.id + '(enabled=' + p.enabled + ', checked=' + p.checked + ')');
48
- });
49
- })();
50
- return () => {
51
- alive = false;
52
- };
53
- }, []);
54
-
55
- async function greet() {
56
- if (!bridge) {
57
- setResult('桥未就绪');
58
- return;
59
- }
60
- try {
61
- const data = await bridge.invoke('greet', { name });
62
- setResult('invoke OK: ' + data);
63
- } catch (err) {
64
- setResult('invoke FAIL: ' + err.message);
65
- }
66
- }
67
-
68
- async function startTimer() {
69
- if (bridge) await bridge.invoke('timer');
70
- }
71
-
72
- function reload() {
73
- if (bridge) bridge.reload();
74
- }
75
-
76
- // 页面 → 原生菜单:menu 插件的前端 shim(window.__CJ_TAURI__.menu,注入在 document start)
77
- async function toggleSidebar(e) {
78
- const checked = e.target.checked;
79
- if (!bridge || !bridge.menu) {
80
- setResult('未注入 menu 插件 shim(页面来自 runUrl 时插件 JS 不在本进程)');
81
- return;
82
- }
83
- try {
84
- await bridge.menu.setChecked({ id: 'view.sidebar', checked });
85
- setResult('原生菜单「侧边栏」已同步为 ' + checked);
86
- } catch (err) {
87
- setResult('menu.setChecked FAIL: ' + err.message);
88
- }
89
- }
90
-
91
- return (
92
- <>
93
- <h1>cj-tauri · React + Vite</h1>
94
- <p className="hint">
95
- 页面来源:<code>{href}</code>
96
- <br />
97
- 桥状态:{bridgeOk ? '已注入 __CJ_TAURI__' : '未注入'}
98
- </p>
99
-
100
- <div className="card">
101
- <div className="row">
102
- <input
103
- value={name}
104
- placeholder="输入名字"
105
- onChange={(e) => setName(e.target.value)}
106
- />
107
- <button onClick={greet}>greet</button>
108
- <button className="ghost" onClick={startTimer}>timer</button>
109
- <button className="ghost" onClick={reload}>reload</button>
110
- </div>
111
- <div className="out">{result}</div>
112
- <div className="tick">{tick}</div>
113
- <div className="tick">{menuMsg}</div>
114
- <div className="chk">
115
- <input type="checkbox" checked={sidebar} onChange={toggleSidebar} />
116
- <span>侧边栏(对应原生菜单「视图 → 侧边栏」)</span>
117
- </div>
118
- <p className="tip">
119
- 改这个文件(<code>ui/src/App.jsx</code>)保存,页面会在不重启应用的情况下更新。
120
- </p>
121
- </div>
122
- </>
123
- );
124
- }
1
+ import { useEffect, useState } from 'react';
2
+ import './App.css'; // 与 app-vue 模板等价的样式(React 没有 SFC,样式走独立的 css)
3
+
4
+ // 桥(window.__CJ_TAURI__)由宿主注入,注入时机随平台/页面来源而异:Linux 宿主注入在文档开始,
5
+ // 但发行态单文件里的内联 <script type="module"> 解析完就执行,谁先谁后与加载方式有关。
6
+ // 所以统一「等桥出现再初始化」,不要在模块作用域直接读。
7
+ let bridge = null;
8
+
9
+ function waitForBridge(timeoutMs = 3000) {
10
+ return new Promise((resolve) => {
11
+ const t0 = Date.now();
12
+ const tick = () => {
13
+ if (window.__CJ_TAURI__) return resolve(window.__CJ_TAURI__);
14
+ if (Date.now() - t0 >= timeoutMs) return resolve(null);
15
+ setTimeout(tick, 20);
16
+ };
17
+ tick();
18
+ });
19
+ }
20
+
21
+ export default function App() {
22
+ const [name, setName] = useState('world');
23
+ const [result, setResult] = useState('等待 invoke…');
24
+ const [tick, setTick] = useState('');
25
+ const [menuMsg, setMenuMsg] = useState('');
26
+ const [sidebar, setSidebar] = useState(true);
27
+ const [href, setHref] = useState('');
28
+ const [bridgeOk, setBridgeOk] = useState(false);
29
+
30
+ useEffect(() => {
31
+ let alive = true; // 等桥是异步的:组件卸载后不要再回写状态
32
+ (async () => {
33
+ setHref(location.href);
34
+ const t = await waitForBridge();
35
+ if (!alive) return;
36
+ bridge = t;
37
+ if (!t) {
38
+ setResult('未找到 window.__CJ_TAURI__');
39
+ return;
40
+ }
41
+ setBridgeOk(true);
42
+ t.listen('tick', (payload) => {
43
+ setTick('事件 tick #' + payload.n + ' 来自仓颉后端');
44
+ });
45
+ // 原生菜单 → 页面(capabilities 里要声明 menu:click)
46
+ t.listen('menu:click', (p) => {
47
+ setMenuMsg('菜单被点: ' + p.id + '(enabled=' + p.enabled + ', checked=' + p.checked + ')');
48
+ });
49
+ })();
50
+ return () => {
51
+ alive = false;
52
+ };
53
+ }, []);
54
+
55
+ async function greet() {
56
+ if (!bridge) {
57
+ setResult('桥未就绪');
58
+ return;
59
+ }
60
+ try {
61
+ const data = await bridge.invoke('greet', { name });
62
+ setResult('invoke OK: ' + data);
63
+ } catch (err) {
64
+ setResult('invoke FAIL: ' + err.message);
65
+ }
66
+ }
67
+
68
+ async function startTimer() {
69
+ if (bridge) await bridge.invoke('timer');
70
+ }
71
+
72
+ function reload() {
73
+ if (bridge) bridge.reload();
74
+ }
75
+
76
+ // 页面 → 原生菜单:menu 插件的前端 shim(window.__CJ_TAURI__.menu,注入在 document start)
77
+ async function toggleSidebar(e) {
78
+ const checked = e.target.checked;
79
+ if (!bridge || !bridge.menu) {
80
+ setResult('未注入 menu 插件 shim(页面来自 runUrl 时插件 JS 不在本进程)');
81
+ return;
82
+ }
83
+ try {
84
+ await bridge.menu.setChecked({ id: 'view.sidebar', checked });
85
+ setResult('原生菜单「侧边栏」已同步为 ' + checked);
86
+ } catch (err) {
87
+ setResult('menu.setChecked FAIL: ' + err.message);
88
+ }
89
+ }
90
+
91
+ return (
92
+ <>
93
+ <h1>cj-tauri · React + Vite</h1>
94
+ <p className="hint">
95
+ 页面来源:<code>{href}</code>
96
+ <br />
97
+ 桥状态:{bridgeOk ? '已注入 __CJ_TAURI__' : '未注入'}
98
+ </p>
99
+
100
+ <div className="card">
101
+ <div className="row">
102
+ <input
103
+ value={name}
104
+ placeholder="输入名字"
105
+ onChange={(e) => setName(e.target.value)}
106
+ />
107
+ <button onClick={greet}>greet</button>
108
+ <button className="ghost" onClick={startTimer}>timer</button>
109
+ <button className="ghost" onClick={reload}>reload</button>
110
+ </div>
111
+ <div className="out">{result}</div>
112
+ <div className="tick">{tick}</div>
113
+ <div className="tick">{menuMsg}</div>
114
+ <div className="chk">
115
+ <input type="checkbox" checked={sidebar} onChange={toggleSidebar} />
116
+ <span>侧边栏(对应原生菜单「视图 → 侧边栏」)</span>
117
+ </div>
118
+ <p className="tip">
119
+ 改这个文件(<code>ui/src/App.jsx</code>)保存,页面会在不重启应用的情况下更新。
120
+ </p>
121
+ </div>
122
+ </>
123
+ );
124
+ }
@@ -1,6 +1,6 @@
1
- import { createRoot } from 'react-dom/client';
2
- import App from './App.jsx';
3
-
4
- // 这里不套 React.StrictMode:它在开发模式下会把 effect 跑两遍,
5
- // 而这个模板的 effect 里就要调 invoke / listen,双跑会让人误以为桥重复投递。
6
- createRoot(document.getElementById('root')).render(<App />);
1
+ import { createRoot } from 'react-dom/client';
2
+ import App from './App.jsx';
3
+
4
+ // 这里不套 React.StrictMode:它在开发模式下会把 effect 跑两遍,
5
+ // 而这个模板的 effect 里就要调 invoke / listen,双跑会让人误以为桥重复投递。
6
+ createRoot(document.getElementById('root')).render(<App />);