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,167 +1,167 @@
1
- package ohos_app_cangjie_entry
2
-
3
- import kit.ArkUI.*
4
- import kit.ArkWeb.*
5
- import kit.PerformanceAnalysisKit.Hilog
6
- import ohos.arkui.component.web.Web
7
- import ohos.arkui.state_macro_manage.*
8
- import cjTauri.*
9
-
10
- /*
11
- * 应用侧的 ArkWeb 宿主组件
12
- *
13
- * 这个 `@Component` **必须住在应用模块里**,不能由框架包提供:框架包一旦 import
14
- * `kit.ArkUI.*` / `kit.ArkWeb.*`,合并进应用动态库后包初始化链会跳到坏地址、启动即 SIGSEGV。
15
- * 框架侧只留 `arkWebProxyPost` / `arkWebProxyPoll` 两个不碰 ArkUI 的桥接函数。
16
- *
17
- * 单窗口版:label 用框架默认的 DEFAULT_WINDOW_LABEL。要做多窗口就照着加一份、
18
- * 换成别的 label(框架的宿主路由是按 label 查的,不会串台)。
19
- */
20
-
21
- /**
22
- * proxy 的 `post` 入口:先交给框架处理报文,再把队列里的脚本推回页面
23
- *
24
- * 为什么「顺手推」放在这里:`javaScriptProxy` 的回调就在 UI 线程上,而
25
- * `controller.runJavaScript` 也必须在 UI 线程调。页面 → 原生的**返回值通道不可用**
26
- * (实测恒为 `null`),所以鸿蒙侧只能「页面发心跳 → 原生在该回调里把待执行脚本推出去」,
27
- * 这是唯一的回投路径;框架会把它收到的 invoke 结果 / event 都排进这个队列。
28
- */
29
- func onProxyPost(label: String, raw: String, ctrl: WebviewController): String {
30
- let ack = arkWebProxyPost(label, raw)
31
- match (ArkWebRuntime.hostFor(label)) {
32
- case Some(h) =>
33
- let pending = h.takePendingScripts()
34
- var i = 0
35
- while (i < pending.size) {
36
- ctrl.runJavaScript(pending[i], { _, _ => () })
37
- i += 1
38
- }
39
- case None => ()
40
- }
41
- return ack
42
- }
43
-
44
- @Component
45
- class CjTauriWebView {
46
- /* label 决定:①页面里 __CJ_TAURI_LABEL__ ②报文里的 window 字段 ③宿主取哪份页面来源。
47
- 必须是 `@Prop`:普通成员会被宏展开成 ObservedProperty 形参,外面传字符串进来会报
48
- 「expected 4 arguments」。 */
49
- @Prop
50
- var windowLabel: String
51
-
52
- /* 组件生命周期内必须保持同一个 controller 实例(ArkWeb 的硬约束) */
53
- private let controller: WebviewController = WebviewController()
54
-
55
- /* 页面地址由宿主给定;@State 让它参与刷新(宿主登记页面往往晚于组件首次 build)。
56
- `@State` 不允许与 private/public/static 同用——宏会直接拒绝。 */
57
- @State
58
- var pageUrl: String = ""
59
-
60
- public func build() {
61
- Column {
62
- if (this.pageUrl.size > 0) {
63
- Web(src: this.pageUrl, controller: this.controller)
64
- .width(100.percent)
65
- .height(100.percent)
66
- .fileAccess(true)
67
- .javaScriptProxy(
68
- funcList: [
69
- { raw => onProxyPost(this.windowLabel, raw, this.controller) },
70
- /* `poll` 只为兼容页面上可能残留的旧桥脚本:返回值传不回 JS,
71
- 真正的脚本推送已在 `onProxyPost` 里完成。 */
72
- { _ => "" }
73
- ],
74
- name: ARKWEB_PROXY_NAME, methodList: ["post", "poll"],
75
- controller: this.controller
76
- )
77
- .onPageBegin({ _ => this.onPageBegin() })
78
- .onPageEnd({ _ => this.onPageEnd() })
79
- }
80
- }.width(100.percent).height(100.percent)
81
- }
82
-
83
- /**
84
- * 组件挂载钩子(首次 `build()` 之前调用一次)
85
- *
86
- * **必须在这里同步宿主地址**:宿主把页面落盘后才把 `file://` 地址放进自己的字段,
87
- * 而 `build()` 是声明式的一次性求值——`pageUrl` 仍为空就永远不建 `Web`,
88
- * `onPageBegin` 也就永远不会来,页面白屏。
89
- */
90
- protected override func aboutToAppear(): Unit {
91
- match (ArkWebRuntime.hostFor(this.windowLabel)) {
92
- case Some(h) =>
93
- let u = h.currentUrl()
94
- if (u.size > 0) {
95
- this.pageUrl = u
96
- }
97
- Hilog.info(1, "CjTauri", "aboutToAppear label=${this.windowLabel} pageUrl=${u}")
98
- case None => Hilog.info(1, "CjTauri", "aboutToAppear without host")
99
- }
100
- }
101
-
102
- private func onPageBegin(): Unit {
103
- Hilog.info(1, "CjTauri", "page begin label=${this.windowLabel}")
104
- match (ArkWebRuntime.hostFor(this.windowLabel)) {
105
- case Some(h) =>
106
- h.notePageBegin()
107
- let url = h.currentUrl()
108
- if (url.size > 0 && url != this.pageUrl) {
109
- this.pageUrl = url
110
- }
111
- // 桥脚本与插件 shim 都在 initScripts 里:ArkWeb 没有 document-start 注入,
112
- // `onPageBegin` 是能拿到的最早时机。
113
- for (js in h.initScriptsSnapshot()) {
114
- this.runScript(js)
115
- }
116
- this.runScript(arkWebLabelScript(this.windowLabel))
117
- case None => Hilog.info(1, "CjTauri", "page begin without host: label=${this.windowLabel}")
118
- }
119
- }
120
-
121
- private func onPageEnd(): Unit {
122
- Hilog.info(1, "CjTauri", "page end label=${this.windowLabel}")
123
- match (ArkWebRuntime.hostFor(this.windowLabel)) {
124
- case Some(h) =>
125
- h.notePageEnd()
126
- // 组件首次 build 时宿主可能还没登记(页面为空),这里再同步一次并触发刷新
127
- let url = h.currentUrl()
128
- if (url.size > 0 && url != this.pageUrl) {
129
- this.pageUrl = url
130
- }
131
- case None => ()
132
- }
133
- }
134
-
135
- /** 单条脚本执行(ArkWeb 的 runJavaScript 是异步的,回调里只做失败日志) */
136
- private func runScript(js: String): Unit {
137
- if (js.size == 0) {
138
- return
139
- }
140
- // runJavaScript(script, callback) 两个都是位置参数(命名参数前缀会被编译器拒)
141
- this.controller.runJavaScript(
142
- js,
143
- {
144
- err, result =>
145
- match (err) {
146
- case Some(e) => Hilog.info(1, "CjTauri", "runJavaScript failed: ${e.message}")
147
- case None => Hilog.info(1, "CjTauri", "runJavaScript ok")
148
- }
149
- let _ = result
150
- }
151
- )
152
- }
153
- }
154
-
155
- @Entry
156
- @Component
157
- class EntryView {
158
- func build() {
159
- Column {
160
- // 高度由**外层容器**给:自定义组件的调用点不能挂链式修饰(`.height(...)`),
161
- // 那样宏展开时补不上 `__gen_parent`,报的是「missing arguments for parameter list」。
162
- Column() {
163
- CjTauriWebView(windowLabel: DEFAULT_WINDOW_LABEL)
164
- }.width(100.percent).height(100.percent)
165
- }.width(100.percent).height(100.percent)
166
- }
167
- }
1
+ package ohos_app_cangjie_entry
2
+
3
+ import kit.ArkUI.*
4
+ import kit.ArkWeb.*
5
+ import kit.PerformanceAnalysisKit.Hilog
6
+ import ohos.arkui.component.web.Web
7
+ import ohos.arkui.state_macro_manage.*
8
+ import cjTauri.*
9
+
10
+ /*
11
+ * 应用侧的 ArkWeb 宿主组件
12
+ *
13
+ * 这个 `@Component` **必须住在应用模块里**,不能由框架包提供:框架包一旦 import
14
+ * `kit.ArkUI.*` / `kit.ArkWeb.*`,合并进应用动态库后包初始化链会跳到坏地址、启动即 SIGSEGV。
15
+ * 框架侧只留 `arkWebProxyPost` / `arkWebProxyPoll` 两个不碰 ArkUI 的桥接函数。
16
+ *
17
+ * 单窗口版:label 用框架默认的 DEFAULT_WINDOW_LABEL。要做多窗口就照着加一份、
18
+ * 换成别的 label(框架的宿主路由是按 label 查的,不会串台)。
19
+ */
20
+
21
+ /**
22
+ * proxy 的 `post` 入口:先交给框架处理报文,再把队列里的脚本推回页面
23
+ *
24
+ * 为什么「顺手推」放在这里:`javaScriptProxy` 的回调就在 UI 线程上,而
25
+ * `controller.runJavaScript` 也必须在 UI 线程调。页面 → 原生的**返回值通道不可用**
26
+ * (实测恒为 `null`),所以鸿蒙侧只能「页面发心跳 → 原生在该回调里把待执行脚本推出去」,
27
+ * 这是唯一的回投路径;框架会把它收到的 invoke 结果 / event 都排进这个队列。
28
+ */
29
+ func onProxyPost(label: String, raw: String, ctrl: WebviewController): String {
30
+ let ack = arkWebProxyPost(label, raw)
31
+ match (ArkWebRuntime.hostFor(label)) {
32
+ case Some(h) =>
33
+ let pending = h.takePendingScripts()
34
+ var i = 0
35
+ while (i < pending.size) {
36
+ ctrl.runJavaScript(pending[i], { _, _ => () })
37
+ i += 1
38
+ }
39
+ case None => ()
40
+ }
41
+ return ack
42
+ }
43
+
44
+ @Component
45
+ class CjTauriWebView {
46
+ /* label 决定:①页面里 __CJ_TAURI_LABEL__ ②报文里的 window 字段 ③宿主取哪份页面来源。
47
+ 必须是 `@Prop`:普通成员会被宏展开成 ObservedProperty 形参,外面传字符串进来会报
48
+ 「expected 4 arguments」。 */
49
+ @Prop
50
+ var windowLabel: String
51
+
52
+ /* 组件生命周期内必须保持同一个 controller 实例(ArkWeb 的硬约束) */
53
+ private let controller: WebviewController = WebviewController()
54
+
55
+ /* 页面地址由宿主给定;@State 让它参与刷新(宿主登记页面往往晚于组件首次 build)。
56
+ `@State` 不允许与 private/public/static 同用——宏会直接拒绝。 */
57
+ @State
58
+ var pageUrl: String = ""
59
+
60
+ public func build() {
61
+ Column {
62
+ if (this.pageUrl.size > 0) {
63
+ Web(src: this.pageUrl, controller: this.controller)
64
+ .width(100.percent)
65
+ .height(100.percent)
66
+ .fileAccess(true)
67
+ .javaScriptProxy(
68
+ funcList: [
69
+ { raw => onProxyPost(this.windowLabel, raw, this.controller) },
70
+ /* `poll` 只为兼容页面上可能残留的旧桥脚本:返回值传不回 JS,
71
+ 真正的脚本推送已在 `onProxyPost` 里完成。 */
72
+ { _ => "" }
73
+ ],
74
+ name: ARKWEB_PROXY_NAME, methodList: ["post", "poll"],
75
+ controller: this.controller
76
+ )
77
+ .onPageBegin({ _ => this.onPageBegin() })
78
+ .onPageEnd({ _ => this.onPageEnd() })
79
+ }
80
+ }.width(100.percent).height(100.percent)
81
+ }
82
+
83
+ /**
84
+ * 组件挂载钩子(首次 `build()` 之前调用一次)
85
+ *
86
+ * **必须在这里同步宿主地址**:宿主把页面落盘后才把 `file://` 地址放进自己的字段,
87
+ * 而 `build()` 是声明式的一次性求值——`pageUrl` 仍为空就永远不建 `Web`,
88
+ * `onPageBegin` 也就永远不会来,页面白屏。
89
+ */
90
+ protected override func aboutToAppear(): Unit {
91
+ match (ArkWebRuntime.hostFor(this.windowLabel)) {
92
+ case Some(h) =>
93
+ let u = h.currentUrl()
94
+ if (u.size > 0) {
95
+ this.pageUrl = u
96
+ }
97
+ Hilog.info(1, "CjTauri", "aboutToAppear label=${this.windowLabel} pageUrl=${u}")
98
+ case None => Hilog.info(1, "CjTauri", "aboutToAppear without host")
99
+ }
100
+ }
101
+
102
+ private func onPageBegin(): Unit {
103
+ Hilog.info(1, "CjTauri", "page begin label=${this.windowLabel}")
104
+ match (ArkWebRuntime.hostFor(this.windowLabel)) {
105
+ case Some(h) =>
106
+ h.notePageBegin()
107
+ let url = h.currentUrl()
108
+ if (url.size > 0 && url != this.pageUrl) {
109
+ this.pageUrl = url
110
+ }
111
+ // 桥脚本与插件 shim 都在 initScripts 里:ArkWeb 没有 document-start 注入,
112
+ // `onPageBegin` 是能拿到的最早时机。
113
+ for (js in h.initScriptsSnapshot()) {
114
+ this.runScript(js)
115
+ }
116
+ this.runScript(arkWebLabelScript(this.windowLabel))
117
+ case None => Hilog.info(1, "CjTauri", "page begin without host: label=${this.windowLabel}")
118
+ }
119
+ }
120
+
121
+ private func onPageEnd(): Unit {
122
+ Hilog.info(1, "CjTauri", "page end label=${this.windowLabel}")
123
+ match (ArkWebRuntime.hostFor(this.windowLabel)) {
124
+ case Some(h) =>
125
+ h.notePageEnd()
126
+ // 组件首次 build 时宿主可能还没登记(页面为空),这里再同步一次并触发刷新
127
+ let url = h.currentUrl()
128
+ if (url.size > 0 && url != this.pageUrl) {
129
+ this.pageUrl = url
130
+ }
131
+ case None => ()
132
+ }
133
+ }
134
+
135
+ /** 单条脚本执行(ArkWeb 的 runJavaScript 是异步的,回调里只做失败日志) */
136
+ private func runScript(js: String): Unit {
137
+ if (js.size == 0) {
138
+ return
139
+ }
140
+ // runJavaScript(script, callback) 两个都是位置参数(命名参数前缀会被编译器拒)
141
+ this.controller.runJavaScript(
142
+ js,
143
+ {
144
+ err, result =>
145
+ match (err) {
146
+ case Some(e) => Hilog.info(1, "CjTauri", "runJavaScript failed: ${e.message}")
147
+ case None => Hilog.info(1, "CjTauri", "runJavaScript ok")
148
+ }
149
+ let _ = result
150
+ }
151
+ )
152
+ }
153
+ }
154
+
155
+ @Entry
156
+ @Component
157
+ class EntryView {
158
+ func build() {
159
+ Column {
160
+ // 高度由**外层容器**给:自定义组件的调用点不能挂链式修饰(`.height(...)`),
161
+ // 那样宏展开时补不上 `__gen_parent`,报的是「missing arguments for parameter list」。
162
+ Column() {
163
+ CjTauriWebView(windowLabel: DEFAULT_WINDOW_LABEL)
164
+ }.width(100.percent).height(100.percent)
165
+ }.width(100.percent).height(100.percent)
166
+ }
167
+ }
@@ -1,139 +1,139 @@
1
- package ohos_app_cangjie_entry
2
-
3
- import kit.PerformanceAnalysisKit.Hilog
4
- import kit.AbilityKit.Want
5
- import kit.AbilityKit.UIAbility
6
- import kit.AbilityKit.LaunchParam
7
- import kit.AbilityKit.LaunchReason
8
- import kit.ArkUI.WindowStage
9
- import ohos.display.getDefaultDisplaySync
10
- import ohos.resource_manager.*
11
- import cjTauri.*
12
-
13
- /*
14
- * {{PROJECT_NAME}} 的 UIAbility
15
- *
16
- * 鸿蒙应用没有「自己起进程跑一个 main」这回事:窗口属于 ArkUI、页面由 ArkWeb 组件承载。
17
- * 所以桌面的 `main()` 在这里被拆成三段:
18
- * ① 把沙箱目录告诉框架(`run(html)` 要把页面落到沙箱再以 file:// 加载);
19
- * ② 在**非 UI 线程**上装配并跑框架应用(`run()` 阻塞到 quit(),UI 线程要留给 ArkUI 建树);
20
- * ③ 等框架把默认窗口的宿主登记好,再 `loadContent`。
21
- *
22
- * 页面与能力清单都是 hap 里的 `resources/rawfile/` 真文件,运行期读成字符串:
23
- * rawfile/index.html ← 就是桌面工程的 ui/index.html(改了重建即可)
24
- * rawfile/capabilities.json ← 就是桌面的 capabilities 目录下那份 default.json
25
- * 好处是 DevEco 里直接点 Build 也能编过(不需要先跑一次生成脚本)。
26
- */
27
- class MainAbility <: UIAbility {
28
- public init() {
29
- super()
30
- registerSelf()
31
- }
32
-
33
- public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
34
- Hilog.info(1, "CjTauri", "MainAbility OnCreated.${want.abilityName}")
35
-
36
- // 1) 沙箱目录要在 run() 之前给定:run(html) 把页面写成文件再以 file:// 加载
37
- // (ArkWeb 的仓颉绑定没有 loadData)。
38
- let filesDir = "${this.context.filesDir}"
39
- ArkWebRuntime.configure(filesDir)
40
-
41
- // 2) 页面与能力清单:从 hap 的 rawfile 读(读不到就是构建期没打进去,如实报一行)
42
- let pageHtml = this.readRawText("index.html")
43
- let capsJson = this.readRawText("capabilities.json")
44
- if (pageHtml.size == 0) {
45
- Hilog.info(1, "CjTauri", "rawfile/index.html 读不到,页面将只显示一句提示")
46
- }
47
-
48
- // 3) run() 会阻塞到 quit(),只能在非 UI 线程上跑——UI 线程要留给 ArkUI 建树。
49
- let _ = spawn { startTauriApp("{{APP_LABEL}}", pageHtml, capsJson) }
50
-
51
- match (launchParam.launchReason) {
52
- case LaunchReason.StartAbility => Hilog.info(1, "CjTauri", "START_ABILITY")
53
- case _ => ()
54
- }
55
- }
56
-
57
- public override func onWindowStageCreate(windowStage: WindowStage): Unit {
58
- Hilog.info(1, "CjTauri", "MainAbility onWindowStageCreate.")
59
- this.logDefaultWindowRect(windowStage)
60
- // 等框架把宿主登记上再建树:组件的 build() 只有在该 label 有宿主、页面地址非空时
61
- // 才渲染 `Web`。等不到也照常 loadContent——页面会自己报桥缺失,比静默白屏好排查。
62
- this.waitAndLogHost(DEFAULT_WINDOW_LABEL)
63
- windowStage.loadContent("EntryView")
64
- }
65
-
66
- /**
67
- * 读 hap 里的 rawfile(路径相对 `resources/rawfile/`)
68
- *
69
- * 读失败返回空串并把原因打进 hilog:调用方按「空 = 没有」降级,不让异常冒到生命周期里
70
- * (生命周期里抛异常,应用直接退,页面上看到的是「点了图标没反应」)。
71
- */
72
- private func readRawText(path: String): String {
73
- try {
74
- let bytes = this.context.resourceManager.getRawFileContent(path)
75
- Hilog.info(1, "CjTauri", "rawfile ${path} bytes=${bytes.size}")
76
- return String.fromUtf8(bytes)
77
- } catch (e: Exception) {
78
- Hilog.info(1, "CjTauri", "rawfile ${path} 读取失败: ${e}")
79
- return ""
80
- }
81
- }
82
-
83
- /**
84
- * 不干预窗口尺寸:**鸿蒙 PC 上系统给的默认窗口就是答案**
85
- *
86
- * 早先这里自己 `resize()` 定尺寸(先按屏幕比例 86%×84% = 2683×1747、后按逻辑 1000×700 vp),
87
- * 两版都是在跟平台的默认窗口抢方向盘:实测系统本来就按桌面形态给一扇约 2/3 屏宽的窗
88
- * (3120×2080 的屏上初始矩形 `[515 281 2090 1394]`,即 2090×1394 px ≈ 1100×734 vp),
89
- * 而紧接着的 resize 把它改成 1900×1330——屏上看到的是「先弹一个尺寸、随即变成另一个」,
90
- * 观感就是窗口莫名跳一下。所以模板**默认不碰窗口尺寸**,只读取矩形打一行日志作凭证;
91
- * 应用真需要固定尺寸时自己调 `windowStage.getMainWindow().resize()`。
92
- * 前提是 `entry/build-profile.json5` 的 `deviceTypes` 写成 `2in1` 且
93
- * `cangjieOptions.checkDeviceTypes=false` 放行——写成 phone/tablet 时系统按手机应用塞进
94
- * 竖屏兼容窗(约 600 宽),那种情况才需要自己定尺寸。
95
- */
96
- private func logDefaultWindowRect(windowStage: WindowStage): Unit {
97
- try {
98
- let screen = getDefaultDisplaySync()
99
- let screenW = Int64(screen.width)
100
- let screenH = Int64(screen.height)
101
- // `densityPixels` 是 **Float32**(.cj.d 里看着像 Float64),先化成整数百分比再参与运算。
102
- let densityRaw = screen.densityPixels
103
- let densityPct = Int64(densityRaw * 100.0)
104
- let mainWin = windowStage.getMainWindow()
105
- let r = mainWin.getWindowProperties().windowRect
106
- Hilog.info(1, "CjTauri", "main window keeps system default ${r.width}x${r.height} px at density ${densityPct}% on screen ${screenW}x${screenH}, rect=[${r.left} ${r.top} ${r.width} ${r.height}]")
107
- } catch (e: Exception) {
108
- Hilog.info(1, "CjTauri", "main window geometry read failed: ${e}")
109
- }
110
- }
111
-
112
- /**
113
- * 有界等待(3s)默认窗口的宿主登记完成,并把页面地址 / 预执行脚本条数打进 hilog
114
- *
115
- * 白屏定位用:宿主把页面落盘后的 file:// 地址必须非空,否则组件拿不到 src。
116
- */
117
- private func waitAndLogHost(label: String): Unit {
118
- var polls = 0
119
- while (polls < 300) {
120
- match (ArkWebRuntime.hostFor(label)) {
121
- case Some(_) => break
122
- case None => ()
123
- }
124
- sleep(Duration.millisecond * 10)
125
- polls += 1
126
- }
127
- if (polls >= 300) {
128
- Hilog.info(1, "CjTauri", "host not registered in 3s: label=${label}")
129
- return
130
- }
131
- Hilog.info(1, "CjTauri", "host registered after ${polls} polls label=${label}")
132
- match (ArkWebRuntime.hostFor(label)) {
133
- case Some(h) =>
134
- Hilog.info(1, "CjTauri", "host pageUrl label=${label} url=\"${h.currentUrl()}\"")
135
- Hilog.info(1, "CjTauri", "host initScripts label=${label} count=${h.initScriptsSnapshot().size}")
136
- case None => Hilog.info(1, "CjTauri", "host missing at loadContent label=${label}")
137
- }
138
- }
139
- }
1
+ package ohos_app_cangjie_entry
2
+
3
+ import kit.PerformanceAnalysisKit.Hilog
4
+ import kit.AbilityKit.Want
5
+ import kit.AbilityKit.UIAbility
6
+ import kit.AbilityKit.LaunchParam
7
+ import kit.AbilityKit.LaunchReason
8
+ import kit.ArkUI.WindowStage
9
+ import ohos.display.getDefaultDisplaySync
10
+ import ohos.resource_manager.*
11
+ import cjTauri.*
12
+
13
+ /*
14
+ * {{PROJECT_NAME}} 的 UIAbility
15
+ *
16
+ * 鸿蒙应用没有「自己起进程跑一个 main」这回事:窗口属于 ArkUI、页面由 ArkWeb 组件承载。
17
+ * 所以桌面的 `main()` 在这里被拆成三段:
18
+ * ① 把沙箱目录告诉框架(`run(html)` 要把页面落到沙箱再以 file:// 加载);
19
+ * ② 在**非 UI 线程**上装配并跑框架应用(`run()` 阻塞到 quit(),UI 线程要留给 ArkUI 建树);
20
+ * ③ 等框架把默认窗口的宿主登记好,再 `loadContent`。
21
+ *
22
+ * 页面与能力清单都是 hap 里的 `resources/rawfile/` 真文件,运行期读成字符串:
23
+ * rawfile/index.html ← 就是桌面工程的 ui/index.html(改了重建即可)
24
+ * rawfile/capabilities.json ← 就是桌面的 capabilities 目录下那份 default.json
25
+ * 好处是 DevEco 里直接点 Build 也能编过(不需要先跑一次生成脚本)。
26
+ */
27
+ class MainAbility <: UIAbility {
28
+ public init() {
29
+ super()
30
+ registerSelf()
31
+ }
32
+
33
+ public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
34
+ Hilog.info(1, "CjTauri", "MainAbility OnCreated.${want.abilityName}")
35
+
36
+ // 1) 沙箱目录要在 run() 之前给定:run(html) 把页面写成文件再以 file:// 加载
37
+ // (ArkWeb 的仓颉绑定没有 loadData)。
38
+ let filesDir = "${this.context.filesDir}"
39
+ ArkWebRuntime.configure(filesDir)
40
+
41
+ // 2) 页面与能力清单:从 hap 的 rawfile 读(读不到就是构建期没打进去,如实报一行)
42
+ let pageHtml = this.readRawText("index.html")
43
+ let capsJson = this.readRawText("capabilities.json")
44
+ if (pageHtml.size == 0) {
45
+ Hilog.info(1, "CjTauri", "rawfile/index.html 读不到,页面将只显示一句提示")
46
+ }
47
+
48
+ // 3) run() 会阻塞到 quit(),只能在非 UI 线程上跑——UI 线程要留给 ArkUI 建树。
49
+ let _ = spawn { startTauriApp("{{APP_LABEL}}", pageHtml, capsJson) }
50
+
51
+ match (launchParam.launchReason) {
52
+ case LaunchReason.StartAbility => Hilog.info(1, "CjTauri", "START_ABILITY")
53
+ case _ => ()
54
+ }
55
+ }
56
+
57
+ public override func onWindowStageCreate(windowStage: WindowStage): Unit {
58
+ Hilog.info(1, "CjTauri", "MainAbility onWindowStageCreate.")
59
+ this.logDefaultWindowRect(windowStage)
60
+ // 等框架把宿主登记上再建树:组件的 build() 只有在该 label 有宿主、页面地址非空时
61
+ // 才渲染 `Web`。等不到也照常 loadContent——页面会自己报桥缺失,比静默白屏好排查。
62
+ this.waitAndLogHost(DEFAULT_WINDOW_LABEL)
63
+ windowStage.loadContent("EntryView")
64
+ }
65
+
66
+ /**
67
+ * 读 hap 里的 rawfile(路径相对 `resources/rawfile/`)
68
+ *
69
+ * 读失败返回空串并把原因打进 hilog:调用方按「空 = 没有」降级,不让异常冒到生命周期里
70
+ * (生命周期里抛异常,应用直接退,页面上看到的是「点了图标没反应」)。
71
+ */
72
+ private func readRawText(path: String): String {
73
+ try {
74
+ let bytes = this.context.resourceManager.getRawFileContent(path)
75
+ Hilog.info(1, "CjTauri", "rawfile ${path} bytes=${bytes.size}")
76
+ return String.fromUtf8(bytes)
77
+ } catch (e: Exception) {
78
+ Hilog.info(1, "CjTauri", "rawfile ${path} 读取失败: ${e}")
79
+ return ""
80
+ }
81
+ }
82
+
83
+ /**
84
+ * 不干预窗口尺寸:**鸿蒙 PC 上系统给的默认窗口就是答案**
85
+ *
86
+ * 早先这里自己 `resize()` 定尺寸(先按屏幕比例 86%×84% = 2683×1747、后按逻辑 1000×700 vp),
87
+ * 两版都是在跟平台的默认窗口抢方向盘:实测系统本来就按桌面形态给一扇约 2/3 屏宽的窗
88
+ * (3120×2080 的屏上初始矩形 `[515 281 2090 1394]`,即 2090×1394 px ≈ 1100×734 vp),
89
+ * 而紧接着的 resize 把它改成 1900×1330——屏上看到的是「先弹一个尺寸、随即变成另一个」,
90
+ * 观感就是窗口莫名跳一下。所以模板**默认不碰窗口尺寸**,只读取矩形打一行日志作凭证;
91
+ * 应用真需要固定尺寸时自己调 `windowStage.getMainWindow().resize()`。
92
+ * 前提是 `entry/build-profile.json5` 的 `deviceTypes` 写成 `2in1` 且
93
+ * `cangjieOptions.checkDeviceTypes=false` 放行——写成 phone/tablet 时系统按手机应用塞进
94
+ * 竖屏兼容窗(约 600 宽),那种情况才需要自己定尺寸。
95
+ */
96
+ private func logDefaultWindowRect(windowStage: WindowStage): Unit {
97
+ try {
98
+ let screen = getDefaultDisplaySync()
99
+ let screenW = Int64(screen.width)
100
+ let screenH = Int64(screen.height)
101
+ // `densityPixels` 是 **Float32**(.cj.d 里看着像 Float64),先化成整数百分比再参与运算。
102
+ let densityRaw = screen.densityPixels
103
+ let densityPct = Int64(densityRaw * 100.0)
104
+ let mainWin = windowStage.getMainWindow()
105
+ let r = mainWin.getWindowProperties().windowRect
106
+ Hilog.info(1, "CjTauri", "main window keeps system default ${r.width}x${r.height} px at density ${densityPct}% on screen ${screenW}x${screenH}, rect=[${r.left} ${r.top} ${r.width} ${r.height}]")
107
+ } catch (e: Exception) {
108
+ Hilog.info(1, "CjTauri", "main window geometry read failed: ${e}")
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 有界等待(3s)默认窗口的宿主登记完成,并把页面地址 / 预执行脚本条数打进 hilog
114
+ *
115
+ * 白屏定位用:宿主把页面落盘后的 file:// 地址必须非空,否则组件拿不到 src。
116
+ */
117
+ private func waitAndLogHost(label: String): Unit {
118
+ var polls = 0
119
+ while (polls < 300) {
120
+ match (ArkWebRuntime.hostFor(label)) {
121
+ case Some(_) => break
122
+ case None => ()
123
+ }
124
+ sleep(Duration.millisecond * 10)
125
+ polls += 1
126
+ }
127
+ if (polls >= 300) {
128
+ Hilog.info(1, "CjTauri", "host not registered in 3s: label=${label}")
129
+ return
130
+ }
131
+ Hilog.info(1, "CjTauri", "host registered after ${polls} polls label=${label}")
132
+ match (ArkWebRuntime.hostFor(label)) {
133
+ case Some(h) =>
134
+ Hilog.info(1, "CjTauri", "host pageUrl label=${label} url=\"${h.currentUrl()}\"")
135
+ Hilog.info(1, "CjTauri", "host initScripts label=${label} count=${h.initScriptsSnapshot().size}")
136
+ case None => Hilog.info(1, "CjTauri", "host missing at loadContent label=${label}")
137
+ }
138
+ }
139
+ }