cj-tauri 0.8.0 → 1.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.
- package/README.md +3 -3
- package/cjpm.toml +52 -25
- package/cli/cjpm.toml +1 -1
- package/cli/src/doctor.cj +304 -0
- package/cli/src/main.cj +33 -7
- package/cli/src/project.cj +59 -1
- package/cli/src/resolve.cj +348 -304
- package/cli/src/scaffold.cj +139 -22
- package/cli/templates/ohos-pc/AppScope/app.json5 +13 -0
- package/cli/templates/ohos-pc/AppScope/resources/base/element/string.json +8 -0
- package/cli/templates/ohos-pc/AppScope/resources/base/media/background.png +0 -0
- package/cli/templates/ohos-pc/AppScope/resources/base/media/foreground.png +0 -0
- package/cli/templates/ohos-pc/AppScope/resources/base/media/layered_image.json +6 -0
- package/cli/templates/ohos-pc/README.md +79 -0
- package/cli/templates/ohos-pc/build-hap.sh +190 -0
- package/cli/templates/ohos-pc/build-profile.json5 +46 -0
- package/cli/templates/ohos-pc/entry/build-profile.json5 +26 -0
- package/cli/templates/ohos-pc/entry/cjpm.toml +43 -0
- package/cli/templates/ohos-pc/entry/hvigorfile.ts +6 -0
- package/cli/templates/ohos-pc/entry/oh-package.json5 +9 -0
- package/cli/templates/ohos-pc/entry/src/main/cangjie/ability_stage.cj +17 -0
- package/cli/templates/ohos-pc/entry/src/main/cangjie/app_host.cj +94 -0
- package/cli/templates/ohos-pc/entry/src/main/cangjie/index.cj +167 -0
- package/cli/templates/ohos-pc/entry/src/main/cangjie/main_ability.cj +139 -0
- package/cli/templates/ohos-pc/entry/src/main/module.json5 +49 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/element/color.json +8 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/element/string.json +16 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/media/background.png +0 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/media/foreground.png +0 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/media/layered_image.json +6 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/base/profile/main_pages.json +2 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/rawfile/capabilities.json +6 -0
- package/cli/templates/ohos-pc/entry/src/main/resources/rawfile/index.html +92 -0
- package/cli/templates/ohos-pc/hvigor/hvigor-config.json5 +5 -0
- package/cli/templates/ohos-pc/hvigorfile.ts +6 -0
- package/cli/templates/ohos-pc/oh-package.json5 +10 -0
- package/native/build_linux.sh +30 -2
- package/native/build_win.bat +62 -61
- package/native/core/bridge_core.c +1626 -0
- package/native/{bridge_core.h → core/bridge_core.h} +246 -6
- package/native/platforms/harmonyos/README.md +106 -0
- package/native/platforms/linux/bridge_linux.c +2485 -0
- package/native/platforms/windows/bridge_win.c +3248 -0
- package/native/tests/test_bridge_core.c +528 -1
- package/package.json +12 -3
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/api_system.cj +10 -2
- package/src/app.cj +248 -85
- package/src/arkweb_proxy.cj +56 -0
- package/src/arkweb_script.cj +154 -0
- package/src/capability.cj +140 -0
- package/src/clipboard_bridge.cj +55 -0
- package/src/dragdrop.cj +26 -0
- package/src/embedded_assets.cj +98 -0
- package/src/hexutil.cj +53 -0
- package/src/host.cj +197 -26
- package/src/host_arkweb.cj +740 -0
- package/src/host_factory.cj +48 -0
- package/src/host_webkit.cj +202 -26
- package/src/host_webview2.cj +176 -3
- package/src/hotkey.cj +66 -0
- package/src/ipc_hub.cj +49 -0
- package/src/ipc_message.cj +2 -1
- package/src/jsutil.cj +33 -0
- package/src/log_bridge.cj +107 -0
- package/src/logging.cj +127 -0
- package/src/menu.cj +197 -196
- package/src/plugin.cj +40 -0
- package/src/plugins/clipboard/plugin_clipboard.cj +144 -0
- package/src/{plugin_dialog.cj → plugins/dialog/plugin_dialog.cj} +74 -17
- package/src/plugins/fs/plugin_fs.cj +194 -0
- package/src/plugins/http/plugin_http.cj +459 -0
- package/src/plugins/log/plugin_log.cj +105 -0
- package/src/{plugin_menu.cj → plugins/menu/plugin_menu.cj} +4 -2
- package/src/plugins/mqtt/mqtt_codec.cj +661 -0
- package/src/plugins/mqtt/plugin_mqtt.cj +1446 -0
- package/src/plugins/notification/plugin_notification.cj +146 -0
- package/src/plugins/plugins.cj +5 -0
- package/src/plugins/redis/plugin_redis.cj +2409 -0
- package/src/plugins/redis/redis_codec.cj +556 -0
- package/src/{plugin_serial.cj → plugins/serial/plugin_serial.cj} +442 -489
- package/src/{serial_bridge.cj → plugins/serial/serial_bridge.cj} +185 -184
- package/src/{plugin_shell.cj → plugins/shell/plugin_shell.cj} +230 -228
- package/src/plugins/sqlite/plugin_sqlite.cj +697 -0
- package/src/plugins/sqlite/sqlite_ffi.cj +206 -0
- package/src/plugins/store/plugin_store.cj +287 -0
- package/src/plugins/ws/plugin_ws.cj +470 -0
- package/src/process_hidden.cj +174 -0
- package/src/scope.cj +391 -0
- package/src/shell_event.cj +69 -68
- package/src/tests/capability_test.cj +53 -0
- package/src/tests/fake_host.cj +85 -20
- package/src/tests/ipc_hub_test.cj +13 -0
- package/src/tests/jsutil_test.cj +62 -0
- package/src/tests/logging_test.cj +62 -0
- package/src/tests/mqtt_codec_test.cj +405 -0
- package/src/tests/plugin_clipboard_test.cj +222 -0
- package/src/tests/plugin_dialog_test.cj +98 -4
- package/src/tests/plugin_http_test.cj +124 -0
- package/src/tests/plugin_menu_test.cj +1 -0
- package/src/tests/plugin_notification_test.cj +205 -0
- package/src/tests/plugin_serial_test.cj +269 -267
- package/src/tests/plugin_shell_test.cj +189 -188
- package/src/tests/plugin_sqlite_test.cj +230 -0
- package/src/tests/plugin_store_test.cj +260 -0
- package/src/tests/plugin_test.cj +542 -524
- package/src/tests/plugin_ws_test.cj +40 -0
- package/src/tests/process_hidden_test.cj +129 -0
- package/src/tests/redis_codec_test.cj +396 -0
- package/src/tests/scope_test.cj +350 -0
- package/src/tests/shell_event_test.cj +0 -1
- package/src/tests/system_host_test.cj +81 -79
- package/src/tests/urlutil_test.cj +53 -0
- package/src/tests/version_test.cj +75 -65
- package/src/tray.cj +90 -0
- package/src/urlutil.cj +159 -0
- package/src/version.cj +23 -2
- package/src/window.cj +51 -42
- package/native/bridge_core.c +0 -797
- package/native/bridge_linux.c +0 -1268
- package/native/bridge_win.c +0 -1633
- package/src/plugin_fs.cj +0 -118
- /package/native/{bridge_js.h → core/bridge_js.h} +0 -0
|
@@ -1,65 +1,75 @@
|
|
|
1
|
-
package cjTauri.tests
|
|
2
|
-
|
|
3
|
-
import cjTauri.*
|
|
4
|
-
import stdx.encoding.json.*
|
|
5
|
-
import std.unittest.*
|
|
6
|
-
import std.unittest.testmacro.*
|
|
7
|
-
|
|
8
|
-
/*
|
|
9
|
-
* 版本与内置命令的一致性测试。
|
|
10
|
-
* 版本号的权威目标是 CHANGELOG.md,四处常量由 scripts/check-version.sh 巡检;
|
|
11
|
-
* 这里只锁「代码里的单一来源不漂移」。
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
@Test
|
|
15
|
-
func testVersionConstantNotEmpty(): Unit {
|
|
16
|
-
@Expect(CJ_TAURI_VERSION.size > 0, true)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
@Test
|
|
20
|
-
func testSystemVersionReportsConstant(): Unit {
|
|
21
|
-
let commands = SystemCommands()
|
|
22
|
-
let v = commands.handle("system:version", JsonObject(), IpcContext("main", IpcHub()))
|
|
23
|
-
let o = v.asObject()
|
|
24
|
-
// 命令报告的版本必须就是常量本身(防止有人改了其中一处)
|
|
25
|
-
@Expect(o.get("version").getOrThrow().asString().getValue(), CJ_TAURI_VERSION)
|
|
26
|
-
@Expect(o.get("name").getOrThrow().asString().getValue(), "cj-tauri")
|
|
27
|
-
@Expect(o.get("
|
|
28
|
-
@Expect(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@Expect(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@Expect(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
package cjTauri.tests
|
|
2
|
+
|
|
3
|
+
import cjTauri.*
|
|
4
|
+
import stdx.encoding.json.*
|
|
5
|
+
import std.unittest.*
|
|
6
|
+
import std.unittest.testmacro.*
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* 版本与内置命令的一致性测试。
|
|
10
|
+
* 版本号的权威目标是 CHANGELOG.md,四处常量由 scripts/check-version.sh 巡检;
|
|
11
|
+
* 这里只锁「代码里的单一来源不漂移」。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
@Test
|
|
15
|
+
func testVersionConstantNotEmpty(): Unit {
|
|
16
|
+
@Expect(CJ_TAURI_VERSION.size > 0, true)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Test
|
|
20
|
+
func testSystemVersionReportsConstant(): Unit {
|
|
21
|
+
let commands = SystemCommands()
|
|
22
|
+
let v = commands.handle("system:version", JsonObject(), IpcContext("main", IpcHub()))
|
|
23
|
+
let o = v.asObject()
|
|
24
|
+
// 命令报告的版本必须就是常量本身(防止有人改了其中一处)
|
|
25
|
+
@Expect(o.get("version").getOrThrow().asString().getValue(), CJ_TAURI_VERSION)
|
|
26
|
+
@Expect(o.get("name").getOrThrow().asString().getValue(), "cj-tauri")
|
|
27
|
+
@Expect(o.get("protocol").getOrThrow().asString().getValue(), CJ_IPC_PROTOCOL)
|
|
28
|
+
@Expect(o.get("platform").getOrThrow().asString().getValue(), currentPlatform())
|
|
29
|
+
@Expect(currentPlatform().size > 0, true)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Test
|
|
33
|
+
func testIpcProtocolConstantPresent(): Unit {
|
|
34
|
+
// 协议版本常量:非空、纯数字(抬版本时按「删字段/改语义必须 +1」的纪律走)
|
|
35
|
+
@Expect(CJ_IPC_PROTOCOL.size > 0, true)
|
|
36
|
+
for (b in CJ_IPC_PROTOCOL.toArray()) {
|
|
37
|
+
@Expect(b >= 48u8 && b <= 57u8, true)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
func testSystemPingEchoAndUnknown(): Unit {
|
|
43
|
+
let commands = SystemCommands()
|
|
44
|
+
let ctx = IpcContext("main", IpcHub())
|
|
45
|
+
@Expect(commands.handle("system:ping", JsonObject(), ctx).asString().getValue(), "pong")
|
|
46
|
+
|
|
47
|
+
var args = JsonObject()
|
|
48
|
+
args.put("who", JsonString("cj"))
|
|
49
|
+
let echoed = commands.handle("system:echo", args, ctx).asObject()
|
|
50
|
+
@Expect(echoed.get("who").getOrThrow().asString().getValue(), "cj")
|
|
51
|
+
|
|
52
|
+
// 未定义的 system:* 走 CommandException,前端拿到的是这句原文
|
|
53
|
+
var thrown = ""
|
|
54
|
+
try {
|
|
55
|
+
commands.handle("system:nope", JsonObject(), ctx)
|
|
56
|
+
} catch (e: CommandException) {
|
|
57
|
+
thrown = e.message
|
|
58
|
+
}
|
|
59
|
+
@Expect(thrown, "unknown system command: system:nope")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
func testWindowConfigDefaultsMatchBridgeHardcoding(): Unit {
|
|
64
|
+
// 默认值必须与桥里此前的硬编码一致(标题 cj-tauri / 900x640),否则不调 window() 的应用会变样
|
|
65
|
+
let def = WindowConfig()
|
|
66
|
+
@Expect(def.title, "cj-tauri")
|
|
67
|
+
@Expect(def.width, 900)
|
|
68
|
+
@Expect(def.height, 640)
|
|
69
|
+
@Expect(def.devTools, true)
|
|
70
|
+
|
|
71
|
+
let custom = WindowConfig("我的应用", 1200, 800)
|
|
72
|
+
@Expect(custom.title, "我的应用")
|
|
73
|
+
@Expect(custom.width, 1200)
|
|
74
|
+
@Expect(custom.height, 800)
|
|
75
|
+
}
|
package/src/tray.cj
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
import std.collection.*
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 托盘的**纯仓颉模型**(RFC-002 §5.1 / §7.C):只描述意图,不含任何平台概念。
|
|
7
|
+
*
|
|
8
|
+
* 分层与菜单同构:模型(本文件)→ `WebViewHost.setTray()` → C 桥 `cj_bridge_set_tray` →
|
|
9
|
+
* 平台翻译层(Linux = libayatana-appindicator3,可选编译;Windows 桩如实报未实现)。
|
|
10
|
+
*
|
|
11
|
+
* 托盘是**应用级**资源(一个进程一个图标):重复 setTray = 整体重建(图标 / 提示 / 菜单一起换);
|
|
12
|
+
* 空线路 = 移除托盘。菜单项复用 `MenuItem`(v1 只收 Normal / Checkbox / Separator,
|
|
13
|
+
* Submenu 整条跳过并打 stderr——托盘菜单平铺,别给子菜单)。
|
|
14
|
+
*
|
|
15
|
+
* 线路格式(与 `native/core/bridge_core.h` 的 `pending_tray` 注释逐字对应):
|
|
16
|
+
*
|
|
17
|
+
* icon\t<路径>\n (空路径 = 平台用主题默认图标兜底)
|
|
18
|
+
* tooltip\t<文本>\n
|
|
19
|
+
* <kind>\t<id>\t<label>\t<flags>\t<accel>\n (与菜单线路同格式,平铺一层)
|
|
20
|
+
*
|
|
21
|
+
* 菜单项被点以 `{"kind":"tray","id":"…"}` 走 shell 通道回投(前端事件 `tray:click`,
|
|
22
|
+
* 需在能力清单 `events` 声明)。图标 / 提示文本来自应用作者,不做转义;
|
|
23
|
+
* 字段含制表符 / 换行的头部值整体丢弃为空(wire 的结构字符,与菜单同一铁律)。
|
|
24
|
+
*/
|
|
25
|
+
public class TrayModel {
|
|
26
|
+
/** 图标路径(png/ico 绝对路径);空串 = 平台用主题默认图标兜底 */
|
|
27
|
+
public let icon: String
|
|
28
|
+
|
|
29
|
+
/** 悬停提示文本;空串 = 不设 */
|
|
30
|
+
public let tooltip: String
|
|
31
|
+
|
|
32
|
+
/** 托盘右键菜单(平铺一层;Submenu 项会被 toWire 丢弃并打 stderr) */
|
|
33
|
+
public let items: ArrayList<MenuItem>
|
|
34
|
+
|
|
35
|
+
public init(icon: String, tooltip: String, items: ArrayList<MenuItem>) {
|
|
36
|
+
this.icon = icon
|
|
37
|
+
this.tooltip = tooltip
|
|
38
|
+
this.items = items
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 无菜单托盘(纯图标 + 提示) */
|
|
42
|
+
public init(icon: String, tooltip: String) {
|
|
43
|
+
this.icon = icon
|
|
44
|
+
this.tooltip = tooltip
|
|
45
|
+
this.items = ArrayList<MenuItem>()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 线路文本(三段:icon 行 / tooltip 行 / 菜单行走 MenuModel.emit 同一遍发射器) */
|
|
49
|
+
public func toWire(): String {
|
|
50
|
+
// 托盘菜单平铺:Submenu 整条出局(枚举不能写 ==,AGENTS §4,走 match)
|
|
51
|
+
let flat = ArrayList<MenuItem>()
|
|
52
|
+
for (it in this.items) {
|
|
53
|
+
if (TrayModel.isSubmenu(it.kind)) {
|
|
54
|
+
eprintln("[cj-tauri] tray: submenu item dropped (v1 tray menu is flat): ${it.label}")
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
flat.add(it)
|
|
58
|
+
}
|
|
59
|
+
// 头部两行的结构字符会让整份线路解析错位:宁可丢提示也不装错
|
|
60
|
+
var icon = this.icon
|
|
61
|
+
var tooltip = this.tooltip
|
|
62
|
+
if (TrayModel.hasStructChar(icon) || TrayModel.hasStructChar(tooltip)) {
|
|
63
|
+
eprintln("[cj-tauri] tray: icon/tooltip contains tab or newline - dropped to empty")
|
|
64
|
+
icon = ""
|
|
65
|
+
tooltip = ""
|
|
66
|
+
}
|
|
67
|
+
var out = "icon\t" + icon + "\n"
|
|
68
|
+
out += "tooltip\t" + tooltip + "\n"
|
|
69
|
+
out += MenuModel.emit(flat, 0)
|
|
70
|
+
return out
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 枚举判别只能走 match(`==` 编译不过,见 MenuModel.isSubmenu 的实测注释) */
|
|
74
|
+
private static func isSubmenu(kind: MenuItemKind): Bool {
|
|
75
|
+
match (kind) {
|
|
76
|
+
case MenuItemKind.Submenu => true
|
|
77
|
+
case _ => false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 结构字符(tab/LF/CR)检查——wire 的分隔符就是它们 */
|
|
82
|
+
private static func hasStructChar(s: String): Bool {
|
|
83
|
+
for (b in s) {
|
|
84
|
+
if (b == 9u8 || b == 10u8 || b == 13u8) {
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/urlutil.cj
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* URL → host 提取(核心共享层):http 与 ws 两个插件共用同一套解析与大小写口径。
|
|
5
|
+
*
|
|
6
|
+
* 为什么放核心不放插件:主机白名单的「匹配目标」只要有第二份实现,就一定会有一份
|
|
7
|
+
* 是错的——与 scope 判定同一条铁律(AGENTS §2:安全校验不吃「各插件各写一套」)。
|
|
8
|
+
* 插件只管调,不自己扫字节。
|
|
9
|
+
*
|
|
10
|
+
* 语义:从 `scheme://[user@]host[:port][/path]` 提取 host(小写返回)。
|
|
11
|
+
* 协议只认 http / https / ws / wss;解析不出 host 返回 None。按字节扫(URL 结构
|
|
12
|
+
* 是 ASCII),不用 String 切片撞 UTF-8 边界。public 是为了单测可直接构造(跨包须 public)。
|
|
13
|
+
*/
|
|
14
|
+
public func urlHostOf(rawUrl: String): Option<String> {
|
|
15
|
+
let b = rawUrl.toArray()
|
|
16
|
+
// 找 "://"(58 47 47)
|
|
17
|
+
var schemeEnd: Int64 = -1
|
|
18
|
+
var i: Int64 = 0
|
|
19
|
+
while (i + 2 < b.size) {
|
|
20
|
+
if (b[i] == 58u8 && b[i + 1] == 47u8 && b[i + 2] == 47u8) {
|
|
21
|
+
schemeEnd = i
|
|
22
|
+
break
|
|
23
|
+
}
|
|
24
|
+
i++
|
|
25
|
+
}
|
|
26
|
+
if (schemeEnd <= 0) {
|
|
27
|
+
return None
|
|
28
|
+
}
|
|
29
|
+
let scheme = asciiLowerOf(b[0..schemeEnd])
|
|
30
|
+
if (scheme != "http" && scheme != "https" && scheme != "ws" && scheme != "wss") {
|
|
31
|
+
return None
|
|
32
|
+
}
|
|
33
|
+
// host 段:到 '/' '?' '#' 为止
|
|
34
|
+
var start = schemeEnd + 3
|
|
35
|
+
var end = start
|
|
36
|
+
while (end < b.size) {
|
|
37
|
+
let c = b[end]
|
|
38
|
+
if (c == 47u8 || c == 63u8 || c == 35u8) {
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
end++
|
|
42
|
+
}
|
|
43
|
+
if (end == start) {
|
|
44
|
+
return None
|
|
45
|
+
}
|
|
46
|
+
// 去 userinfo(最后一个 '@' 之前的部分)
|
|
47
|
+
var at: Int64 = -1
|
|
48
|
+
var k = end - 1
|
|
49
|
+
while (k >= start) {
|
|
50
|
+
if (b[k] == 64u8) {
|
|
51
|
+
at = k
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
k--
|
|
55
|
+
}
|
|
56
|
+
if (at >= 0) {
|
|
57
|
+
start = at + 1
|
|
58
|
+
}
|
|
59
|
+
// 去端口(第一个 ':' 之后)——IPv6 字面量 [..] 截到 ']' 为止,方括号内不拆
|
|
60
|
+
if (b[start] == 91u8) {
|
|
61
|
+
var close: Int64 = -1
|
|
62
|
+
var m = start + 1
|
|
63
|
+
while (m < end) {
|
|
64
|
+
if (b[m] == 93u8) {
|
|
65
|
+
close = m
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
m++
|
|
69
|
+
}
|
|
70
|
+
if (close >= 0) {
|
|
71
|
+
end = close + 1
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
var colon: Int64 = -1
|
|
75
|
+
var m = start
|
|
76
|
+
while (m < end) {
|
|
77
|
+
if (b[m] == 58u8) {
|
|
78
|
+
colon = m
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
m++
|
|
82
|
+
}
|
|
83
|
+
if (colon >= 0) {
|
|
84
|
+
end = colon
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (end <= start) {
|
|
88
|
+
return None
|
|
89
|
+
}
|
|
90
|
+
let host = asciiLowerOf(b[start..end])
|
|
91
|
+
if (host.size == 0) {
|
|
92
|
+
return None
|
|
93
|
+
}
|
|
94
|
+
return Some(host)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* host 是不是 IP 字面量(IPv4 / IPv6,可带方括号)。
|
|
99
|
+
*
|
|
100
|
+
* 为什么需要它:建 TLS 客户端时要用 host 当 **SNI**(`serverName`),而 RFC 6066 明确不许 SNI 里放
|
|
101
|
+
* IP 字面量——stdx 默认不发 SNI,本仓给「域名」补上 SNI 的那一轮必须把 IP 排除在外,否则
|
|
102
|
+
* 「https 直连 IP」这类原本可用的端点会被对端当场拒掉(修一个坑别踩出另一个)。
|
|
103
|
+
* 判定只认严格形态:四段十进制且每段 <= 255 的 IPv4,或含冒号 / 带方括号的 IPv6——
|
|
104
|
+
* 纯十六进制字母的域名(如 `dead.beef`)是域名,不是 IP。
|
|
105
|
+
*/
|
|
106
|
+
public func isIpLiteralHost(host: String): Bool {
|
|
107
|
+
if (host.size == 0) {
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
let b = host.toArray()
|
|
111
|
+
// 带方括号 = IPv6 字面量(urlHostOf 会把方括号一起返回)
|
|
112
|
+
if (b[0] == 91u8 && b[b.size - 1] == 93u8) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
for (i in 0..b.size) {
|
|
116
|
+
if (b[i] == 58u8) {
|
|
117
|
+
return true // 含冒号即 IPv6(含 ::1 这类简写)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
var segs = 0
|
|
121
|
+
var digits = 0
|
|
122
|
+
var value: Int64 = 0
|
|
123
|
+
for (i in 0..b.size) {
|
|
124
|
+
let c = b[i]
|
|
125
|
+
if (c == 46u8) {
|
|
126
|
+
if (digits == 0 || value > 255) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
segs = segs + 1
|
|
130
|
+
digits = 0
|
|
131
|
+
value = 0
|
|
132
|
+
} else if (c >= 48u8 && c <= 57u8) {
|
|
133
|
+
digits = digits + 1
|
|
134
|
+
if (digits > 3) {
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
value = value * 10 + Int64(c - 48u8)
|
|
138
|
+
} else {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (digits == 0 || value > 255) {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
return segs == 3
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** ASCII 字节 → 小写字符串(非 ASCII 原样保留);host 匹配统一走这里 */
|
|
149
|
+
private func asciiLowerOf(bytes: Array<Byte>): String {
|
|
150
|
+
let out = Array<Byte>(bytes.size, repeat: 0)
|
|
151
|
+
for (i in 0..bytes.size) {
|
|
152
|
+
var c = bytes[i]
|
|
153
|
+
if (c >= 65u8 && c <= 90u8) {
|
|
154
|
+
c = c + 32u8
|
|
155
|
+
}
|
|
156
|
+
out[i] = c
|
|
157
|
+
}
|
|
158
|
+
return String.fromUtf8(out)
|
|
159
|
+
}
|
package/src/version.cj
CHANGED
|
@@ -9,14 +9,35 @@ package cjTauri
|
|
|
9
9
|
* - cli/src/project.cj 的 cliVersion()
|
|
10
10
|
* - CHANGELOG.md 顶部第一个已发版段
|
|
11
11
|
*/
|
|
12
|
-
public let CJ_TAURI_VERSION = "
|
|
12
|
+
public let CJ_TAURI_VERSION = "1.1.0"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* IPC 报文协议版本(报文形状是跨层契约,加版本字段是协议治理惯例):事件 / 回执信封的 `protocol` 字段
|
|
16
|
+
* 与 `system:version` 返回的 `protocol` 字段都来自这里——单一代码来源,改协议时同步抬版本。
|
|
17
|
+
*
|
|
18
|
+
* 为什么同进程也要有:报文形状现在是跨层契约(C 桥 bridge_js.h 的页面侧解析、CLI 的
|
|
19
|
+
* manifest 消费、将来 HarmonyOS/独立进程形态的跨进程边界),有版本字段才能在「旧页面
|
|
20
|
+
* 配新框架」这类错配时给出可诊断的信号,而不是行为悄悄漂移。
|
|
21
|
+
* v1:`type` + `event`/`id` + `payload` (+ 可选 `coalesce`/`window`);本字段加入记 v1,
|
|
22
|
+
* 此后任何「删字段 / 改语义」必须抬到 v2。
|
|
23
|
+
*/
|
|
24
|
+
public let CJ_IPC_PROTOCOL = "1"
|
|
13
25
|
|
|
14
26
|
/** 当前宿主平台标识(`system:version` 的 platform 字段) */
|
|
15
|
-
|
|
27
|
+
// 鸿蒙目标的 `os` 仍报 Linux(实测),所以 `os` 维度分不开「GTK 宿主」与「ArkWeb 宿主」。
|
|
28
|
+
// 判据用 `env`:鸿蒙目标(`aarch64-linux-ohos`)是 `ohos`,桌面 Linux 是 `gnu`——两者互斥;
|
|
29
|
+
// 而 `arch` 不行(桌面 aarch64 Linux 同样是 `aarch64`,会被误报成鸿蒙)。
|
|
30
|
+
// 平台标识必须是真实的那一个:`linux` / `ohos` 混用会让前端与证据链分不清跑在哪个宿主上。
|
|
31
|
+
@When[os == "Linux" && env != "ohos"]
|
|
16
32
|
public func currentPlatform(): String {
|
|
17
33
|
return "linux"
|
|
18
34
|
}
|
|
19
35
|
|
|
36
|
+
@When[env == "ohos"]
|
|
37
|
+
public func currentPlatform(): String {
|
|
38
|
+
return "ohos"
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
@When[os == "Windows"]
|
|
21
42
|
public func currentPlatform(): String {
|
|
22
43
|
return "windows"
|
package/src/window.cj
CHANGED
|
@@ -1,42 +1,51 @@
|
|
|
1
|
-
package cjTauri
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 窗口配置(对标 tauri.conf.json 的 app.windows[] 项)
|
|
5
|
-
*
|
|
6
|
-
* 用法:
|
|
7
|
-
* let app = TauriApp().window(WindowConfig("我的应用", 1200, 800))
|
|
8
|
-
*
|
|
9
|
-
* // 要自定义窗口图标(可选):
|
|
10
|
-
* var cfg = WindowConfig("我的应用", 1200, 800)
|
|
11
|
-
* cfg.iconPath = "assets/app.ico"
|
|
12
|
-
* let app = TauriApp().window(cfg)
|
|
13
|
-
*
|
|
14
|
-
* 默认值与此前桥内硬编码的值一致(标题 cj-tauri / 900x640),
|
|
15
|
-
* 因此不调用 window() 的应用行为不变。
|
|
16
|
-
*/
|
|
17
|
-
public class WindowConfig {
|
|
18
|
-
public var title: String
|
|
19
|
-
public var width: Int64
|
|
20
|
-
public var height: Int64
|
|
21
|
-
// 是否允许打开开发者工具(WebView2 的 AreDevToolsEnabled / WebKit 的 developer extras)
|
|
22
|
-
public var devTools: Bool
|
|
23
|
-
// 窗口图标路径:Windows 用 .ico(LoadImageW 只认 .ico);Linux 走 GTK,png/ico 都行。
|
|
24
|
-
// 空字符串 = 用系统默认图标(默认行为,不影响老应用)。
|
|
25
|
-
public var iconPath: String
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
}
|
|
42
|
-
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 窗口配置(对标 tauri.conf.json 的 app.windows[] 项)
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* let app = TauriApp().window(WindowConfig("我的应用", 1200, 800))
|
|
8
|
+
*
|
|
9
|
+
* // 要自定义窗口图标(可选):
|
|
10
|
+
* var cfg = WindowConfig("我的应用", 1200, 800)
|
|
11
|
+
* cfg.iconPath = "assets/app.ico"
|
|
12
|
+
* let app = TauriApp().window(cfg)
|
|
13
|
+
*
|
|
14
|
+
* 默认值与此前桥内硬编码的值一致(标题 cj-tauri / 900x640),
|
|
15
|
+
* 因此不调用 window() 的应用行为不变。
|
|
16
|
+
*/
|
|
17
|
+
public class WindowConfig {
|
|
18
|
+
public var title: String
|
|
19
|
+
public var width: Int64
|
|
20
|
+
public var height: Int64
|
|
21
|
+
// 是否允许打开开发者工具(WebView2 的 AreDevToolsEnabled / WebKit 的 developer extras)
|
|
22
|
+
public var devTools: Bool
|
|
23
|
+
// 窗口图标路径:Windows 用 .ico(LoadImageW 只认 .ico);Linux 走 GTK,png/ico 都行。
|
|
24
|
+
// 空字符串 = 用系统默认图标(默认行为,不影响老应用)。
|
|
25
|
+
public var iconPath: String
|
|
26
|
+
|
|
27
|
+
// 最小化到托盘(B 项):用户在标题栏点「最小化」时窗口**隐藏**(从任务栏消失、只留托盘图标),
|
|
28
|
+
// 而不是缩成任务栏按钮。默认 false = 老行为不变。
|
|
29
|
+
// 必须与服务端配套:应用得先 setTray(...),否则隐藏之后界面上没有任何入口——
|
|
30
|
+
// 框架不替应用拦这个组合(它不知道应用的托盘菜单长什么样),但真到最小化那一刻平台会
|
|
31
|
+
// 复核托盘是否**真的**装上了:没装就降级为普通最小化并打一行 stderr,避免「窗口没了也点不回来」。
|
|
32
|
+
public var minimizeToTray: Bool
|
|
33
|
+
|
|
34
|
+
public init() {
|
|
35
|
+
this.title = "cj-tauri"
|
|
36
|
+
this.width = 900
|
|
37
|
+
this.height = 640
|
|
38
|
+
this.devTools = true
|
|
39
|
+
this.iconPath = ""
|
|
40
|
+
this.minimizeToTray = false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public init(title: String, width: Int64, height: Int64) {
|
|
44
|
+
this.title = title
|
|
45
|
+
this.width = width
|
|
46
|
+
this.height = height
|
|
47
|
+
this.devTools = true
|
|
48
|
+
this.iconPath = ""
|
|
49
|
+
this.minimizeToTray = false
|
|
50
|
+
}
|
|
51
|
+
}
|