cj-tauri 1.1.2 → 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.
- package/README.md +137 -77
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +13 -2
- package/cli/cjpm.toml +22 -13
- package/cli/src/doctor.cj +308 -304
- package/cli/src/main.cj +129 -121
- package/cli/src/manifest.cj +632 -0
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -754
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +46 -38
- package/cli/src/sync.cj +526 -0
- package/cli/src/util.cj +9 -0
- package/cli/templates/app/README.md +30 -59
- package/cli/templates/app/{ui → assets/ui}/index.html +2 -0
- package/cli/templates/app/cj-tauri.toml +32 -0
- package/cli/templates/app/cjpm.toml +21 -4
- package/cli/templates/app/src/main.cj +29 -16
- package/cli/templates/app-react/README.md +34 -45
- package/cli/templates/app-react/{ui → assets/ui}/index.html +25 -25
- package/cli/templates/app-react/{ui → assets/ui}/src/App.css +67 -67
- package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +124 -124
- package/cli/templates/app-react/cj-tauri.toml +33 -0
- package/cli/templates/app-react/cjpm.toml +21 -4
- package/cli/templates/app-react/src/main.cj +18 -29
- package/cli/templates/app-vue/README.md +34 -38
- package/cli/templates/app-vue/{ui → assets/ui}/index.html +25 -25
- package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +176 -176
- package/cli/templates/app-vue/cj-tauri.toml +33 -0
- package/cli/templates/app-vue/cjpm.toml +21 -4
- package/cli/templates/app-vue/src/main.cj +18 -29
- package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +5 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +15 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +8 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -0
- package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +8 -0
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3740 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -51
- package/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -940
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -0
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +0 -12
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -604
- package/src/host_arkweb.cj +201 -17
- package/src/host_webkit.cj +663 -548
- package/src/host_webview2.cj +663 -548
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +337 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -0
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -0
- package/src/platform/platform_assets.cj +33 -0
- package/src/platform/platform_discover.cj +176 -0
- package/src/platform/platform_env.cj +29 -0
- package/src/platform/platform_feature.cj +31 -0
- package/src/platform/platform_page_source.cj +87 -0
- package/src/platform/platform_target_info.cj +32 -0
- package/src/platform_host_provider.cj +87 -0
- package/src/plugin.cj +333 -299
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -144
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +8 -0
- package/src/plugins/notification/plugin_notification.cj +154 -146
- package/src/plugins/serial/plugin_serial.cj +17 -1
- package/src/plugins/serial/serial_bridge.cj +9 -1
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +29 -1
- package/src/plugins/sqlite/plugin_sqlite.cj +8 -0
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +126 -0
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -186
- package/src/tests/ipc_hub_test.cj +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -0
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +66 -3
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/cli/templates/ohos-pc/entry/src/main/cangjie/index.cj +0 -167
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/host_factory.cj +0 -48
- /package/cli/templates/app/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/package.json +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +0 -0
- /package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +0 -0
- /package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/package.json +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +0 -0
- /package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +0 -0
- /package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +0 -0
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import './App.css'; // 与 app-vue 模板等价的样式(React 没有 SFC,样式走独立的 css)
|
|
3
|
-
|
|
4
|
-
// 桥(window.__CJ_TAURI__)由宿主注入,注入时机随平台/页面来源而异:Linux 宿主注入在文档开始,
|
|
5
|
-
// 但发行态单文件里的内联 <script type="module"> 解析完就执行,谁先谁后与加载方式有关。
|
|
6
|
-
// 所以统一「等桥出现再初始化」,不要在模块作用域直接读。
|
|
7
|
-
let bridge = null;
|
|
8
|
-
|
|
9
|
-
function waitForBridge(timeoutMs = 3000) {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
const t0 = Date.now();
|
|
12
|
-
const tick = () => {
|
|
13
|
-
if (window.__CJ_TAURI__) return resolve(window.__CJ_TAURI__);
|
|
14
|
-
if (Date.now() - t0 >= timeoutMs) return resolve(null);
|
|
15
|
-
setTimeout(tick, 20);
|
|
16
|
-
};
|
|
17
|
-
tick();
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default function App() {
|
|
22
|
-
const [name, setName] = useState('world');
|
|
23
|
-
const [result, setResult] = useState('等待 invoke…');
|
|
24
|
-
const [tick, setTick] = useState('');
|
|
25
|
-
const [menuMsg, setMenuMsg] = useState('');
|
|
26
|
-
const [sidebar, setSidebar] = useState(true);
|
|
27
|
-
const [href, setHref] = useState('');
|
|
28
|
-
const [bridgeOk, setBridgeOk] = useState(false);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
let alive = true; // 等桥是异步的:组件卸载后不要再回写状态
|
|
32
|
-
(async () => {
|
|
33
|
-
setHref(location.href);
|
|
34
|
-
const t = await waitForBridge();
|
|
35
|
-
if (!alive) return;
|
|
36
|
-
bridge = t;
|
|
37
|
-
if (!t) {
|
|
38
|
-
setResult('未找到 window.__CJ_TAURI__');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
setBridgeOk(true);
|
|
42
|
-
t.listen('tick', (payload) => {
|
|
43
|
-
setTick('事件 tick #' + payload.n + ' 来自仓颉后端');
|
|
44
|
-
});
|
|
45
|
-
// 原生菜单 → 页面(capabilities 里要声明 menu:click)
|
|
46
|
-
t.listen('menu:click', (p) => {
|
|
47
|
-
setMenuMsg('菜单被点: ' + p.id + '(enabled=' + p.enabled + ', checked=' + p.checked + ')');
|
|
48
|
-
});
|
|
49
|
-
})();
|
|
50
|
-
return () => {
|
|
51
|
-
alive = false;
|
|
52
|
-
};
|
|
53
|
-
}, []);
|
|
54
|
-
|
|
55
|
-
async function greet() {
|
|
56
|
-
if (!bridge) {
|
|
57
|
-
setResult('桥未就绪');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
const data = await bridge.invoke('greet', { name });
|
|
62
|
-
setResult('invoke OK: ' + data);
|
|
63
|
-
} catch (err) {
|
|
64
|
-
setResult('invoke FAIL: ' + err.message);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function startTimer() {
|
|
69
|
-
if (bridge) await bridge.invoke('timer');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function reload() {
|
|
73
|
-
if (bridge) bridge.reload();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 页面 → 原生菜单:menu 插件的前端 shim(window.__CJ_TAURI__.menu,注入在 document start)
|
|
77
|
-
async function toggleSidebar(e) {
|
|
78
|
-
const checked = e.target.checked;
|
|
79
|
-
if (!bridge || !bridge.menu) {
|
|
80
|
-
setResult('未注入 menu 插件 shim(页面来自 runUrl 时插件 JS 不在本进程)');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
await bridge.menu.setChecked({ id: 'view.sidebar', checked });
|
|
85
|
-
setResult('原生菜单「侧边栏」已同步为 ' + checked);
|
|
86
|
-
} catch (err) {
|
|
87
|
-
setResult('menu.setChecked FAIL: ' + err.message);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<>
|
|
93
|
-
<h1>cj-tauri · React + Vite</h1>
|
|
94
|
-
<p className="hint">
|
|
95
|
-
页面来源:<code>{href}</code>
|
|
96
|
-
<br />
|
|
97
|
-
桥状态:{bridgeOk ? '已注入 __CJ_TAURI__' : '未注入'}
|
|
98
|
-
</p>
|
|
99
|
-
|
|
100
|
-
<div className="card">
|
|
101
|
-
<div className="row">
|
|
102
|
-
<input
|
|
103
|
-
value={name}
|
|
104
|
-
placeholder="输入名字"
|
|
105
|
-
onChange={(e) => setName(e.target.value)}
|
|
106
|
-
/>
|
|
107
|
-
<button onClick={greet}>greet</button>
|
|
108
|
-
<button className="ghost" onClick={startTimer}>timer</button>
|
|
109
|
-
<button className="ghost" onClick={reload}>reload</button>
|
|
110
|
-
</div>
|
|
111
|
-
<div className="out">{result}</div>
|
|
112
|
-
<div className="tick">{tick}</div>
|
|
113
|
-
<div className="tick">{menuMsg}</div>
|
|
114
|
-
<div className="chk">
|
|
115
|
-
<input type="checkbox" checked={sidebar} onChange={toggleSidebar} />
|
|
116
|
-
<span>侧边栏(对应原生菜单「视图 → 侧边栏」)</span>
|
|
117
|
-
</div>
|
|
118
|
-
<p className="tip">
|
|
119
|
-
改这个文件(<code>ui/src/App.jsx</code>)保存,页面会在不重启应用的情况下更新。
|
|
120
|
-
</p>
|
|
121
|
-
</div>
|
|
122
|
-
</>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import './App.css'; // 与 app-vue 模板等价的样式(React 没有 SFC,样式走独立的 css)
|
|
3
|
+
|
|
4
|
+
// 桥(window.__CJ_TAURI__)由宿主注入,注入时机随平台/页面来源而异:Linux 宿主注入在文档开始,
|
|
5
|
+
// 但发行态单文件里的内联 <script type="module"> 解析完就执行,谁先谁后与加载方式有关。
|
|
6
|
+
// 所以统一「等桥出现再初始化」,不要在模块作用域直接读。
|
|
7
|
+
let bridge = null;
|
|
8
|
+
|
|
9
|
+
function waitForBridge(timeoutMs = 3000) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const t0 = Date.now();
|
|
12
|
+
const tick = () => {
|
|
13
|
+
if (window.__CJ_TAURI__) return resolve(window.__CJ_TAURI__);
|
|
14
|
+
if (Date.now() - t0 >= timeoutMs) return resolve(null);
|
|
15
|
+
setTimeout(tick, 20);
|
|
16
|
+
};
|
|
17
|
+
tick();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function App() {
|
|
22
|
+
const [name, setName] = useState('world');
|
|
23
|
+
const [result, setResult] = useState('等待 invoke…');
|
|
24
|
+
const [tick, setTick] = useState('');
|
|
25
|
+
const [menuMsg, setMenuMsg] = useState('');
|
|
26
|
+
const [sidebar, setSidebar] = useState(true);
|
|
27
|
+
const [href, setHref] = useState('');
|
|
28
|
+
const [bridgeOk, setBridgeOk] = useState(false);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
let alive = true; // 等桥是异步的:组件卸载后不要再回写状态
|
|
32
|
+
(async () => {
|
|
33
|
+
setHref(location.href);
|
|
34
|
+
const t = await waitForBridge();
|
|
35
|
+
if (!alive) return;
|
|
36
|
+
bridge = t;
|
|
37
|
+
if (!t) {
|
|
38
|
+
setResult('未找到 window.__CJ_TAURI__');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setBridgeOk(true);
|
|
42
|
+
t.listen('tick', (payload) => {
|
|
43
|
+
setTick('事件 tick #' + payload.n + ' 来自仓颉后端');
|
|
44
|
+
});
|
|
45
|
+
// 原生菜单 → 页面(capabilities 里要声明 menu:click)
|
|
46
|
+
t.listen('menu:click', (p) => {
|
|
47
|
+
setMenuMsg('菜单被点: ' + p.id + '(enabled=' + p.enabled + ', checked=' + p.checked + ')');
|
|
48
|
+
});
|
|
49
|
+
})();
|
|
50
|
+
return () => {
|
|
51
|
+
alive = false;
|
|
52
|
+
};
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
async function greet() {
|
|
56
|
+
if (!bridge) {
|
|
57
|
+
setResult('桥未就绪');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const data = await bridge.invoke('greet', { name });
|
|
62
|
+
setResult('invoke OK: ' + data);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
setResult('invoke FAIL: ' + err.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function startTimer() {
|
|
69
|
+
if (bridge) await bridge.invoke('timer');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function reload() {
|
|
73
|
+
if (bridge) bridge.reload();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 页面 → 原生菜单:menu 插件的前端 shim(window.__CJ_TAURI__.menu,注入在 document start)
|
|
77
|
+
async function toggleSidebar(e) {
|
|
78
|
+
const checked = e.target.checked;
|
|
79
|
+
if (!bridge || !bridge.menu) {
|
|
80
|
+
setResult('未注入 menu 插件 shim(页面来自 runUrl 时插件 JS 不在本进程)');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
await bridge.menu.setChecked({ id: 'view.sidebar', checked });
|
|
85
|
+
setResult('原生菜单「侧边栏」已同步为 ' + checked);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
setResult('menu.setChecked FAIL: ' + err.message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
<h1>cj-tauri · React + Vite</h1>
|
|
94
|
+
<p className="hint">
|
|
95
|
+
页面来源:<code>{href}</code>
|
|
96
|
+
<br />
|
|
97
|
+
桥状态:{bridgeOk ? '已注入 __CJ_TAURI__' : '未注入'}
|
|
98
|
+
</p>
|
|
99
|
+
|
|
100
|
+
<div className="card">
|
|
101
|
+
<div className="row">
|
|
102
|
+
<input
|
|
103
|
+
value={name}
|
|
104
|
+
placeholder="输入名字"
|
|
105
|
+
onChange={(e) => setName(e.target.value)}
|
|
106
|
+
/>
|
|
107
|
+
<button onClick={greet}>greet</button>
|
|
108
|
+
<button className="ghost" onClick={startTimer}>timer</button>
|
|
109
|
+
<button className="ghost" onClick={reload}>reload</button>
|
|
110
|
+
</div>
|
|
111
|
+
<div className="out">{result}</div>
|
|
112
|
+
<div className="tick">{tick}</div>
|
|
113
|
+
<div className="tick">{menuMsg}</div>
|
|
114
|
+
<div className="chk">
|
|
115
|
+
<input type="checkbox" checked={sidebar} onChange={toggleSidebar} />
|
|
116
|
+
<span>侧边栏(对应原生菜单「视图 → 侧边栏」)</span>
|
|
117
|
+
</div>
|
|
118
|
+
<p className="tip">
|
|
119
|
+
改这个文件(<code>ui/src/App.jsx</code>)保存,页面会在不重启应用的情况下更新。
|
|
120
|
+
</p>
|
|
121
|
+
</div>
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# cj-tauri 应用清单——整个工程的单一真相(RFC-002 §7)。
|
|
2
|
+
# cj-tauri sync 从这里派生 targets/ 骨架(desktop/ohos);cj-tauri check 校验它并检测骨架漂移。
|
|
3
|
+
[app]
|
|
4
|
+
name = "{{PROJECT_NAME}}"
|
|
5
|
+
version = "0.1.0"
|
|
6
|
+
# 应用标识:桌面是安装 ID,鸿蒙直接是 bundleName(每段须字母开头、只含字母数字下划线)
|
|
7
|
+
id = "{{BUNDLE_NAME}}"
|
|
8
|
+
# 业务包名(cjpm 约束①:toml 模块名必须等于源包名)
|
|
9
|
+
package = "{{PACKAGE_NAME}}"
|
|
10
|
+
|
|
11
|
+
[[windows]]
|
|
12
|
+
label = "main"
|
|
13
|
+
title = "{{PROJECT_NAME}}"
|
|
14
|
+
width = 1000
|
|
15
|
+
height = 700
|
|
16
|
+
|
|
17
|
+
[assets]
|
|
18
|
+
# 骨架 launcher 加载的页面:Vite 打出来的单文件产物(vite-plugin-singlefile 内联 js/css)。
|
|
19
|
+
# dev 模式自动改写到 Vite dev server(热更新);发布构建才用这个产物。
|
|
20
|
+
page = "ui/dist/index.html"
|
|
21
|
+
|
|
22
|
+
[capabilities]
|
|
23
|
+
dir = "assets/capabilities"
|
|
24
|
+
|
|
25
|
+
[links]
|
|
26
|
+
# 各目标端**追加**的链接选项(拼在框架默认值之后;留空 = 只用默认)。
|
|
27
|
+
# 什么时候需要它:要调的原生能力在仓颉 SDK 里没有绑定,只能 FFI 直连系统库——
|
|
28
|
+
# 符号必须出现在最终链接那一侧,而那份 cjpm.toml 是 sync 生成的、不能手改。
|
|
29
|
+
# 例:鸿蒙读设备电量走系统 C API(ohbattery_info.h),写 ohos = "-lohbattery_info"
|
|
30
|
+
# 三端分开写是因为同一个库在三端上名字通常不同(-lfoo / foo.lib / -lfoo_ndk.z)。
|
|
31
|
+
desktop-linux = ""
|
|
32
|
+
desktop-windows = ""
|
|
33
|
+
ohos = ""
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
+
# 业务包:业务代码的唯一真相(RFC-002 §7.3——骨架永远是外壳,业务只有一份)。
|
|
2
|
+
# output-type = "static":桌面骨架(targets/desktop)与鸿蒙入口(targets/ohos/entry)
|
|
3
|
+
# 都以 path 依赖引用本包——同一份业务代码,多个目标各编各的产物。
|
|
1
4
|
[package]
|
|
2
5
|
cjc-version = "{{CJC_VERSION}}"
|
|
3
6
|
name = "{{PACKAGE_NAME}}"
|
|
4
|
-
description = "{{PROJECT_NAME}} - cj-tauri
|
|
7
|
+
description = "{{PROJECT_NAME}} - cj-tauri 业务包"
|
|
5
8
|
version = "0.1.0"
|
|
6
9
|
target-dir = ""
|
|
7
|
-
output-type = "
|
|
10
|
+
output-type = "static"
|
|
8
11
|
compile-option = ""
|
|
9
12
|
override-compile-option = ""
|
|
10
13
|
link-option = ""
|
|
11
14
|
package-configuration = {}
|
|
12
15
|
|
|
13
16
|
[dependencies]
|
|
14
|
-
# cj-tauri 框架本体(由 cj-tauri create
|
|
17
|
+
# cj-tauri 框架本体(由 cj-tauri create 写入的本机路径;换机器重跑 create 或手改)
|
|
15
18
|
cjTauri = { path = "{{FRAMEWORK_ROOT_TOML}}" }
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
[target.x86_64-unknown-linux-gnu.bin-dependencies]
|
|
21
|
+
path-option = ["${CANGJIE_STDX}"]
|
|
22
|
+
|
|
23
|
+
[target.x86_64-w64-mingw32.bin-dependencies]
|
|
24
|
+
path-option = ["${CANGJIE_STDX}"]
|
|
25
|
+
|
|
26
|
+
[target.aarch64-linux-ohos]
|
|
27
|
+
# 鸿蒙目标:交叉编译工具链与 sysroot 由 DevEco 仓颉套件提供,路径全部走环境变量
|
|
28
|
+
# (与框架根 cjpm.toml 同口径;末尾 --dy-std 不可省,原因见框架根 toml 的鸿蒙段注释)
|
|
29
|
+
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\" --dy-std"
|
|
30
|
+
|
|
31
|
+
[target.aarch64-linux-ohos.bin-dependencies]
|
|
32
|
+
# stdx 必须用鸿蒙版(CANGJIE_STDX_OHOS,桌面版 ABI 不通);CANGJIE_OHOS_LIBS 指 DevEco
|
|
33
|
+
# 套件的 api/lib/linux_ohos_aarch64_cjnative/ohos(仓颉标准库 + ohos API 绑定)
|
|
34
|
+
path-option = ["${CANGJIE_STDX_OHOS}", "${CANGJIE_OHOS_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
package {{PACKAGE_NAME}}
|
|
2
2
|
|
|
3
3
|
import cjTauri.*
|
|
4
|
+
import cjTauri.plugins.menu.*
|
|
4
5
|
import stdx.encoding.json.*
|
|
5
|
-
import std.fs.*
|
|
6
6
|
import std.collection.*
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -25,7 +25,7 @@ public class GreetCommand <: CommandHandler {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* 定时器命令:启动后台线程,每秒向前端推一个 tick 事件(验证 仓颉 → JS 通道)
|
|
29
29
|
* 前端:tauri.listen("tick", cb) + tauri.invoke("timer")
|
|
30
30
|
*/
|
|
31
31
|
public class TimerCommand <: CommandHandler {
|
|
@@ -67,36 +67,25 @@ func demoMenu(): MenuModel {
|
|
|
67
67
|
return MenuModel(items)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
/**
|
|
71
|
+
* 业务装配点(RFC-002 §4.1):骨架的 launcher(targets/ 下各目标的 src)调用它取 AppSpec。
|
|
72
|
+
*
|
|
73
|
+
* 零平台知识:不碰宿主、不知道页面文件路径、不知道能力目录——那些由清单
|
|
74
|
+
* (cj-tauri.toml)决定,launcher 按清单覆盖(page/window/assets/capabilities)。
|
|
75
|
+
* dev 模式页面来自 Vite dev server(框架按 CJ_TAURI_DEV_URL 自动改写),
|
|
76
|
+
* 发布构建加载 assets/ui/dist 的单文件产物——两种跑法业务代码完全一致。
|
|
77
|
+
*/
|
|
78
|
+
public func build(): AppSpec {
|
|
79
|
+
return AppSpec()
|
|
74
80
|
.window(WindowConfig("{{PROJECT_NAME}}", 1000, 700))
|
|
75
|
-
.register("greet", GreetCommand())
|
|
76
|
-
.register("timer", TimerCommand())
|
|
77
81
|
// menu 插件:给前端挂 window.__CJ_TAURI__.menu.setEnabled / setChecked
|
|
78
82
|
// (插件只声明「我提供什么」,**不自动放行**——要能调,就在 capabilities 里引用
|
|
79
83
|
// permissions: ["menu:state"],或逐条写 commands)
|
|
80
84
|
.plugin(MenuPlugin())
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// 发布 : 读 Vite 打出来的单文件 ui/dist/index.html(js/css 已内联,无外部依赖)
|
|
88
|
-
if (let Some(url) <- TauriApp.devUrl()) {
|
|
89
|
-
eprintln("[app] dev 模式,页面来自 ${url}")
|
|
90
|
-
app.runUrl(url)
|
|
91
|
-
} else {
|
|
92
|
-
let dist = "ui/dist/index.html"
|
|
93
|
-
if (!exists(dist)) {
|
|
94
|
-
eprintln("[app] 找不到 ${dist}")
|
|
95
|
-
eprintln(" 开发: 在项目根跑 cj-tauri dev(会自动 npm install + 起 dev server)")
|
|
96
|
-
eprintln(" 发布: cd ui && npm run build")
|
|
97
|
-
return 1
|
|
98
|
-
}
|
|
99
|
-
app.run(String.fromUtf8(File.readFrom(dist)))
|
|
100
|
-
}
|
|
101
|
-
return 0
|
|
85
|
+
.wire({ app =>
|
|
86
|
+
app.register("greet", GreetCommand())
|
|
87
|
+
app.register("timer", TimerCommand())
|
|
88
|
+
// 菜单栏必须在 run 之前挂:预置类宿主能力要赶在页面加载前定稿
|
|
89
|
+
app.setMenu(demoMenu())
|
|
90
|
+
})
|
|
102
91
|
}
|
|
@@ -1,60 +1,56 @@
|
|
|
1
1
|
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
基于 [cj-tauri]({{FRAMEWORK_ROOT}})(仓颉版 Tauri)+ Vue 3 + Vite 的应用:**仓颉后端 + 系统 WebView 前端**,
|
|
4
|
+
通过 `window.__CJ_TAURI__` 做 invoke/listen 双向 IPC,并由 capabilities 清单做最小权限管控。
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
## 目录结构(一份业务代码,骨架按目标派生)
|
|
6
7
|
|
|
7
8
|
```
|
|
8
9
|
{{PROJECT_NAME}}/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
├── cj-tauri.toml # 应用清单——单一真相(id/version/窗口/资产/能力目录)
|
|
11
|
+
├── cjpm.toml # 业务包(static 库;骨架以 path 依赖引用)
|
|
12
|
+
├── src/main.cj # 业务装配点 build(): AppSpec(零平台知识)
|
|
13
|
+
├── assets/
|
|
14
|
+
│ ├── ui/ # Vue 3 + Vite 前端工程(package.json / vite.config.js / src/)
|
|
15
|
+
│ └── capabilities/default.json # 能力白名单(命令 / 事件 / 权限集)
|
|
16
|
+
└── targets/ # cj-tauri sync 生成的骨架(勿手改;check 判漂移)
|
|
17
|
+
├── desktop/ # 桌面可执行(launcher 调 build())
|
|
18
|
+
└── ohos/ # 鸿蒙 hap 工程(DevEco + build-hap.sh)
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
##
|
|
21
|
+
## 首次运行(dev,热更新)
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
|
-
|
|
24
|
+
cd assets/ui && npm install # 前端依赖(需要 Node.js)
|
|
25
|
+
cd ../.. && cj-tauri sync # 派生 targets/ 骨架
|
|
26
|
+
cj-tauri dev # 起 Vite dev server(改 .vue 保存即刷新)+ 编译 + 启动窗口
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
(`127.0.0.1:5173`)→ 探活 → 以环境变量 `CJ_TAURI_DEV_URL` 启动应用。页面来自真实 URL,所以:
|
|
27
|
-
|
|
28
|
-
- 改 `ui/src/**.vue` / `.js`:保存后页面自己更新,**应用不重启**(Vite HMR);
|
|
29
|
-
- 改后端 `src/*.cj`:需要重新 `cj-tauri dev`(框架不承诺后端进程内热替换);
|
|
30
|
-
- 应用窗口关闭后会一并收掉 dev server。
|
|
31
|
-
|
|
32
|
-
想手动控制前端时,也可以自己起 dev server 再启动应用:
|
|
29
|
+
## 发布构建
|
|
33
30
|
|
|
34
31
|
```bash
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
cj-tauri build # 先在 assets/ui 跑 npm run build,再编后端
|
|
33
|
+
cj-tauri run
|
|
37
34
|
```
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
`vite.config.js` 用 vite-plugin-singlefile:产物是 `assets/ui/dist/index.html`
|
|
37
|
+
(js/css 内联、无外部依赖)——正是清单 `[assets] page` 指向的页面。
|
|
38
|
+
|
|
39
|
+
## 鸿蒙端(PC / 2in1)
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
|
|
43
|
-
cd
|
|
44
|
-
|
|
42
|
+
export CANGJIE_STDX_OHOS=<鸿蒙版 stdx>/linux_ohos_aarch64_cjnative/dynamic/stdx
|
|
43
|
+
cd targets/ohos && bash build-hap.sh # 编 hap
|
|
44
|
+
cd targets/ohos && bash build-hap.sh --install # 编完装到设备并拉起
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
提示:模板不带签名证书——首次在 DevEco 里 File → Project Structure → Signing Configs
|
|
48
|
+
勾「Automatically generate signature」(要登录华为账号);不签名只能编出
|
|
49
|
+
*-unsigned.hap,装不进设备。
|
|
48
50
|
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
前端拿到的是 `window.__CJ_TAURI__`(与 Tauri 的 `@tauri-apps/api` 同形):
|
|
52
|
-
|
|
53
|
-
```js
|
|
54
|
-
window.__CJ_TAURI__.invoke('greet', { name: '仓颉' }); // → Promise
|
|
55
|
-
window.__CJ_TAURI__.listen('tick', (payload) => { /* ... */ });
|
|
56
|
-
window.__CJ_TAURI__.reload(); // 整页刷新
|
|
57
|
-
```
|
|
51
|
+
## 命令与能力
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
命令在 `src/main.cj`(`greet` / `timer` + 演示菜单),白名单在 `assets/capabilities/default.json`——
|
|
54
|
+
两边必须对齐:`cj-tauri check` 的三方比对会交叉核对「能力声明 × 插件自描述 × 实际装配」。
|
|
55
|
+
插件在某端没有实现时(如 shell/sqlite 在鸿蒙端),骨架照样编得过,运行期调用得到
|
|
56
|
+
可照做的「端不支持」错误(RFC-002 §4.6.4 的 P-1 桩)。
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>{{PROJECT_NAME}}</title>
|
|
7
|
-
<style>
|
|
8
|
-
/* 整页基础样式:组件样式在各组件里(App.vue),这里给页面底色 + 让内容整列居中,
|
|
9
|
-
调色板与零 Node 模板、examples/hello 一致(#16161d 深色底)。 */
|
|
10
|
-
:root { color-scheme: dark; }
|
|
11
|
-
/* min-height(不是 height)+ 居中:内容比视口高时 body 跟着长,顶部不会被裁掉。 */
|
|
12
|
-
body { box-sizing: border-box; margin: 0; padding: 24px; min-height: 100vh;
|
|
13
|
-
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
14
|
-
background: #16161d; color: #e6e6e6;
|
|
15
|
-
font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
16
|
-
/* 标题 / 提示 / 卡片同一列居中:光靠 body 的 align-items 不够——挂载点只有一个子节点,
|
|
17
|
-
提示行(页面来源、桥状态)很长时会把整列撑宽,卡片反而贴在左边。 */
|
|
18
|
-
#app { width: 100%; display: flex; flex-direction: column; align-items: center; }
|
|
19
|
-
</style>
|
|
20
|
-
</head>
|
|
21
|
-
<body>
|
|
22
|
-
<div id="app"></div>
|
|
23
|
-
<script type="module" src="/src/main.js"></script>
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{PROJECT_NAME}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* 整页基础样式:组件样式在各组件里(App.vue),这里给页面底色 + 让内容整列居中,
|
|
9
|
+
调色板与零 Node 模板、examples/hello 一致(#16161d 深色底)。 */
|
|
10
|
+
:root { color-scheme: dark; }
|
|
11
|
+
/* min-height(不是 height)+ 居中:内容比视口高时 body 跟着长,顶部不会被裁掉。 */
|
|
12
|
+
body { box-sizing: border-box; margin: 0; padding: 24px; min-height: 100vh;
|
|
13
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
14
|
+
background: #16161d; color: #e6e6e6;
|
|
15
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
16
|
+
/* 标题 / 提示 / 卡片同一列居中:光靠 body 的 align-items 不够——挂载点只有一个子节点,
|
|
17
|
+
提示行(页面来源、桥状态)很长时会把整列撑宽,卡片反而贴在左边。 */
|
|
18
|
+
#app { width: 100%; display: flex; flex-direction: column; align-items: center; }
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div id="app"></div>
|
|
23
|
+
<script type="module" src="/src/main.js"></script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|