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,194 +1,194 @@
1
- package cjTauri.plugins.serial
2
-
3
- // 桌面两端包装函数用(ohos 桩在 serial_ohos_stub.cj,RFC §4.6.4 P-1);
4
- // foreign 声明不带门禁:ohos 侧无引用即不要求符号,不产生链接错误
5
- @When[env != "ohos"]
6
- import std.collection.*
7
-
8
- /**
9
- * 串口桥(plugin_serial 的 FFI 面):C 桥 `cj_bridge_serial_*` 的声明与薄包装。
10
- *
11
- * 为什么这里**不带 @When**:两平台的导出同名同签名(AGENTS §2 的硬约束),平台差异全在
12
- * `native/bridge_*.c` 里——Linux 是 termios,Windows 侧接口已留、实现待补(返回可读的
13
- * 「暂不支持」,不是静默失败)。所以仓颉侧一份声明就能同时编两个平台,上层不需要平台分支。
14
- *
15
- * 参数约定(与 native/bridge_core.h 逐字一致):parity 0=无 1=奇 2=偶,stopBits 1|2,
16
- * dataBits 5..8。**路径白名单与参数校验都在 C 侧 core**——页面可控的字符串绝不能只在仓颉侧挡。
17
- *
18
- * 这组导出**不收宿主句柄**:串口不是宿主能力,不必先建窗口才能用(无头进程里照样能开)。
19
- */
20
- @C
21
- foreign func cj_bridge_serial_open(path: CString, baud: Int32, dataBits: Int32, parity: Int32,
22
- stopBits: Int32): Int64
23
-
24
- /**
25
- * 读:最多 len 字节、最多等 timeoutMs 毫秒(0 = 立即返回)。返回实际字节数(0 = 超时无数据)
26
- */
27
- @C
28
- foreign func cj_bridge_serial_read(h: Int64, buf: CPointer<UInt8>, len: Int32, timeoutMs: Int32): Int32
29
-
30
- /**
31
- * 写:在 timeoutMs 预算内尽量写完;返回实际写入字节数(可能小于 len)
32
- */
33
- @C
34
- foreign func cj_bridge_serial_write(h: Int64, buf: CPointer<UInt8>, len: Int32,
35
- timeoutMs: Int32): Int32
36
-
37
- /**
38
- * 关闭并释放句柄(幂等:重复关同一句柄返回 -4)
39
- */
40
- @C
41
- foreign func cj_bridge_serial_close(h: Int64): Int32
42
-
43
- /**
44
- * 最近一次失败的明细(UTF-8,C 侧**线程局部**:命令各自跑在 worker 线程上,
45
- * 静态单槽会被并发调用互相覆盖)
46
- */
47
- @C
48
- foreign func cj_bridge_serial_last_error(): CString
49
-
50
- /**
51
- * 枚举本机串口(serial:list 的平台层):一行一个名字写进 buf('\n' 分隔),
52
- * 返回条数(0 = 没有串口,不是错误);-1 参数非法,-4 本平台未实现。
53
- * 两平台导出同名同签名:Windows 走 QueryDosDeviceW,Linux 返回 -4(list 在 Linux 走 /dev 扫描)。
54
- */
55
- @C
56
- foreign func cj_bridge_serial_list(buf: CPointer<UInt8>, bufLen: Int32): Int32
57
-
58
- /**
59
- * 打开串口:成功返回 >0 句柄,失败返回负码(-1 参数/白名单、-2 句柄表满、-3 平台打不开)。
60
- * 失败明细用 serialLastError() 取(C 侧线程局部,同一线程内取才有意义)。
61
- */
62
- @When[env != "ohos"]
63
- public func serialOpen(path: String, baud: Int32, dataBits: Int32, parity: Int32,
64
- stopBits: Int32): Int64 {
65
- var h: Int64 = -1
66
- unsafe {
67
- let cpath = LibC.mallocCString(path)
68
- h = cj_bridge_serial_open(cpath, baud, dataBits, parity, stopBits)
69
- // C 侧(core)当场把路径拷进自己的句柄槽,不持有我们的指针——这里立刻归还,
70
- // 免得每次打开漏一小块(跨 FFI 的内存一律「谁分配谁释放」)
71
- LibC.free(cpath)
72
- }
73
- return h
74
- }
75
-
76
- /**
77
- * C 侧最近一次失败的明细;取不到时返回空串(调用方自行降级成数字码)
78
- */
79
- @When[env != "ohos"]
80
- public func serialLastError(): String {
81
- unsafe {
82
- return cj_bridge_serial_last_error().toString()
83
- }
84
- }
85
-
86
- /**
87
- * C 桥枚举本机串口:Windows 下走 QueryDosDeviceW(返回 COM 名字列表);
88
- * Linux 返回 None(-4 未实现,serial:list 在 Linux 走 /dev 扫描)。C 侧异常时也回 None。
89
- */
90
- @When[env != "ohos"]
91
- public func serialListC(): Option<ArrayList<String>> {
92
- let cap: Int32 = 8192
93
- let buf = LibC.malloc<UInt8>(count: Int64(cap))
94
- var n: Int32 = 0
95
- unsafe {
96
- n = cj_bridge_serial_list(buf, cap)
97
- }
98
- if (n < 0) {
99
- unsafe {
100
- LibC.free(buf)
101
- }
102
- return None
103
- }
104
- // 返回值是「条数」不是字节数;真正的文本以 '\0' 结尾,按终止符取整段再按 '\n' 切开
105
- var len: Int64 = 0
106
- unsafe {
107
- while (len < Int64(cap) && buf.read(len) != 0) {
108
- len++
109
- }
110
- }
111
- let bytes = Array<UInt8>(len, repeat: 0)
112
- unsafe {
113
- for (i in 0..len) {
114
- bytes[i] = buf.read(i)
115
- }
116
- }
117
- unsafe {
118
- LibC.free(buf)
119
- }
120
- let out = ArrayList<String>()
121
- let text = String.fromUtf8(bytes)
122
- if (text != "") {
123
- for (line in text.split('\n')) {
124
- if (line != "") {
125
- out.add(line)
126
- }
127
- }
128
- }
129
- return Some(out)
130
- }
131
-
132
- /**
133
- * 读:最多 maxBytes 字节。返回实际读到的字节(空数组 = 超时无数据)。
134
- * 缓冲在这里现分配现释放——每次读的用量都很小,不值得为它引一层对象池。
135
- */
136
- @When[env != "ohos"]
137
- public func serialRead(h: Int64, maxBytes: Int32, timeoutMs: Int32): Array<UInt8> {
138
- let cap = Int64(maxBytes)
139
- let buf = LibC.malloc<UInt8>(count: cap)
140
- var n: Int32 = 0
141
- unsafe {
142
- n = cj_bridge_serial_read(h, buf, maxBytes, timeoutMs)
143
- }
144
- var out = Array<UInt8>(0, repeat: 0)
145
- if (n > 0) {
146
- out = Array<UInt8>(Int64(n), repeat: 0)
147
- unsafe {
148
- for (i in 0..Int64(n)) {
149
- out[i] = buf.read(i)
150
- }
151
- }
152
- }
153
- unsafe {
154
- LibC.free(buf)
155
- }
156
- return out
157
- }
158
-
159
- /**
160
- * 写:在 timeoutMs 预算内尽量写完,返回实际写入字节数(可能 < data.size;0 = 一个字节都没出去)
161
- */
162
- @When[env != "ohos"]
163
- public func serialWrite(h: Int64, data: Array<UInt8>, timeoutMs: Int32): Int32 {
164
- if (data.size == 0) {
165
- return 0
166
- }
167
- let n = data.size
168
- let buf = LibC.malloc<UInt8>(count: n)
169
- unsafe {
170
- for (i in 0..n) {
171
- buf.write(i, data[i])
172
- }
173
- }
174
- var w: Int32 = 0
175
- unsafe {
176
- w = cj_bridge_serial_write(h, buf, Int32(n), timeoutMs)
177
- }
178
- unsafe {
179
- LibC.free(buf)
180
- }
181
- return w
182
- }
183
-
184
- /**
185
- * 关句柄:0 = 成功,负码 = 失败(重复关返回 -4)
186
- */
187
- @When[env != "ohos"]
188
- public func serialClose(h: Int64): Int32 {
189
- var rc: Int32 = 0
190
- unsafe {
191
- rc = cj_bridge_serial_close(h)
192
- }
193
- return rc
194
- }
1
+ package cjTauri.plugins.serial
2
+
3
+ // 桌面两端包装函数用(ohos 走 NDK FFI 直连,实现在 serial_ohos.cj,批次 4);
4
+ // foreign 声明不带门禁:ohos 侧无引用即不要求符号,不产生链接错误
5
+ @When[env != "ohos"]
6
+ import std.collection.*
7
+
8
+ /**
9
+ * 串口桥(plugin_serial 的 FFI 面):C 桥 `cj_bridge_serial_*` 的声明与薄包装。
10
+ *
11
+ * 为什么这里**不带 @When**:两平台的导出同名同签名(AGENTS §2 的硬约束),平台差异全在
12
+ * `native/bridge_*.c` 里——Linux 是 termios,Windows 侧接口已留、实现待补(返回可读的
13
+ * 「暂不支持」,不是静默失败)。所以仓颉侧一份声明就能同时编两个平台,上层不需要平台分支。
14
+ *
15
+ * 参数约定(与 native/bridge_core.h 逐字一致):parity 0=无 1=奇 2=偶,stopBits 1|2,
16
+ * dataBits 5..8。**路径白名单与参数校验都在 C 侧 core**——页面可控的字符串绝不能只在仓颉侧挡。
17
+ *
18
+ * 这组导出**不收宿主句柄**:串口不是宿主能力,不必先建窗口才能用(无头进程里照样能开)。
19
+ */
20
+ @C
21
+ foreign func cj_bridge_serial_open(path: CString, baud: Int32, dataBits: Int32, parity: Int32,
22
+ stopBits: Int32): Int64
23
+
24
+ /**
25
+ * 读:最多 len 字节、最多等 timeoutMs 毫秒(0 = 立即返回)。返回实际字节数(0 = 超时无数据)
26
+ */
27
+ @C
28
+ foreign func cj_bridge_serial_read(h: Int64, buf: CPointer<UInt8>, len: Int32, timeoutMs: Int32): Int32
29
+
30
+ /**
31
+ * 写:在 timeoutMs 预算内尽量写完;返回实际写入字节数(可能小于 len)
32
+ */
33
+ @C
34
+ foreign func cj_bridge_serial_write(h: Int64, buf: CPointer<UInt8>, len: Int32,
35
+ timeoutMs: Int32): Int32
36
+
37
+ /**
38
+ * 关闭并释放句柄(幂等:重复关同一句柄返回 -4)
39
+ */
40
+ @C
41
+ foreign func cj_bridge_serial_close(h: Int64): Int32
42
+
43
+ /**
44
+ * 最近一次失败的明细(UTF-8,C 侧**线程局部**:命令各自跑在 worker 线程上,
45
+ * 静态单槽会被并发调用互相覆盖)
46
+ */
47
+ @C
48
+ foreign func cj_bridge_serial_last_error(): CString
49
+
50
+ /**
51
+ * 枚举本机串口(serial:list 的平台层):一行一个名字写进 buf('\n' 分隔),
52
+ * 返回条数(0 = 没有串口,不是错误);-1 参数非法,-4 本平台未实现。
53
+ * 两平台导出同名同签名:Windows 走 QueryDosDeviceW,Linux 返回 -4(list 在 Linux 走 /dev 扫描)。
54
+ */
55
+ @C
56
+ foreign func cj_bridge_serial_list(buf: CPointer<UInt8>, bufLen: Int32): Int32
57
+
58
+ /**
59
+ * 打开串口:成功返回 >0 句柄,失败返回负码(-1 参数/白名单、-2 句柄表满、-3 平台打不开)。
60
+ * 失败明细用 serialLastError() 取(C 侧线程局部,同一线程内取才有意义)。
61
+ */
62
+ @When[env != "ohos"]
63
+ public func serialOpen(path: String, baud: Int32, dataBits: Int32, parity: Int32,
64
+ stopBits: Int32): Int64 {
65
+ var h: Int64 = -1
66
+ unsafe {
67
+ let cpath = LibC.mallocCString(path)
68
+ h = cj_bridge_serial_open(cpath, baud, dataBits, parity, stopBits)
69
+ // C 侧(core)当场把路径拷进自己的句柄槽,不持有我们的指针——这里立刻归还,
70
+ // 免得每次打开漏一小块(跨 FFI 的内存一律「谁分配谁释放」)
71
+ LibC.free(cpath)
72
+ }
73
+ return h
74
+ }
75
+
76
+ /**
77
+ * C 侧最近一次失败的明细;取不到时返回空串(调用方自行降级成数字码)
78
+ */
79
+ @When[env != "ohos"]
80
+ public func serialLastError(): String {
81
+ unsafe {
82
+ return cj_bridge_serial_last_error().toString()
83
+ }
84
+ }
85
+
86
+ /**
87
+ * C 桥枚举本机串口:Windows 下走 QueryDosDeviceW(返回 COM 名字列表);
88
+ * Linux 返回 None(-4 未实现,serial:list 在 Linux 走 /dev 扫描)。C 侧异常时也回 None。
89
+ */
90
+ @When[env != "ohos"]
91
+ public func serialListC(): Option<ArrayList<String>> {
92
+ let cap: Int32 = 8192
93
+ let buf = LibC.malloc<UInt8>(count: Int64(cap))
94
+ var n: Int32 = 0
95
+ unsafe {
96
+ n = cj_bridge_serial_list(buf, cap)
97
+ }
98
+ if (n < 0) {
99
+ unsafe {
100
+ LibC.free(buf)
101
+ }
102
+ return None
103
+ }
104
+ // 返回值是「条数」不是字节数;真正的文本以 '\0' 结尾,按终止符取整段再按 '\n' 切开
105
+ var len: Int64 = 0
106
+ unsafe {
107
+ while (len < Int64(cap) && buf.read(len) != 0) {
108
+ len++
109
+ }
110
+ }
111
+ let bytes = Array<UInt8>(len, repeat: 0)
112
+ unsafe {
113
+ for (i in 0..len) {
114
+ bytes[i] = buf.read(i)
115
+ }
116
+ }
117
+ unsafe {
118
+ LibC.free(buf)
119
+ }
120
+ let out = ArrayList<String>()
121
+ let text = String.fromUtf8(bytes)
122
+ if (text != "") {
123
+ for (line in text.split('\n')) {
124
+ if (line != "") {
125
+ out.add(line)
126
+ }
127
+ }
128
+ }
129
+ return Some(out)
130
+ }
131
+
132
+ /**
133
+ * 读:最多 maxBytes 字节。返回实际读到的字节(空数组 = 超时无数据)。
134
+ * 缓冲在这里现分配现释放——每次读的用量都很小,不值得为它引一层对象池。
135
+ */
136
+ @When[env != "ohos"]
137
+ public func serialRead(h: Int64, maxBytes: Int32, timeoutMs: Int32): Array<UInt8> {
138
+ let cap = Int64(maxBytes)
139
+ let buf = LibC.malloc<UInt8>(count: cap)
140
+ var n: Int32 = 0
141
+ unsafe {
142
+ n = cj_bridge_serial_read(h, buf, maxBytes, timeoutMs)
143
+ }
144
+ var out = Array<UInt8>(0, repeat: 0)
145
+ if (n > 0) {
146
+ out = Array<UInt8>(Int64(n), repeat: 0)
147
+ unsafe {
148
+ for (i in 0..Int64(n)) {
149
+ out[i] = buf.read(i)
150
+ }
151
+ }
152
+ }
153
+ unsafe {
154
+ LibC.free(buf)
155
+ }
156
+ return out
157
+ }
158
+
159
+ /**
160
+ * 写:在 timeoutMs 预算内尽量写完,返回实际写入字节数(可能 < data.size;0 = 一个字节都没出去)
161
+ */
162
+ @When[env != "ohos"]
163
+ public func serialWrite(h: Int64, data: Array<UInt8>, timeoutMs: Int32): Int32 {
164
+ if (data.size == 0) {
165
+ return 0
166
+ }
167
+ let n = data.size
168
+ let buf = LibC.malloc<UInt8>(count: n)
169
+ unsafe {
170
+ for (i in 0..n) {
171
+ buf.write(i, data[i])
172
+ }
173
+ }
174
+ var w: Int32 = 0
175
+ unsafe {
176
+ w = cj_bridge_serial_write(h, buf, Int32(n), timeoutMs)
177
+ }
178
+ unsafe {
179
+ LibC.free(buf)
180
+ }
181
+ return w
182
+ }
183
+
184
+ /**
185
+ * 关句柄:0 = 成功,负码 = 失败(重复关返回 -4)
186
+ */
187
+ @When[env != "ohos"]
188
+ public func serialClose(h: Int64): Int32 {
189
+ var rc: Int32 = 0
190
+ unsafe {
191
+ rc = cj_bridge_serial_close(h)
192
+ }
193
+ return rc
194
+ }
@@ -0,0 +1,92 @@
1
+ package cjTauri.plugins.serial
2
+
3
+ // 桌面侧看不到这个文件里的 `@When[env == "ohos"]` 函数体,`ArrayList` 就成「未使用」——
4
+ // 按 AGENTS §4「跨平台文件别按单平台警告去清 import」,给 import 加同款门禁,而不是删掉它。
5
+ @When[env == "ohos"]
6
+ import std.collection.*
7
+
8
+ /**
9
+ * 鸿蒙侧 USB 串口后端:**如实降级,不做互操作**
10
+ *
11
+ * ## 为什么这里是空的(2026-10-24 真机 A/B 实测,别再徒手重试)
12
+ *
13
+ * 原本的落法是「仓颉侧经 `ohos.ark_interop` 调 `@ohos.usbManager.serial`」,三组对照把它否掉了:
14
+ *
15
+ * | 产物 | 内容 | 真机结果 |
16
+ * | --- | --- | --- |
17
+ * | 有互操作、真去用 | `spawn` 出 owner 线程建 `JSRuntime()` | 启动即 `signal:6`(SIGABRT),日志停在 `startApp begin`,`initScripts` 都没打印 |
18
+ * | 只有互操作、从不执行 | 同名符号全留,owner 线程刻意不起 | **应用正常启动**,页面跑完自检(`pidof` 有、`page end label=main`) |
19
+ * | 互操作整段摘掉 | 本文件上一版的空桩 | 同样正常启动 |
20
+ *
21
+ * 第三组证明「链接 `libohos.ark_interop.so` 本身无害」(`NEEDED` 里两个库、4 个 JSRuntime 符号都在,
22
+ * 应用照跑);**真正 abort 的是执行 `JSRuntime()`**。再补一个探针把创建点挪到 UI 线程
23
+ * (`onWindowStageCreate`,与 ArkTS 同一条线程)——只打出 `runtime probe begin [ui-thread]`,
24
+ * 结论行永远没出现、进程当场消失。所以不是「Cangjie 轻量线程没有 VM」,
25
+ * 而是**纯仓颉进程里根本没有 ArkTS VM**;线程换哪儿都一样。
26
+ *
27
+ * 结论:`serialManager` 只能由**真正的 ArkTS 侧**调用(混合工程,或应用自己宿主 ArkTS 模块),
28
+ * 不能关在框架插件的叶子内。ADR 见 `docs/设计决策记录.md` D-022 的 2026-10-24 结论段。
29
+ *
30
+ * ## 所以这里的选择
31
+ *
32
+ * 保持插件导出面(`serialOpen` 等已按句柄空间分流到这里),但一律**如实报不可用**——
33
+ * 既不假装成功,也不让「没后台」变成「应用起不来」。POSIX 的 `/dev/ttyS*` 通路不受影响
34
+ * (`serialOpen` 先试 POSIX,失败才落到这儿)。
35
+ */
36
+
37
+ /** 降级原因(命令层原样回给前端,便于页面说清楚为什么不能开 USB 串口) */
38
+ @When[env == "ohos"]
39
+ const OHOS_USB_UNAVAILABLE: String =
40
+ "USB 串口在鸿蒙上需要 ArkTS 侧调用 serialManager:纯仓颉进程里没有 ArkTS VM," +
41
+ "建 JSRuntime 会让进程 SIGABRT(真机 A/B 已证);请改用 /dev/ttyS* 或走 ArkTS 宿主"
42
+
43
+ /** 句柄是否属于 USB 串口空间——降级后永远 false,`serialRead/Write/Close` 全部留在 POSIX 侧 */
44
+ @When[env == "ohos"]
45
+ public func usbSerialIsHandle(h: Int64): Bool {
46
+ let _ = h
47
+ return false
48
+ }
49
+
50
+ /** 枚举 USB 串口:降级后恒空(「没有 USB 串口」与「后端不可用」在命令层同形,原因进错误槽) */
51
+ @When[env == "ohos"]
52
+ public func usbSerialListNames(): ArrayList<String> {
53
+ return ArrayList<String>()
54
+ }
55
+
56
+ /** 打开 USB 串口:恒回 -3(打不开/未授权)并写明原因 */
57
+ @When[env == "ohos"]
58
+ public func usbSerialOpenByPath(path: String, baud: Int32, dataBits: Int32, parity: Int32,
59
+ stopBits: Int32): Int64 {
60
+ let _ = path
61
+ let _ = baud
62
+ let _ = dataBits
63
+ let _ = parity
64
+ let _ = stopBits
65
+ serialOhosSetError(OHOS_USB_UNAVAILABLE)
66
+ return -3
67
+ }
68
+
69
+ /** 读:不会有句柄走到这里(`usbSerialIsHandle` 恒 false),保留签名为了上层不分叉 */
70
+ @When[env == "ohos"]
71
+ public func usbSerialReadHandle(h: Int64, maxBytes: Int32, timeoutMs: Int32): Array<UInt8> {
72
+ let _ = h
73
+ let _ = maxBytes
74
+ let _ = timeoutMs
75
+ return Array<UInt8>(0, repeat: 0)
76
+ }
77
+
78
+ /** 写:同上,恒 0 字节 */
79
+ @When[env == "ohos"]
80
+ public func usbSerialWriteHandle(h: Int64, data: Array<UInt8>, timeoutMs: Int32): Int32 {
81
+ let _ = h
82
+ let _ = data
83
+ let _ = timeoutMs
84
+ return 0
85
+ }
86
+
87
+ /** 关句柄:同上,恒 -4 */
88
+ @When[env == "ohos"]
89
+ public func usbSerialCloseHandle(h: Int64): Int32 {
90
+ let _ = h
91
+ return -4
92
+ }