cj-tauri 1.1.2 → 2.0.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 (101) hide show
  1. package/README.md +64 -18
  2. package/cjpm.toml +52 -52
  3. package/cli/cjpm.toml +10 -1
  4. package/cli/src/doctor.cj +4 -2
  5. package/cli/src/main.cj +11 -3
  6. package/cli/src/manifest.cj +558 -0
  7. package/cli/src/project.cj +38 -22
  8. package/cli/src/scaffold.cj +369 -361
  9. package/cli/src/sync.cj +526 -0
  10. package/cli/src/util.cj +168 -159
  11. package/cli/templates/app/README.md +48 -80
  12. package/cli/templates/app/{capabilities → assets/capabilities}/default.json +7 -7
  13. package/cli/templates/app/{ui → assets/ui}/index.html +118 -118
  14. package/cli/templates/app/cj-tauri.toml +22 -0
  15. package/cli/templates/app/cjpm.toml +34 -17
  16. package/cli/templates/app/src/main.cj +90 -88
  17. package/cli/templates/app-react/README.md +56 -67
  18. package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +7 -7
  19. package/cli/templates/app-react/{ui → assets/ui}/package.json +19 -19
  20. package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +6 -6
  21. package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +24 -24
  22. package/cli/templates/app-react/cj-tauri.toml +23 -0
  23. package/cli/templates/app-react/cjpm.toml +34 -17
  24. package/cli/templates/app-react/src/main.cj +91 -102
  25. package/cli/templates/app-vue/README.md +56 -60
  26. package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +7 -7
  27. package/cli/templates/app-vue/{ui → assets/ui}/package.json +18 -18
  28. package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +4 -4
  29. package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +24 -24
  30. package/cli/templates/app-vue/cj-tauri.toml +23 -0
  31. package/cli/templates/app-vue/cjpm.toml +34 -17
  32. package/cli/templates/app-vue/src/main.cj +91 -102
  33. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +13 -13
  34. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +8 -8
  35. package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +6 -6
  36. package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +79 -79
  37. package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +46 -46
  38. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +26 -26
  39. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +43 -43
  40. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +6 -6
  41. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +9 -9
  42. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +17 -17
  43. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +94 -94
  44. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/index.cj +167 -167
  45. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +139 -139
  46. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +49 -49
  47. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +8 -8
  48. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +16 -16
  49. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +6 -6
  50. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +2 -2
  51. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +6 -6
  52. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +92 -92
  53. package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +5 -5
  54. package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +6 -6
  55. package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +10 -10
  56. package/package.json +4 -2
  57. package/prebuilt/win32-x64/cj-tauri.exe +0 -0
  58. package/src/app.cj +49 -20
  59. package/src/app_spec.cj +76 -0
  60. package/src/arkweb_script.cj +142 -154
  61. package/src/host.cj +20 -17
  62. package/src/host_arkweb.cj +778 -740
  63. package/src/host_webkit.cj +22 -0
  64. package/src/host_webview2.cj +22 -0
  65. package/src/launch.cj +62 -0
  66. package/src/platform/platform_app_context.cj +77 -0
  67. package/src/platform/platform_assets.cj +33 -0
  68. package/src/platform/platform_discover.cj +176 -0
  69. package/src/platform/platform_env.cj +29 -0
  70. package/src/platform/platform_feature.cj +31 -0
  71. package/src/platform/platform_page_source.cj +87 -0
  72. package/src/platform/platform_target_info.cj +32 -0
  73. package/src/platform_host_provider.cj +87 -0
  74. package/src/plugin.cj +36 -2
  75. package/src/plugins/clipboard/plugin_clipboard.cj +8 -0
  76. package/src/plugins/menu/plugin_menu.cj +158 -150
  77. package/src/plugins/notification/plugin_notification.cj +8 -0
  78. package/src/plugins/serial/plugin_serial.cj +454 -442
  79. package/src/plugins/serial/serial_bridge.cj +194 -186
  80. package/src/plugins/serial/serial_ohos_stub.cj +61 -0
  81. package/src/plugins/shell/plugin_shell.cj +258 -230
  82. package/src/plugins/sqlite/plugin_sqlite.cj +710 -697
  83. package/src/plugins/sqlite/sqlite_ffi.cj +211 -206
  84. package/src/plugins/sqlite/sqlite_ohos_stub.cj +91 -0
  85. package/src/tests/app_spec_test.cj +127 -0
  86. package/src/tests/fake_host.cj +23 -8
  87. package/src/tests/platform_contract_test.cj +251 -0
  88. package/src/tests/plugin_test.cj +585 -542
  89. package/src/version.cj +1 -1
  90. package/src/host_factory.cj +0 -48
  91. /package/cli/templates/app-react/{ui → assets/ui}/index.html +0 -0
  92. /package/cli/templates/app-react/{ui → assets/ui}/src/App.css +0 -0
  93. /package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +0 -0
  94. /package/cli/templates/app-vue/{ui → assets/ui}/index.html +0 -0
  95. /package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +0 -0
  96. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
  97. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
  98. /package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +0 -0
  99. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
  100. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
  101. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
