@qilin-agent/client-ui-directory-picker-native 0.0.0-stage → 3.1.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkutysllb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-directory-picker-native/README.md
5
+ /:
6
+ en: 6f45a471a5e9305f
7
+ zh: d771883685e91a45
8
+ /qilin-agent-client-ui-directory-picker-native:
9
+ en: 6251d6baf414cbd5
10
+ zh: 30c71437be56b37b
11
+ /qilin-agent-client-ui-directory-picker-native/summary:
12
+ en: cc747c58ca147259
13
+ zh: 4c824ab9ca48fb50
14
+ /qilin-agent-client-ui-directory-picker-native/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /qilin-agent-client-ui-directory-picker-native/use-this-package:
18
+ en: db8eb73d3deb11cf
19
+ zh: 117b1601bec2d17e
20
+ /qilin-agent-client-ui-directory-picker-native/use-this-package/when-to-choose-it:
21
+ en: 21e63f891429e3e4
22
+ zh: 59f2db2767873b37
23
+ /qilin-agent-client-ui-directory-picker-native/understand-the-implementation:
24
+ en: f7a287740b4b08f9
25
+ zh: 3347375c4e486b22
26
+ /qilin-agent-client-ui-directory-picker-native/further-exploration:
27
+ en: 41bda049cc870571
28
+ zh: 549115c78ea39360
29
+ /qilin-agent-client-ui-directory-picker-native/model-experience:
30
+ en: 35d8546cfb4b540b
31
+ zh: 25e4ce35c66c92e2
32
+ /qilin-agent-client-ui-directory-picker-native/model-experience/kv-cache-effect:
33
+ en: ca75c51c89c2c9b0
34
+ zh: f57cc48a1b5cbfa9
35
+ /qilin-agent-client-ui-directory-picker-native/known-limitations-and-deferred-work:
36
+ en: 52484b1e7a8c91de
37
+ zh: 701059acbb1b78ed
38
+ /qilin-agent-client-ui-directory-picker-native/known-limitations-and-deferred-work/dev-note:
39
+ en: e67f633dc6fe7773
40
+ zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -1,3 +1,90 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Native directory-picker surface: the browser half that drives the local Desktop or Host OS chooser for workspace-directory flows; for users and maintainers choosing a picking interaction."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @qilin-agent/client-ui-directory-picker-native
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ This package provides the native directory-picking surface for the Web GUI: when a workspace flow asks for a directory, a renderless browser occupant opens the operating system's own chooser on the local machine and reports the single outcome — a picked path, a cancellation, or a failure. It fills the two directory-flow slots declared by `ui-workspace`, composing the client side of the native picking interaction in one `cordis.yml` row. Choose it when the browser runs on the same machine as the Host; in-process and remote-browser deployments need the [`-browse`](../ui-directory-picker-browse/README.md) surface instead.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this plugin alongside `ui-workspace` and the host backend [`qilin-host-directory-picker-native`](../../host/directory-picker-native/README.md); one `cordis.yml` row then composes the whole native picking interaction. When a workspace add or picker flow opens a directory request, the user sees the operating system's folder dialog; the picked path is adopted by the workspace flow, and cancelling closes the dialog.
29
+
30
+ A desktop carrier may inject the narrow `__QILIN_DIRECTORY_PICKER__` preload bridge; this flow uses it when present. Cancellation and failure never retry through the Host chooser. Ordinary Web uses the Host call; the separate browse composition always lists Host directories.
31
+
32
+ ### When to choose it
33
+
34
+ Choose this surface when the browser runs on the same machine as the Host, so an OS dialog can open there. Choose the [`-browse`](../ui-directory-picker-browse/README.md) surface when the browser is remote or in-process and no local chooser exists. The two surfaces fill the same slots, so switching is a composition change, not a code change.
35
+
36
+ -----
37
+
38
+ <a id="understand-the-implementation"></a>
39
+ ## Understand the implementation
40
+
41
+ <details>
42
+ <summary>Implementation internals — click to expand</summary>
43
+
44
+ Both slot registrations install as one transactional effect through nested `ctx.slots.inject()` calls, because either declaring entry may activate later or replace its declaration. The occupant arms once per rising `open` edge, so re-renders never launch a second chooser; settlements ride a ref so the answer reaches the owner's latest handlers. An unmount (HMR replacing the occupant) discards the settlement wholesale: the wire carries no per-request abort, so the native chooser survives until answered and its answer lands nowhere. The node half is an empty `apply` that keeps the plugin on the host roster.
45
+
46
+ </details>
47
+
48
+ -----
49
+
50
+ <a id="further-exploration"></a>
51
+ ## Further Exploration
52
+
53
+ Read these pages when the picking surface is not enough. They move from the browser half to the host backend and the slots it fills.
54
+
55
+ - [qilin-host-directory-picker-native](../../host/directory-picker-native/README.md) — the OS chooser backend this surface drives.
56
+ - [ui-workspace](../ui-workspace/README.md) — declares the directory-flow slots and owns the picking conversation.
57
+ - [ui-directory-picker-browse](../ui-directory-picker-browse/README.md) — the in-app browsing alternative for remote and in-process deployments.
58
+ - [Web client architecture](../../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) — how browser plugin rows load and register slots.
59
+
60
+ -----
61
+
62
+ <a id="model-experience"></a>
63
+ ## Model Experience
64
+
65
+ None, as the directory chooser is browser chrome; nothing here reaches a model request.
66
+
67
+ #### KV Cache effect
68
+
69
+ None; this package neither assembles nor sends a provider request.
70
+
71
+ ## Known Limitations and Deferred Work
72
+
73
+ <a id="known-limitations-and-deferred-work"></a>
74
+
75
+
76
+ These limits define when the native chooser fits. They are current package constraints, not a general picker comparison or a task backlog.
77
+
78
+ - **No cancellation of an open chooser** — the wire has no per-request abort, so a chooser already on the local display cannot be closed from the browser; a discarded settlement is ignored.
79
+ - **Local carriers only** — a local carrier dialog selects local paths; ordinary Web opens the Host chooser. Remote-browser and in-process deployments use the `-browse` composition. Platform failures surface through the owner's retryable folder dialog.
80
+ - **Linux automatic selection** — without zenity or kdialog, the Host selects browse even when a carrier bridge is present; the carrier dialog is not used.
81
+
82
+ <a id="dev-note"></a>
83
+ ### Dev Note
84
+
85
+ <details>
86
+ <summary>Working context for maintainers — click to expand</summary>
87
+
88
+ None.
89
+
90
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,90 @@
1
+ ---
2
+ description: "原生目录选择表面:驱动本地 Desktop 或 Host 操作系统选择器的浏览器半部,用于工作区目录流程;供选择拾取交互的用户与维护者阅读。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @qilin-agent/client-ui-directory-picker-native
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 本包提供 Web GUI 的原生目录拾取表面:当工作区流程请求一个目录时,一个无渲染的浏览器填充会在本地机器上打开操作系统自带的选择器,并回报唯一结果——拾取的路径、取消或失败。它填充 `ui-workspace` 声明的两个目录流程 slot,用一行 `cordis.yml` 组合出原生拾取交互的客户端一侧。当浏览器与 Host 运行在同一台机器上时选择它;进程内与远程浏览器部署则需要 [`-browse`](../ui-directory-picker-browse/README.zh.md) 表面。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 与 `ui-workspace` 及 Host 后端 [`qilin-host-directory-picker-native`](../../host/directory-picker-native/README.zh.md) 一起挂载本插件;一行 `cordis.yml` 随即组合出完整的原生拾取交互。当工作区添加或选择器流程发起目录请求时,用户看到操作系统的文件夹对话框;拾取的路径被工作区流程采纳,取消则关闭对话框。
29
+
30
+ 桌面载体可以注入 preload 提供的窄 `__QILIN_DIRECTORY_PICKER__` 接口;此流程在接口存在时使用它。取消和失败都不会改用 Host 选择器重试。普通 Web 使用 Host 调用;独立的浏览组合始终列出 Host 目录。
31
+
32
+ ### 何时选择
33
+
34
+ 当浏览器与 Host 运行在同一台机器上、操作系统对话框可以在那里打开时,选择此表面。当浏览器为远程或进程内、没有本地选择器时,选择 [`-browse`](../ui-directory-picker-browse/README.zh.md) 表面。两个表面填充相同的 slot,因此切换只是组合改动,而非代码改动。
35
+
36
+ -----
37
+
38
+ <a id="understand-the-implementation"></a>
39
+ ## 理解实现
40
+
41
+ <details>
42
+ <summary>实现细节——点击展开</summary>
43
+
44
+ 两个 slot 注册经嵌套的 `ctx.slots.inject()` 调用作为一个事务性 effect 安装,因为任一声明条目都可能晚些激活或替换其声明。填充在每个上升沿 `open` 时只武装一次,因此重渲染永远不会再拉起一个选择器;结算结果挂在 ref 上,让答复到达持有方最新的处理器。HMR(热模块替换)导致填充被替换时,卸载会整体丢弃结算:wire 没有按请求中止的机制,因此 Host 侧选择器会一直存活到被答复,而它的答复无处落地。node 半部是一个空 `apply`,让插件留在 Host 名单上。
45
+
46
+ </details>
47
+
48
+ -----
49
+
50
+ <a id="further-exploration"></a>
51
+ ## 进一步探索
52
+
53
+ 当拾取面不够用时阅读以下页面。它们从浏览器半部进入 Host 后端与它所填充的 slot。
54
+
55
+ - [qilin-host-directory-picker-native](../../host/directory-picker-native/README.zh.md)——本表面驱动的操作系统选择器后端。
56
+ - [ui-workspace](../ui-workspace/README.zh.md)——声明目录流程 slot 并拥有拾取对话。
57
+ - [ui-directory-picker-browse](../ui-directory-picker-browse/README.zh.md)——面向远程与进程内部署的应用内浏览替代方案。
58
+ - [Web 客户端架构](../../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md)——浏览器插件行如何加载并注册 slot。
59
+
60
+ -----
61
+
62
+ <a id="model-experience"></a>
63
+ ## 模型体验
64
+
65
+ 无,因为目录选择器属于浏览器界面;本包中的任何内容都不会进入模型请求。
66
+
67
+ #### KV Cache 影响
68
+
69
+ 无;本包既不组装也不发送提供方请求。
70
+
71
+ ## 已知限制与延期工作
72
+
73
+ <a id="known-limitations-and-deferred-work"></a>
74
+
75
+
76
+ 这些限制界定了原生选择器的适用时机。它们是当前包约束,不是通用选择器对比或任务积压。
77
+
78
+ - **无法取消已打开的选择器**——wire 没有按请求中止的机制,因此已显示在本地的选择器无法从浏览器关闭;被丢弃的结算会被忽略。
79
+ - **仅限本地承载**——本地承载对话框选择本地路径;普通 Web 打开 Host 选择器。远程浏览器与进程内部署使用 `-browse` 组合。平台失败经由持有方的可重试文件夹对话框呈现。
80
+ - **Linux 自动选择**——缺少 zenity 或 kdialog 时,Host 即使在承载接口存在时也选择浏览模式,不使用承载对话框。
81
+
82
+ <a id="dev-note"></a>
83
+ ### 开发备注
84
+
85
+ <details>
86
+ <summary>维护者的工作上下文——点击展开</summary>
87
+
88
+ 无。
89
+
90
+ </details>
package/lib/client.js ADDED
@@ -0,0 +1,84 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@qilin-agent/client-ui-directory-picker-native",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region lib/types/client/flow.js
9
+ /**
10
+ * The native picking occupant (package-internal; the `./client` surface
11
+ * exposes only the Loader exports). Same-package tests exercise it directly
12
+ * through this module.
13
+ */
14
+ /**
15
+ * Renderless flow occupant: each rising `open` edge runs exactly one pick and
16
+ * reports exactly one outcome; the ref arms once per open so re-renders (and
17
+ * an adoption keeping `open` true while `busy`) never launch a second
18
+ * chooser. The owner withdrawing `open` re-arms the next request.
19
+ * @param props - owner conversation plus the injected pick call.
20
+ * @returns nothing — the native chooser renders on the host display.
21
+ */
22
+ function NativeDirectoryFlow(props) {
23
+ const { open, pick } = props;
24
+ const armed = (0, react.useRef)(false);
25
+ const outcome = (0, react.useRef)(props);
26
+ outcome.current = props;
27
+ const alive = (0, react.useRef)(true);
28
+ (0, react.useEffect)(() => {
29
+ alive.current = true;
30
+ return () => {
31
+ alive.current = false;
32
+ };
33
+ }, []);
34
+ (0, react.useEffect)(() => {
35
+ if (!open) {
36
+ armed.current = false;
37
+ return;
38
+ }
39
+ if (armed.current) return;
40
+ armed.current = true;
41
+ pick().then((path) => {
42
+ if (!alive.current) return;
43
+ if (path === null) outcome.current.onCancel();
44
+ else outcome.current.onPicked(path);
45
+ }, (reason) => {
46
+ if (!alive.current) return;
47
+ outcome.current.onError(reason instanceof Error ? reason.message : String(reason));
48
+ });
49
+ }, [open, pick]);
50
+ return null;
51
+ }
52
+ //#endregion
53
+ //#region lib/types/client/index.js
54
+ /** Required services (cordis fiber inject): the slot registry and workspace UI service. */
55
+ const inject = ["slots", "uiWorkspace"];
56
+ /**
57
+ * Client plugin body: register the renderless native flow into both
58
+ * directory-flow holes through `slots.inject()` because the ui-workspace
59
+ * entries may activate later or replace their declarations.
60
+ * @param ctx - client root context.
61
+ */
62
+ function apply(ctx) {
63
+ const desktop = globalThis.__QILIN_DIRECTORY_PICKER__;
64
+ const pick = desktop === void 0 ? () => ctx.uiWorkspace.pickDirectory() : () => desktop.pick();
65
+ const injected = () => ({ pick });
66
+ ctx.slots.inject("conversation.hero.workspace.directoryFlow", () => ctx.slots.inject("sidebar.workspaces.directoryFlow", function* () {
67
+ yield ctx.slots.register({
68
+ name: "conversation.hero.workspace.directoryFlow",
69
+ inject: injected
70
+ }, NativeDirectoryFlow);
71
+ yield ctx.slots.register({
72
+ name: "sidebar.workspaces.directoryFlow",
73
+ inject: injected
74
+ }, NativeDirectoryFlow);
75
+ }));
76
+ }
77
+ //#endregion
78
+ exports.apply = apply;
79
+ exports.inject = inject;
80
+ return module.exports;
81
+ }
82
+ });
83
+
84
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ //#region lib/types/index.js
2
+ /**
3
+ * Native directory-picker surface, node half. Pure UI plugin: the empty apply
4
+ * exists so the plugin appears in the host cordis.yml / Loader; the browser
5
+ * half ships via exports["./client"], discovered through the package.json
6
+ * qilin.client declaration. The OS chooser it drives lives in
7
+ * `@qilin-agent/host-directory-picker-native`.
8
+ */
9
+ /** Host plugin body — no host-side behavior for this surface plugin. */
10
+ function apply() {}
11
+ //#endregion
12
+ export { apply };
@@ -0,0 +1,17 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { DirectoryFlowOwnerProps } from '@qilin-agent/client-ui-workspace/client';
3
+ /** Injected face: the native chooser call the flow drives (bound in apply's closure). */
4
+ export interface NativeFlowInjected {
5
+ /** Open the local desktop or Host single-directory chooser. */
6
+ pick: () => Promise<string | null>;
7
+ }
8
+ /**
9
+ * Renderless flow occupant: each rising `open` edge runs exactly one pick and
10
+ * reports exactly one outcome; the ref arms once per open so re-renders (and
11
+ * an adoption keeping `open` true while `busy`) never launch a second
12
+ * chooser. The owner withdrawing `open` re-arms the next request.
13
+ * @param props - owner conversation plus the injected pick call.
14
+ * @returns nothing — the native chooser renders on the host display.
15
+ */
16
+ export declare function NativeDirectoryFlow(props: DirectoryFlowOwnerProps & NativeFlowInjected): ReactElement | null;
17
+ //# sourceMappingURL=flow.d.ts.map
@@ -0,0 +1,12 @@
1
+ /** Native directory flow using the local desktop bridge or the Host's OS chooser. */
2
+ import type { Context as ClientContext } from '@qilin-agent/kylin';
3
+ /** Required services (cordis fiber inject): the slot registry and workspace UI service. */
4
+ export declare const inject: string[];
5
+ /**
6
+ * Client plugin body: register the renderless native flow into both
7
+ * directory-flow holes through `slots.inject()` because the ui-workspace
8
+ * entries may activate later or replace their declarations.
9
+ * @param ctx - client root context.
10
+ */
11
+ export declare function apply(ctx: ClientContext): void;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Native directory-picker surface, node half. Pure UI plugin: the empty apply
3
+ * exists so the plugin appears in the host cordis.yml / Loader; the browser
4
+ * half ships via exports["./client"], discovered through the package.json
5
+ * qilin.client declaration. The OS chooser it drives lives in
6
+ * `@qilin-agent/host-directory-picker-native`.
7
+ */
8
+ /** Host plugin body — no host-side behavior for this surface plugin. */
9
+ export declare function apply(): void;
10
+ //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@qilin-agent/client-ui-directory-picker-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "Native directory-picker surface: the renderless workspace directory-flow occupant driving the local Desktop or Host OS chooser",
4
+ "version": "3.1.4",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/kkutysllb/QiLin.git",
11
+ "directory": "packages/client/ui-directory-picker-native"
12
+ },
13
+ "engines": {
14
+ "node": "^22.19.0 || >=24.0.0"
15
+ },
16
+ "type": "module",
17
+ "main": "lib/index.js",
18
+ "types": "lib/types/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./lib/types/index.d.ts",
22
+ "default": "./lib/index.js"
23
+ },
24
+ "./client": {
25
+ "types": "./lib/types/client/index.d.ts",
26
+ "default": "./lib/client.js"
27
+ },
28
+ "./src/*": "./src/*",
29
+ "./package.json": "./package.json"
30
+ },
31
+ "qilin": {
32
+ "client": {
33
+ "inject": [
34
+ "@qilin-agent/client-ui-renderer",
35
+ "@qilin-agent/client-ui-workspace"
36
+ ],
37
+ "platform": "web"
38
+ }
39
+ },
40
+ "license": "MIT",
41
+ "peerDependencies": {
42
+ "@qilin-agent/kylin": "~4.0.3"
43
+ },
44
+ "devDependencies": {
45
+ "@testing-library/react": "^16.1.0",
46
+ "@types/react": "~18.3.1",
47
+ "react": "^18.2.0",
48
+ "react-dom": "^18.2.0",
49
+ "@qilin-agent/client-ui-workspace": "3.1.4",
50
+ "@qilin-agent/kylin": "~4.0.3",
51
+ "@qilin-agent/client-ui-renderer": "3.1.4",
52
+ "@qilin-agent/client-test-runtime": "3.1.4"
53
+ },
54
+ "files": [
55
+ "lib/index.js",
56
+ "lib/client.js",
57
+ "lib/types/**/*.d.ts"
58
+ ],
59
+ "scripts": {
60
+ "bundle": "tsdown",
61
+ "watch": "tsdown --watch"
62
+ }
6
63
  }