| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
docs: add CHANGELOG and dev contract, bump project version to 0.3.0 - Add CHANGELOG.md (Keep a Changelog style) backfilling 0.1.0 / 0.2.0 / 0.3.0. - Rewrite AGENTS.md into this project's development contract: delivery gates, architecture rules (platform isolation, the command registration trio, one-way dependencies), coding style, platform gotchas already hit, dual-remote commit rules and the release checklist. - Single-source the framework version in src/version.cj and make system:version report the real host platform (it was hard-coded "linux"). - Bump cjpm.toml, cli/cjpm.toml and cliVersion() to 0.3.0; add scripts/check-version.sh to verify all four version locations. - Document the version table in docs/使用文档.md, link CHANGELOG/AGENTS from README.md, and let examples/hello assert system:version. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 4 天前 | |
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
feat(capability): named permission sets for plugin-provided command groups Capability manifests gain an optional permissions field, so an app can grant a group of plugin commands by set name instead of listing every full command name: - Plugin.permissions() declares short set name -> members; a short member is namespaced with <plugin>: while a fully qualified name is kept as-is, and the set name itself becomes <plugin>:<short set name>. - CapabilityRegistry.addPermissionSet registers sets at plugin assembly time; canInvoke / canEmit now also accept names covered by a referenced set (single-level expansion, no recursion). - A declared set never auto-grants: it only takes effect when a manifest references it, so the manifest stays the security gate. - warnUnknownPermissionSets reports referenced set names no plugin provides (typo or a plugin that was never assembled) without failing anything. - Plain command names keep working unchanged; the two forms can be mixed and their grants are unioned. - FsPlugin ships fs:readonly (readText + exists) and fs:default (adds writeText), so read-only apps never get write access by accident. - examples/plugin-fs manifest switched to "permissions": ["fs:readonly"], keeping fs:writeText outside as the unauthorized control group. - Tests 38 -> 50 (6 capability-set cases, 6 plugin-set cases). Evidence (Linux / WebKitGTK / Xvfb, examples/plugin-fs, log /tmp/cj-plugin-fs-permset.log): [cj-tauri] 命名权限集(在 capabilities 的 permissions 里按集名引用): fs:readonly fs:readText, fs:exists fs:default fs:readText, fs:writeText, fs:exists [cj-tauri] 插件 fs 的命令 fs:writeText 尚未授权,前端调用会被拒绝 [frontend] fs:readText OK => 147 chars; 清单用集 fs:readonly=true; 清单未写死 fs:readText=true [frontend] DENY-OK fs:writeText: command not allowed: fs:writeText [frontend] [verify] ALL DONE Only fs:writeText is reported unauthorized (readText/exists are covered by the set), and the page read succeeds purely through the named set. Windows not run. Docs: CHANGELOG, AGENTS.md §1/§2, 使用文档 §6.7, 插件体系教程 §2.2/§3.3/§3.5/§4.4/§5.5/§6/§7/§8, RFC-001 §5.3/§7.1/§8, 进度记录. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 4 天前 | |
feat: configurable window, capability auto-loading and devtools toggle - Add WindowConfig(title, width, height, devTools) plus TauriApp.window(), loadCapabilities() and openDevTools(). Defaults keep the previously hard-coded title/size, so existing apps are unaffected. - Auto-scan capabilities json manifests on run() when no capability was mounted explicitly; an explicit loadCapabilities()/addCapabilityJson() takes precedence. - Register the system:devtools built-in command (Windows: WebView2 OpenDevToolsWindow, Linux: WebKit inspector). - Add cj_bridge_set_window / cj_bridge_open_devtools to both C bridges. - Migrate the scaffold template and examples/hello to the new API, and make run_win.bat run the example from the project root. - Sync README.md and docs/使用文档.md. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 4 天前 | |
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
feat(core): window registry and label routing for IPC - TauriApp owns a WindowRegistry (label -> per-window host + config); duplicate labels throw, an unknown label resolves to None - new AppHost interface implemented by TauriApp: quit(), waitForExit(), hostOf(label); WebViewHost gains label(), while quit()/shouldQuit() stay as low-level per-window primitives - bridge JS lazily reads window.__CJ_TAURI_LABEL__ (default "main"), injected by the host as a document-start init script; invoke/emit envelopes now carry a window field and IpcContext.window follows the originating window. The wire change is additive: parseEnvelope only reads known fields, and a missing/non-string/empty window falls back to "main" - events and replies are routed per label: jsSink became (String, String) -> Unit and emitToWindow reports an unknown label on stderr instead of dropping the message silently - tests: 91 -> 101 (WindowRegistry cases plus a fake_host test double) Single-window behaviour is unchanged. Verified on Linux/WebKitGTK: ipc-coalesce three-way comparison, three ipc-bench rounds (11364 / 12195 ops/s vs the 11905 baseline) and 18 quit-path rounds (with one pre-existing exit-time SIGABRT observed and documented). Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 | |
feat(core): window registry and label routing for IPC - TauriApp owns a WindowRegistry (label -> per-window host + config); duplicate labels throw, an unknown label resolves to None - new AppHost interface implemented by TauriApp: quit(), waitForExit(), hostOf(label); WebViewHost gains label(), while quit()/shouldQuit() stay as low-level per-window primitives - bridge JS lazily reads window.__CJ_TAURI_LABEL__ (default "main"), injected by the host as a document-start init script; invoke/emit envelopes now carry a window field and IpcContext.window follows the originating window. The wire change is additive: parseEnvelope only reads known fields, and a missing/non-string/empty window falls back to "main" - events and replies are routed per label: jsSink became (String, String) -> Unit and emitToWindow reports an unknown label on stderr instead of dropping the message silently - tests: 91 -> 101 (WindowRegistry cases plus a fake_host test double) Single-window behaviour is unchanged. Verified on Linux/WebKitGTK: ipc-coalesce three-way comparison, three ipc-bench rounds (11364 / 12195 ops/s vs the 11905 baseline) and 18 quit-path rounds (with one pre-existing exit-time SIGABRT observed and documented). Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 | |
feat(menu): add the menu model and the host interface Give an application a platform-neutral way to describe a menu, and let the two hosts carry it down to the bridge. - src/menu.cj: MenuModel / MenuItem, serialised into the bridge's line format -- tab-separated, indent depth is the nesting level, kind first, always five fields, no escaping, separators carry neither id nor label. JSON never crosses the FFI boundary. - src/host.cj: WebViewHost gains setMenu / setMenuItemState / hostCapabilities / setShellHandler, plus the CJ_CAP_* capability bits. - src/host.cj: shell events go through a handle-keyed table (ShellRoute + registerShellRoute) instead of one static slot. With a single slot a later-assembled window overwrote the earlier one, so in a two-window app every click landed in whichever window had registered last. - src/app.cj: hostOf(label) falls back to the default window's host, so setMenu can be called before run() the way RFC-002 requires. Without the fallback the default window is simply "not registered" during assembly, and the pre-start menu call has nowhere to go. - src/tests/: menu round-trip cases plus the fake host's new recorders. scripts/test.sh: 108 passed. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 2 天前 | |
feat(plugin): add describePluginsJson() for machine-readable manifests RFC §8 item 7 (option C): answer it with a framework-side function instead of a Plugin interface method. The data is already derivable from commands()/events()/permissions(), so an extra interface method would only burden plugin authors, while tools and CI want a function to call. Shape: [{name, commands, events, permissions, hasShim}] with commands/events as fully-qualified names and permissions mapping a set's full name to its expanded member names. All arrays and permission keys are sorted, since HashMap iteration order is unstable and tool output/diffing needs to be. Not wired into cj-tauri info yet: the CLI does not start the app, so it has no access to the assembled plugin list. Unit tests 71 -> 74 (shape, stability across runs, empty list). Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 | |
feat(plugin): add official dialog plugin with system-native dialogs Add DialogPlugin() with three commands and two named permission sets: - dialog:open / dialog:save -> native file chooser, returns the picked path (empty string when cancelled) - dialog:message -> native modal box, kind = info / warning / error / confirm (confirm returns false on cancel) Native dialogs: GTK on Linux, Win32 common dialogs (GetOpenFileNameW / GetSaveFileNameW / MessageBoxW) on Windows. Results travel back through the existing C->Cangjie callback channel, so no new FFI shape is introduced. New host capability follows the AGENTS.md section 2 contract: extend WebViewHost (showFileDialog / showMessageDialog), then both platform hosts, then same-name same-signature exports in the two C bridges. Permission sets dialog:files (open + save) and dialog:default (plus message) work exactly like the fs ones: declared by the plugin, only granted when the capability manifest references them -- nothing is auto-granted. Fix a real deadlock found by the first real-machine run: the JS->native callback already runs on the host UI thread (the GTK thread on Linux, the WebView2 message-loop thread on Windows), so posting the dialog work to that thread and blocking on the result locks the UI thread against itself. The symptom was the log stopping at "[cj-bridge] dialog: kind=..." with no dialog ever appearing. Both bridges now branch on the calling thread: run the dialog inline when already on the UI thread, otherwise post and wait. Verified on Linux (WebKitGTK / Xvfb), log /tmp/cj-plugin-dialog.log: dialog: kind=2 title=cj-tauri dialog 示例 (caller on GTK thread) dialog closed: kind=2 ok=1 dialog:message(info) => confirmed=true dialog closed: kind=0 ok=1 path=/tmp/dialog-pick.txt dialog:open => path="/tmp/dialog-pick.txt" DENY-OK system:devtools: command not allowed: system:devtools [verify] ALL DONE Screenshot docs/images/example-plugin-dialog.png. Windows is NOT verified on a real machine yet (development machine is Linux only); the new bridge code was compile-checked with mingw and a stub harness. Gates: cjpm build ok; cjpm test 57/57; scripts/check-version.sh 5/5; scripts/check-static.sh ok. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 | |
feat(capability): named permission sets for plugin-provided command groups Capability manifests gain an optional permissions field, so an app can grant a group of plugin commands by set name instead of listing every full command name: - Plugin.permissions() declares short set name -> members; a short member is namespaced with <plugin>: while a fully qualified name is kept as-is, and the set name itself becomes <plugin>:<short set name>. - CapabilityRegistry.addPermissionSet registers sets at plugin assembly time; canInvoke / canEmit now also accept names covered by a referenced set (single-level expansion, no recursion). - A declared set never auto-grants: it only takes effect when a manifest references it, so the manifest stays the security gate. - warnUnknownPermissionSets reports referenced set names no plugin provides (typo or a plugin that was never assembled) without failing anything. - Plain command names keep working unchanged; the two forms can be mixed and their grants are unioned. - FsPlugin ships fs:readonly (readText + exists) and fs:default (adds writeText), so read-only apps never get write access by accident. - examples/plugin-fs manifest switched to "permissions": ["fs:readonly"], keeping fs:writeText outside as the unauthorized control group. - Tests 38 -> 50 (6 capability-set cases, 6 plugin-set cases). Evidence (Linux / WebKitGTK / Xvfb, examples/plugin-fs, log /tmp/cj-plugin-fs-permset.log): [cj-tauri] 命名权限集(在 capabilities 的 permissions 里按集名引用): fs:readonly fs:readText, fs:exists fs:default fs:readText, fs:writeText, fs:exists [cj-tauri] 插件 fs 的命令 fs:writeText 尚未授权,前端调用会被拒绝 [frontend] fs:readText OK => 147 chars; 清单用集 fs:readonly=true; 清单未写死 fs:readText=true [frontend] DENY-OK fs:writeText: command not allowed: fs:writeText [frontend] [verify] ALL DONE Only fs:writeText is reported unauthorized (readText/exists are covered by the set), and the page read succeeds purely through the named set. Windows not run. Docs: CHANGELOG, AGENTS.md §1/§2, 使用文档 §6.7, 插件体系教程 §2.2/§3.3/§3.5/§4.4/§5.5/§6/§7/§8, RFC-001 §5.3/§7.1/§8, 进度记录. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 4 天前 | |
docs: document async command dispatch and refresh evidence screenshots - CHANGELOG:新增 Changed 首条(破坏性变更)+ 实测对照数据——同步臂 6 次心跳全堆在命令结束之后 (+3126…3133ms),异步臂首跳 +503ms 并贯穿 3 秒命令全程;shell 插件「阻塞 UI 线程」的限制改为已解除 - AGENTS.md:§2 补「命令执行线程模型」契约、§4 更新 shell 条目并新增三条实机取证坑 (pkill 模式与进程名 / 抓屏会污染时序证据 / 等窗口改轮询),用例数 68 → 71 - README / 使用文档 / 插件体系教程 / RFC §8 / 进度记录:同一处措辞同步,RFC §8 第 8 条①标为已落地 - 截图:新增 docs/images/example-plugin-async.png(异步臂对照),example-plugin-shell.png 换成六按钮版 Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 | |
chore(release): 0.7.0 Ships the menu capability (WebViewHost.setMenu / setMenuItemState / hostCapabilities / setShellHandler plus the five cj_bridge_* exports, both platforms) and the multi-window seam, together with the fixes the Windows verification round turned up (bridge_core.c was not compiled, CreateWindowExW failing with 0, four real menu bugs, probe ordering). That round also settled the platform split: the same "one host, one UI thread, one message loop" layout does support two windows on Windows, so the single-main-loop rework is a Linux-only prerequisite. This commit syncs the release metadata as well: CHANGELOG [0.7.0] - 2026-10-05, the five version positions, the README / AGENTS / architecture-doc version headers, AGENTS' test counts (108 unit cases, 91 bridge-core assertions), and a new pitfall -- rebuild the C bridge after pulling a commit that adds exports, or the link step fails on undefined cj_bridge_* references. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 1 天前 | |
feat: add window icons via WindowConfig.iconPath WindowConfig gains iconPath (default "", so apps that do not set it behave exactly as before) and both bridges export cj_bridge_set_icon, called with the rest of the window config before the host starts. Windows loads the .ico twice with LoadImageW - SM_CXICON for the big icon, SM_CXSMICON for the small one - and sets both the window class icon and WM_SETICON, so the title bar, taskbar and Alt-Tab agree. A failed load only falls back to the system icon and logs one line; it never blocks startup. GetLastError is reported only on failure, since it holds a stale value after a successful call. Linux uses GTK's gtk_window_set_icon_from_file. examples/hello carries an icon.ico so the path is exercised on every run, and scripts/make-demo-icon.js regenerates that file rather than committing an unexplained binary. Verified on Windows: the bridge logs set icon: path=icon.ico then window icon: path=icon.ico loaded (big=0x... small=0x...), and the frontend end-to-end checks still report ALL DONE. cjpm build and cjpm test (23/23) pass. The Linux path is implemented but unverified - this machine has no GTK/WebKit toolchain. Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 4 天前 | |
feat(core): window registry and label routing for IPC - TauriApp owns a WindowRegistry (label -> per-window host + config); duplicate labels throw, an unknown label resolves to None - new AppHost interface implemented by TauriApp: quit(), waitForExit(), hostOf(label); WebViewHost gains label(), while quit()/shouldQuit() stay as low-level per-window primitives - bridge JS lazily reads window.__CJ_TAURI_LABEL__ (default "main"), injected by the host as a document-start init script; invoke/emit envelopes now carry a window field and IpcContext.window follows the originating window. The wire change is additive: parseEnvelope only reads known fields, and a missing/non-string/empty window falls back to "main" - events and replies are routed per label: jsSink became (String, String) -> Unit and emitToWindow reports an unknown label on stderr instead of dropping the message silently - tests: 91 -> 101 (WindowRegistry cases plus a fake_host test double) Single-window behaviour is unchanged. Verified on Linux/WebKitGTK: ipc-coalesce three-way comparison, three ipc-bench rounds (11364 / 12195 ops/s vs the 11905 baseline) and 18 quit-path rounds (with one pre-existing exit-time SIGABRT observed and documented). Co-Authored-By: AtomCode (deepseek-v4-flash-vision-exp) <noreply@atomgit.com> | 3 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 2 天前 | ||
| 4 天前 | ||
| 2 天前 | ||
| 4 天前 | ||
| 4 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 3 天前 | ||
| 3 天前 | ||
| 2 天前 | ||
| 3 天前 | ||
| 3 天前 | ||
| 4 天前 | ||
| 3 天前 | ||
| 1 天前 | ||
| 4 天前 | ||
| 3 天前 |