@@ -1,49 +1,49 @@
1
- {
2
- "module": {
3
- "name": "entry",
4
- "type": "entry",
5
- "description": "$string:module_desc",
6
- "mainElement": "EntryAbility",
7
- /* 鸿蒙 PC 必须声明 "2in1":只写 phone/tablet 时系统按手机应用处理,窗口是竖屏兼容窗。
8
- 构建插件默认把 deviceTypes 限制成 phone/tablet,靠 entry/build-profile.json5 的
9
- cangjieOptions.checkDeviceTypes=false 放行。 */
10
- "deviceTypes": [
11
- "2in1",
12
- "tablet",
13
- "phone"
14
- ],
15
- "deliveryWithInstall": true,
16
- /* 网上取数据(http/https)都需要这条:缺它时请求死在
17
- `SocketException: Failed to create socket: Operation not permitted.`,
18
- 页面上表现为「连不上后台」,看着像网络不通。用不到就删掉这一条。 */
19
- "requestPermissions": [
20
- {
21
- "name": "ohos.permission.INTERNET"
22
- }
23
- ],
24
- "installationFree": false,
25
- "srcEntry": "ohos_app_cangjie_entry.MyAbilityStage",
26
- "abilities": [
27
- {
28
- "name": "EntryAbility",
29
- "srcEntry": "ohos_app_cangjie_entry.MainAbility",
30
- "description": "$string:EntryAbility_desc",
31
- "icon": "$media:layered_image",
32
- "label": "$string:EntryAbility_label",
33
- "startWindowIcon": "$media:startIcon",
34
- "startWindowBackground": "$color:start_window_background",
35
- "exported": true,
36
- "skills": [
37
- {
38
- "entities": [
39
- "entity.system.home"
40
- ],
41
- "actions": [
42
- "action.system.home"
43
- ]
44
- }
45
- ]
46
- }
47
- ]
48
- }
49
- }
1
+ {
2
+ "module": {
3
+ "name": "entry",
4
+ "type": "entry",
5
+ "description": "$string:module_desc",
6
+ "mainElement": "EntryAbility",
7
+ /* 鸿蒙 PC 必须声明 "2in1":只写 phone/tablet 时系统按手机应用处理,窗口是竖屏兼容窗。
8
+ 构建插件默认把 deviceTypes 限制成 phone/tablet,靠 entry/build-profile.json5 的
9
+ cangjieOptions.checkDeviceTypes=false 放行。 */
10
+ "deviceTypes": [
11
+ "2in1",
12
+ "tablet",
13
+ "phone"
14
+ ],
15
+ "deliveryWithInstall": true,
16
+ /* 网上取数据(http/https)都需要这条:缺它时请求死在
17
+ `SocketException: Failed to create socket: Operation not permitted.`,
18
+ 页面上表现为「连不上后台」,看着像网络不通。用不到就删掉这一条。 */
19
+ "requestPermissions": [
20
+ {
21
+ "name": "ohos.permission.INTERNET"
22
+ }
23
+ ],
24
+ "installationFree": false,
25
+ "srcEntry": "ohos_app_cangjie_entry.MyAbilityStage",
26
+ "abilities": [
27
+ {
28
+ "name": "EntryAbility",
29
+ "srcEntry": "ohos_app_cangjie_entry.MainAbility",
30
+ "description": "$string:EntryAbility_desc",
31
+ "icon": "$media:layered_image",
32
+ "label": "$string:EntryAbility_label",
33
+ "startWindowIcon": "$media:startIcon",
34
+ "startWindowBackground": "$color:start_window_background",
35
+ "exported": true,
36
+ "skills": [
37
+ {
38
+ "entities": [
39
+ "entity.system.home"
40
+ ],
41
+ "actions": [
42
+ "action.system.home"
43
+ ]
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ }
@@ -1,8 +1,8 @@
1
- {
2
- "color": [
3
- {
4
- "name": "start_window_background",
5
- "value": "#16161D"
6
- }
7
- ]
8
- }
1
+ {
2
+ "color": [
3
+ {
4
+ "name": "start_window_background",
5
+ "value": "#16161D"
6
+ }
7
+ ]
8
+ }
@@ -1,16 +1,16 @@
1
- {
2
- "string": [
3
- {
4
- "name": "module_desc",
5
- "value": "{{APP_LABEL}}"
6
- },
7
- {
8
- "name": "EntryAbility_desc",
9
- "value": "{{APP_LABEL}}"
10
- },
11
- {
12
- "name": "EntryAbility_label",
13
- "value": "{{APP_LABEL}}"
14
- }
15
- ]
16
- }
1
+ {
2
+ "string": [
3
+ {
4
+ "name": "module_desc",
5
+ "value": "{{APP_LABEL}}"
6
+ },
7
+ {
8
+ "name": "EntryAbility_desc",
9
+ "value": "{{APP_LABEL}}"
10
+ },
11
+ {
12
+ "name": "EntryAbility_label",
13
+ "value": "{{APP_LABEL}}"
14
+ }
15
+ ]
16
+ }
@@ -1,6 +1,6 @@
1
- {
2
- "layered-image": {
3
- "background": "$media:background",
4
- "foreground": "$media:foreground"
5
- }
6
- }
1
+ {
2
+ "layered-image": {
3
+ "background": "$media:background",
4
+ "foreground": "$media:foreground"
5
+ }
6
+ }
@@ -1,6 +1,6 @@
1
- {
2
- "identifier": "default",
3
- "windows": ["main"],
4
- "commands": ["greet", "timer", "system:version", "system:ping", "system:echo"],
5
- "events": ["tick"]
6
- }
1
+ {
2
+ "identifier": "default",
3
+ "windows": ["main"],
4
+ "commands": ["greet", "timer", "system:version", "system:ping", "system:echo"],
5
+ "events": ["tick"]
6
+ }
@@ -1,92 +1,92 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <title>{{PROJECT_NAME}}</title>
6
- <style>
7
- :root { color-scheme: dark; }
8
- body { font-family: system-ui, "HarmonyOS Sans", "Segoe UI", sans-serif; background: #16161d;
9
- color: #e6e6e6; margin: 0; min-height: 100vh;
10
- display: flex; flex-direction: column; align-items: center; justify-content: center; }
11
- .card { background: #1f2029; border: 1px solid #2e3140; border-radius: 14px;
12
- padding: 28px 32px; width: min(520px, 86vw);
13
- box-shadow: 0 12px 32px rgba(0,0,0,.45); }
14
- h1 { margin: 0 0 4px; font-size: 22px; color: #7ce7ff; }
15
- .sub { margin: 0 0 18px; font-size: 13px; color: #9aa0b4; line-height: 1.6; }
16
- .row { display: flex; gap: 8px; margin: 14px 0; }
17
- input, button { font-size: 15px; padding: 9px 12px; border-radius: 9px; border: 1px solid transparent; }
18
- input { flex: 1; background: #141821; color: #e6e6e6; border-color: #2e3140; }
19
- input:focus { outline: none; border-color: #4a7fd4; }
20
- button { background: #e94560; color: #fff; cursor: pointer; font-weight: 600; }
21
- button:hover { opacity: .88; }
22
- button.ghost { background: #2b3040; }
23
- .result { margin-top: 6px; min-height: 24px; font-size: 15px; color: #50fa7b; word-break: break-all; }
24
- .tick { margin-top: 10px; font-size: 13px; color: #8be9fd; min-height: 18px; }
25
- .foot { margin-top: 18px; font-size: 12px; color: #6b7186; line-height: 1.7; }
26
- </style>
27
- </head>
28
- <body>
29
- <div class="card">
30
- <h1>{{PROJECT_NAME}}</h1>
31
- <p class="sub">仓颉后端(静态编译)+ ArkWeb 前端 &nbsp;·&nbsp; invoke / listen 双向 IPC &nbsp;·&nbsp; capability 安全模型</p>
32
-
33
- <div class="row">
34
- <input id="name" placeholder="输入名字" value="world">
35
- <button id="greet">greet</button>
36
- </div>
37
- <div class="row">
38
- <button id="timer" class="ghost">启动后端事件推送</button>
39
- <button id="version" class="ghost">system:version</button>
40
- </div>
41
-
42
- <div id="result" class="result">等待 invoke…</div>
43
- <div id="tick" class="tick"></div>
44
- <div class="foot">未在 rawfile/capabilities.json 里声明的命令会被后端拒绝</div>
45
- </div>
46
-
47
- <script>
48
- // 注入的桥(对标 @tauri-apps/api):window.__CJ_TAURI__
49
- // 鸿蒙的 ArkWeb 没有 document-start 注入,桥是在 onPageBegin 里注入的,
50
- // 所以页面必须「等桥出现」而不是模块作用域直接读。
51
- function withBridge(cb) {
52
- if (window.__CJ_TAURI__) { cb(window.__CJ_TAURI__); return; }
53
- setTimeout(function () { withBridge(cb); }, 50);
54
- }
55
-
56
- withBridge(function (tauri) {
57
- var result = document.getElementById('result');
58
- var tick = document.getElementById('tick');
59
-
60
- function show(text, ok) {
61
- result.textContent = text;
62
- result.style.color = ok === false ? '#ff6b81' : '#50fa7b';
63
- }
64
-
65
- // JS → 仓颉
66
- document.getElementById('greet').onclick = function () {
67
- var name = document.getElementById('name').value || 'world';
68
- tauri.invoke('greet', { name: name })
69
- .then(function (data) { show('invoke OK: ' + data); })
70
- .catch(function (err) { show('invoke FAIL: ' + err.message, false); });
71
- };
72
-
73
- document.getElementById('version').onclick = function () {
74
- tauri.invoke('system:version')
75
- .then(function (data) { show('system:version -> ' + JSON.stringify(data)); })
76
- .catch(function (err) { show('system:version FAIL: ' + err.message, false); });
77
- };
78
-
79
- // 仓颉 → JS(事件推送)
80
- document.getElementById('timer').onclick = function () {
81
- tauri.invoke('timer')
82
- .then(function (d) { show('后端: ' + d); })
83
- .catch(function (err) { show('timer FAIL: ' + err.message, false); });
84
- };
85
-
86
- tauri.listen('tick', function (payload) {
87
- tick.textContent = '收到后端事件 tick #' + payload.n;
88
- });
89
- });
90
- </script>
91
- </body>
92
- </html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>{{PROJECT_NAME}}</title>
6
+ <style>
7
+ :root { color-scheme: dark; }
8
+ body { font-family: system-ui, "HarmonyOS Sans", "Segoe UI", sans-serif; background: #16161d;
9
+ color: #e6e6e6; margin: 0; min-height: 100vh;
10
+ display: flex; flex-direction: column; align-items: center; justify-content: center; }
11
+ .card { background: #1f2029; border: 1px solid #2e3140; border-radius: 14px;
12
+ padding: 28px 32px; width: min(520px, 86vw);
13
+ box-shadow: 0 12px 32px rgba(0,0,0,.45); }
14
+ h1 { margin: 0 0 4px; font-size: 22px; color: #7ce7ff; }
15
+ .sub { margin: 0 0 18px; font-size: 13px; color: #9aa0b4; line-height: 1.6; }
16
+ .row { display: flex; gap: 8px; margin: 14px 0; }
17
+ input, button { font-size: 15px; padding: 9px 12px; border-radius: 9px; border: 1px solid transparent; }
18
+ input { flex: 1; background: #141821; color: #e6e6e6; border-color: #2e3140; }
19
+ input:focus { outline: none; border-color: #4a7fd4; }
20
+ button { background: #e94560; color: #fff; cursor: pointer; font-weight: 600; }
21
+ button:hover { opacity: .88; }
22
+ button.ghost { background: #2b3040; }
23
+ .result { margin-top: 6px; min-height: 24px; font-size: 15px; color: #50fa7b; word-break: break-all; }
24
+ .tick { margin-top: 10px; font-size: 13px; color: #8be9fd; min-height: 18px; }
25
+ .foot { margin-top: 18px; font-size: 12px; color: #6b7186; line-height: 1.7; }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <div class="card">
30
+ <h1>{{PROJECT_NAME}}</h1>
31
+ <p class="sub">仓颉后端(静态编译)+ ArkWeb 前端 &nbsp;·&nbsp; invoke / listen 双向 IPC &nbsp;·&nbsp; capability 安全模型</p>
32
+
33
+ <div class="row">
34
+ <input id="name" placeholder="输入名字" value="world">
35
+ <button id="greet">greet</button>
36
+ </div>
37
+ <div class="row">
38
+ <button id="timer" class="ghost">启动后端事件推送</button>
39
+ <button id="version" class="ghost">system:version</button>
40
+ </div>
41
+
42
+ <div id="result" class="result">等待 invoke…</div>
43
+ <div id="tick" class="tick"></div>
44
+ <div class="foot">未在 rawfile/capabilities.json 里声明的命令会被后端拒绝</div>
45
+ </div>
46
+
47
+ <script>
48
+ // 注入的桥(对标 @tauri-apps/api):window.__CJ_TAURI__
49
+ // 鸿蒙的 ArkWeb 没有 document-start 注入,桥是在 onPageBegin 里注入的,
50
+ // 所以页面必须「等桥出现」而不是模块作用域直接读。
51
+ function withBridge(cb) {
52
+ if (window.__CJ_TAURI__) { cb(window.__CJ_TAURI__); return; }
53
+ setTimeout(function () { withBridge(cb); }, 50);
54
+ }
55
+
56
+ withBridge(function (tauri) {
57
+ var result = document.getElementById('result');
58
+ var tick = document.getElementById('tick');
59
+
60
+ function show(text, ok) {
61
+ result.textContent = text;
62
+ result.style.color = ok === false ? '#ff6b81' : '#50fa7b';
63
+ }
64
+
65
+ // JS → 仓颉
66
+ document.getElementById('greet').onclick = function () {
67
+ var name = document.getElementById('name').value || 'world';
68
+ tauri.invoke('greet', { name: name })
69
+ .then(function (data) { show('invoke OK: ' + data); })
70
+ .catch(function (err) { show('invoke FAIL: ' + err.message, false); });
71
+ };
72
+
73
+ document.getElementById('version').onclick = function () {
74
+ tauri.invoke('system:version')
75
+ .then(function (data) { show('system:version -> ' + JSON.stringify(data)); })
76
+ .catch(function (err) { show('system:version FAIL: ' + err.message, false); });
77
+ };
78
+
79
+ // 仓颉 → JS(事件推送)
80
+ document.getElementById('timer').onclick = function () {
81
+ tauri.invoke('timer')
82
+ .then(function (d) { show('后端: ' + d); })
83
+ .catch(function (err) { show('timer FAIL: ' + err.message, false); });
84
+ };
85
+
86
+ tauri.listen('tick', function (payload) {
87
+ tick.textContent = '收到后端事件 tick #' + payload.n;
88
+ });
89
+ });
90
+ </script>
91
+ </body>
92
+ </html>
@@ -1,5 +1,5 @@
1
- {
2
- "modelVersion": "6.1.1",
3
- "dependencies": {
4
- }
5
- }
1
+ {
2
+ "modelVersion": "6.1.1",
3
+ "dependencies": {
4
+ }
5
+ }
@@ -1,6 +1,6 @@
1
- import { appTasks } from '@ohos/hvigor-ohos-plugin';
2
-
3
- export default {
4
- system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
5
- plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
6
- }
1
+ import { appTasks } from '@ohos/hvigor-ohos-plugin';
2
+
3
+ export default {
4
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
5
+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
6
+ }
@@ -1,10 +1,10 @@
1
- {
2
- "modelVersion": "6.1.1",
3
- "description": "{{PROJECT_NAME}} - cj-tauri 鸿蒙 PC 应用",
4
- "dependencies": {
5
- },
6
- "devDependencies": {
7
- "@ohos/hypium": "1.0.25",
8
- "@ohos/hamock": "1.0.0"
9
- }
10
- }
1
+ {
2
+ "modelVersion": "6.1.1",
3
+ "description": "{{PROJECT_NAME}} - cj-tauri 鸿蒙 PC 应用",
4
+ "dependencies": {
5
+ },
6
+ "devDependencies": {
7
+ "@ohos/hypium": "1.0.25",
8
+ "@ohos/hamock": "1.0.0"
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cj-tauri",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "description": "仓颉语言的 Tauri:用仓颉写桌面应用后端 / 系统 WebView 写前端的混合开发框架脚手架 CLI(IPC 双向桥 + 能力安全模型,Windows / Linux / 鸿蒙 PC)",
5
5
  "keywords": [
6
6
  "cangjie",
@@ -18,7 +18,9 @@
18
18
  "scaffold",
19
19
  "cross-platform",
20
20
  "windows",
21
- "linux"
21
+ "linux",
22
+ "ohos",
23
+ "harmonyos"
22
24
  ],
23
25
  "license": "MIT",
24
26
  "bin": {
Binary file
package/src/app.cj CHANGED
@@ -3,6 +3,7 @@ package cjTauri
3
3
  import std.collection.*
4
4
  import std.env.*
5
5
  import stdx.encoding.json.*
6
+ import cjTauri.platform.*
6
7
 
7
8
  /**
8
9
  * 附加窗口的登记项(试验性 API `TauriApp.addWindow` 的装配状态)
@@ -38,7 +39,10 @@ class ExtraWindowSpec {
38
39
  public class TauriApp <: AppHost {
39
40
  private var hub: IpcHub = IpcHub()
40
41
  private var capabilities: CapabilityRegistry = CapabilityRegistry()
41
- private var host: WebViewHost = createHost(DEFAULT_WINDOW_LABEL)
42
+ // 批次 1(RFC-002 §5.4):注册表优先、内置实现兜底
43
+ private var host: WebViewHost = resolveHost(DEFAULT_WINDOW_LABEL)
44
+ // Asset 页面的解析器(批次 1 可注入;缺省按 cwd 相对路径读盘,清单派生在批次 2)
45
+ private var assetResolver: Option<AssetResolver> = None
42
46
  private var config: WindowConfig = WindowConfig()
43
47
  // 窗口注册表(批次 2):单窗口阶段只有一条 "main",多窗口时是「label → 宿主」的唯一权威来源
44
48
  private var windows: WindowRegistry = WindowRegistry()
@@ -63,6 +67,18 @@ public class TauriApp <: AppHost {
63
67
  return this
64
68
  }
65
69
 
70
+ /**
71
+ * 注入资产解析器(批次 1,RFC-002 §6)
72
+ *
73
+ * 供 run(Asset("ui/index.html")) 取页面与资产;不注入时按 cwd 相对路径读盘,
74
+ * 与现有模板 textOrFile 的桌面行为等价。鸿蒙 rawfile / 内嵌常量的实现由
75
+ * 各端适配层提供,批次 2 随清单派生接入。
76
+ */
77
+ public func assets(resolver: AssetResolver): TauriApp {
78
+ this.assetResolver = Some(resolver)
79
+ return this
80
+ }
81
+
66
82
  /**
67
83
  * 登记一个附加窗口(**试验性**:批次 3「多窗口 UI」的最小切片,先给出可验证的通路)
68
84
  *
@@ -80,7 +96,7 @@ public class TauriApp <: AppHost {
80
96
  if (this.started) {
81
97
  throw CommandException("addWindow 必须在 run() / runUrl() 之前调用(页面加载前窗口表要定稿)")
82
98
  }
83
- let host = createHost(label)
99
+ let host = resolveHost(label)
84
100
  host.setWindowConfig(config)
85
101
  // label 注入与默认窗口走同一条通道(document-start 预执行脚本,桥 JS 懒读,先后顺序无所谓)
86
102
  host.addInitScript("window.__CJ_TAURI_LABEL__ = ${JsonString(label)};")
@@ -541,39 +557,52 @@ public class TauriApp <: AppHost {
541
557
  }
542
558
 
543
559
  /**
544
- * 启动应用:装配 IPC → 启动 WebView 宿主 → 阻塞运行
560
+ * 启动应用:装配 IPC → 启动 WebView 宿主 → 阻塞运行(兼容入口,等价 run(Inline(html)))
545
561
  */
546
562
  public func run(html: String): Unit {
547
- this.prepare()
548
- if (this.dumpManifestIfRequested()) {
549
- return
550
- }
551
- this.registerPluginScripts()
552
- this.registerDefaultWindow()
553
- this.host.start(html)
554
- this.startExtraWindows()
555
- this.waitForExit()
556
- // 事件循环已退出:释放全部宿主编(幂等;句柄由 C 侧回收,见 docs/架构演进-多平台与多窗口.md §6.1)
557
- this.disposeWindows()
563
+ this.run(PageSource.Inline(html))
558
564
  }
559
565
 
560
566
  /**
561
- * 按 URL 启动应用(http(s):// 或 file://)
567
+ * 按 URL 启动应用(兼容入口,等价 run(Url(url)))
562
568
  *
563
569
  * 与 run(html) 平行:同一段装配,只是页面来源换成 URL。有了真实来源,
564
570
  * 相对路径资源、动态 import、前端路由才成立(file:// 页面禁止 fetch/XHR)。
565
571
  * 开发时可配合 TauriApp.devUrl() 指向前端 dev server(Vite 的 HMR 就这样接进来)。
566
572
  */
567
573
  public func runUrl(url: String): Unit {
574
+ this.run(PageSource.Url(url))
575
+ }
576
+
577
+ /**
578
+ * 统一页面入口(批次 1,RFC-002 §4.3):业务与清单只写 PageSource 一种表达
579
+ *
580
+ * - Inline(html):直接给 HTML(探针、错误页)
581
+ * - Asset(name):逻辑名,经注入的 AssetResolver 解析;未注入时按 cwd 相对路径读盘;
582
+ * devMode(CJ_TAURI_DEV_URL 存在)下自动改写为 DevServer(RFC §4.3)
583
+ * - Url(url) / DevServer(url):直载
584
+ *
585
+ * 装配流程与旧入口完全一致:dumpManifest → 插件 shim → 默认窗口 → 附加窗口 → 等待退出。
586
+ */
587
+ public func run(source: PageSource): Unit {
588
+ let src = rewriteForDev(source, TauriApp.devUrl())
589
+ this.runWith({ h => h.load(src) })
590
+ }
591
+
592
+ /** 装配与启动的共同骨架:所有页面入口在 load(source) 收敛,宿主怎么落地是 L0 的事(RFC §4.3/§6.2) */
593
+ private func runWith(start: (WebViewHost) -> Unit): Unit {
568
594
  this.prepare()
569
595
  if (this.dumpManifestIfRequested()) {
570
596
  return
571
597
  }
572
598
  this.registerPluginScripts()
573
599
  this.registerDefaultWindow()
574
- this.host.startUrl(url)
600
+ // 资产解析器随装配进宿主:桌面读盘、鸿蒙 rawfile 材化,各端 load() 自决怎么用
601
+ this.host.setAssetResolver(this.assetResolver)
602
+ start(this.host)
575
603
  this.startExtraWindows()
576
604
  this.waitForExit()
605
+ // 事件循环已退出:释放全部宿主编(幂等;句柄由 C 侧回收,见 docs/架构演进-多平台与多窗口.md §6.1)
577
606
  this.disposeWindows()
578
607
  }
579
608
 
@@ -599,7 +628,7 @@ public class TauriApp <: AppHost {
599
628
  /**
600
629
  * 把插件 jsShim 注册到宿主的「预执行脚本」通道
601
630
  *
602
- * 必须在 start / startUrl 之前调用:宿主创建 WebView 时一次性交给 C 桥,
631
+ * 必须在 load 之前调用:宿主创建 WebView 时一次性交给 C 桥,
603
632
  * 桥以 document-start 注入(早于页面脚本,两平台语义一致)。
604
633
  * HTML 与 URL 两种来源共用这条通道——v1 那个「runUrl 模式下 shim 注入不进去」的局限就此消掉(RFC §5.4),
605
634
  * 所以这里不再需要对使用者的额外提醒。
@@ -709,7 +738,7 @@ public class TauriApp <: AppHost {
709
738
  /**
710
739
  * 把默认窗口登记进注册表(label = "main")并给它注入 label
711
740
  *
712
- * 必须在 start() / startUrl() 之前:① 页面一加载就可能发 invoke / emit,回执要按 label 找到窗口;
741
+ * 必须在 load() 之前:① 页面一加载就可能发 invoke / emit,回执要按 label 找到窗口;
713
742
  * ② label 走宿主的预执行脚本通道(document-start)注入,晚于 start 就没机会了。
714
743
  * 桥 JS 是**懒读** `window.__CJ_TAURI_LABEL__` 的(首次用到才取、取到即缓存),
715
744
  * 所以注入脚本与桥 JS 的先后顺序无所谓——两平台的注入时机本就不同。
@@ -894,9 +923,9 @@ public class TauriApp <: AppHost {
894
923
  private func startExtraWindows(): Unit {
895
924
  for (w in this.extraWindows) {
896
925
  this.wireHostCallbacks(w.host)
897
- // shell 事件分发要在 start 之前装(菜单点击随时可能来,装晚了就漏)
926
+ // shell 事件分发要在 load 之前装(菜单点击随时可能来,装晚了就漏)
898
927
  this.wireShellEvents(w.host, w.label)
899
- w.host.start(w.html)
928
+ w.host.load(PageSource.Inline(w.html))
900
929
  eprintln("[cj-tauri] 附加窗口已启动:label=\"${w.label}\"")
901
930
  }
902
931
  }
@@ -0,0 +1,76 @@
1
+ package cjTauri
2
+
3
+ import std.collection.*
4
+ import cjTauri.platform.*
5
+
6
+ /**
7
+ * 应用装配声明(批次 3,RFC-002 §4.1):`build()` 的返回类型
8
+ *
9
+ * 业务只描述「应用是什么」——页面 / 窗口 / 插件 / 资产 / 命令装配点——
10
+ * 不描述「在哪跑」:`launch(spec)` 在框架侧按目标分派(§4.2),
11
+ * 平台知识只存在于 L0(§5)。
12
+ *
13
+ * 批次 3-PR1 落地形态(§4.1 的补记):
14
+ * - 命令装配点是 `wire` 闭包:拿到装配中的 TauriApp 后 `register`
15
+ * (业务命令多需要 app 自身,如 `music:quit`)。§4.4 的编译期命令表
16
+ * (generated registry)是批次 3 的后续 PR,届时 `wire` 收敛为类型化字段;
17
+ * - 构造器链:RFC §4.1「示意写法;落地用构造器链」。
18
+ */
19
+ public class AppSpec {
20
+ /** 应用标识(与清单 [app] id 一致;生成器填入,手写装配建议补上) */
21
+ public var appId: String = ""
22
+ public var name: String = ""
23
+
24
+ /** 页面来源:只有一种表达(§4.3) */
25
+ public var pageSource: PageSource = PageSource.Asset("ui/index.html")
26
+ /** 默认窗口配置 */
27
+ public var windowConfig: WindowConfig = WindowConfig()
28
+ /** 插件清单:jsShim 与命令由框架在 run 前统一注册 */
29
+ public var plugins: ArrayList<Plugin> = ArrayList<Plugin>()
30
+ /** 资产解析器:None = TauriApp 按 cwd 相对路径读盘(与批次 1 行为一致) */
31
+ public var assetResolver: Option<AssetResolver> = None
32
+ /** 能力清单目录:显式挂载 = 框架不自动扫描(目录缺失时安静返回 0,不报错) */
33
+ public var capabilitiesDir: String = ""
34
+ /** 能力清单 JSON 原文列表(批次 3-PR3):给没有目录概念的端(鸿蒙 rawfile);与目录可并存 */
35
+ public var capabilityJsons: ArrayList<String> = ArrayList<String>()
36
+ /** 命令装配点:拿到装配中的 TauriApp 后 register(字段名带 Fn:仓颉字段与方法同命名空间,不能与构造器 wire() 重名) */
37
+ public var wireFn: Option<(TauriApp) -> Unit> = None
38
+
39
+ public init() {}
40
+
41
+ public func page(source: PageSource): AppSpec {
42
+ this.pageSource = source
43
+ return this
44
+ }
45
+
46
+ public func window(config: WindowConfig): AppSpec {
47
+ this.windowConfig = config
48
+ return this
49
+ }
50
+
51
+ public func plugin(p: Plugin): AppSpec {
52
+ this.plugins.add(p)
53
+ return this
54
+ }
55
+
56
+ public func assets(resolver: AssetResolver): AppSpec {
57
+ this.assetResolver = Some(resolver)
58
+ return this
59
+ }
60
+
61
+ public func capabilities(dir: String): AppSpec {
62
+ this.capabilitiesDir = dir
63
+ return this
64
+ }
65
+
66
+ /** 挂载一份能力清单 JSON(多份多次调用;与 capabilities 目录不互斥) */
67
+ public func capabilityJson(json: String): AppSpec {
68
+ this.capabilityJsons.add(json)
69
+ return this
70
+ }
71
+
72
+ public func wire(f: (TauriApp) -> Unit): AppSpec {
73
+ this.wireFn = Some(f)
74
+ return this
75
+ }
76
+ }