@deepseek-ai/dsh-client-ui-open-in-app 0.1.7-rc.2 → 0.2.0-rc.2
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.i18n.yaml +6 -6
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +37 -23
- package/lib/types/client/OpenInAppAction.d.ts +8 -6
- package/lib/types/client/index.d.ts +2 -2
- package/package.json +20 -19
package/README.i18n.yaml
CHANGED
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
en: 056e2bd9e3c78dd8
|
|
19
19
|
zh: be35d2ba53be0442
|
|
20
20
|
/deepseek-ai-dsh-client-ui-open-in-app/use-this-package/what-to-expect:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: 42f7977b585f9c3a
|
|
22
|
+
zh: a13772312d52b97f
|
|
23
23
|
/deepseek-ai-dsh-client-ui-open-in-app/understand-the-implementation:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: a0304e1bd9b44370
|
|
25
|
+
zh: af0fdfd01c296dce
|
|
26
26
|
/deepseek-ai-dsh-client-ui-open-in-app/further-exploration:
|
|
27
27
|
en: 9deb509f65b6edfd
|
|
28
28
|
zh: d6cd63b2abc3731b
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
en: 3d77fca62d3c8e86
|
|
37
37
|
zh: 3125d32a158e690a
|
|
38
38
|
/deepseek-ai-dsh-client-ui-open-in-app/known-limitations-and-deferred-work/dev-note:
|
|
39
|
-
en:
|
|
40
|
-
zh:
|
|
39
|
+
en: c79e15f681adeacd
|
|
40
|
+
zh: 6a30399297bde201
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Mount this plugin in the Web composition beside [`dsh-host-open-in-app`](../../h
|
|
|
29
29
|
|
|
30
30
|
### What to expect
|
|
31
31
|
|
|
32
|
-
The Session header and document header use the same 24px-high split button with 9px corners.
|
|
32
|
+
The Session header, file-tree header, and document header use the same 24px-high split button with 9px corners. These headers show only the icon; their tooltip names the default application or the reveal action. They show the default action’s icon, mark its application with “(default)” in the menu, disable while their own gesture runs, and report failures through a transient toast. The file-tree opener targets the displayed directory supplied as `absolutePath` and shares the application choice and launch state with the Session header. The directory adapter uses the existing cross-platform application catalog and remembers the last successful choice in `dsh.open-in-app.choice`; a missing choice falls back to the first available application.
|
|
33
33
|
|
|
34
34
|
The **Open locally** shortcut captures the main Session's directory and the same remembered application as the header button. The effective binding appears in the button tooltip and `aria-keyshortcuts`; Web follows the [shortcut service’s platform defaults](../shortcuts/README.md). The command runs only while the Conversation is selected and the header button has a directory and installed application to open. An in-flight launch also blocks the command. Pointer and keyboard gestures share the controller's launch state, so repeated gestures cannot launch twice or change the remembered choice mid-launch.
|
|
35
35
|
|
|
@@ -45,7 +45,7 @@ Mounted controls with the same association reader and file share one query and i
|
|
|
45
45
|
<details>
|
|
46
46
|
<summary>Implementation internals — click to expand</summary>
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Both directory slots use `OpenInAppAction` with an explicit `absolutePath`. The `conversation.session.header.utilities` registration reads the Session cwd, while `sidebar.right.tab.files.actions` receives the displayed directory from its owner. The plugin registers the `open-in-app` dictionaries as one effect. A page-lifetime controller ([`src/client/controller.ts`](src/client/controller.ts)) owns the once-per-page availability read, the persisted choice snapshot store, and the launch POST; the component receives the shared sources through the inject `hooks` compartment, so every Session header shares one truth. Document-relative route forms and wire payload types come from the host package's browser-safe `@deepseek-ai/dsh-host-open-in-app/shared` subpath. The controller guards in-flight launches and publishes their captured directory and status; header controls derive delayed busy and transient error visuals from that source.
|
|
49
49
|
|
|
50
50
|
The directory and file adapters supply application metadata and operations to [`OpenTargetButton`](src/client/OpenTargetButton.tsx), which owns menu ordering, default markers, icons, sizing, and gesture feedback. The file header and empty state share `FileOpenTarget`, while `OpenPathInjected.applications` queries `session.workspacePathApplications` through [`open-path.ts`](src/client/open-path.ts). Opening uses `session.openWorkspacePath`; the Host revalidates an explicitly selected handler before launch. The directory adapter keeps the existing catalog routes. `FileRouteAction` supplies the same control to delivery cards and change review through `deliverables.file.actions` and `deliverables.review.file.actions`; their authenticated routes retain Session file authorization. A failed or unavailable file query therefore needs no platform-specific UI implementation.
|
|
51
51
|
|
|
@@ -94,4 +94,4 @@ The feature-level decisions, including the split into the host package and this
|
|
|
94
94
|
|
|
95
95
|
</details>
|
|
96
96
|
|
|
97
|
-
**Runtime invariant:** No companion is published. The plugin registers one dictionary effect and
|
|
97
|
+
**Runtime invariant:** No companion is published. The plugin registers one dictionary effect and six slot entries whose disposal the HMR-safety spec proves; application availability, the choice, and the desktop answer live in the controllers' snapshot stores with no second copy to diverge.
|
package/README.zh.md
CHANGED
|
@@ -29,7 +29,7 @@ kind: "package-reference"
|
|
|
29
29
|
|
|
30
30
|
### 预期行为
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
会话标题栏、文件树标题栏和文档标题栏共用同一个高 24px、圆角 9px 的分体按钮。这些标题栏都只显示图标,悬停提示显示默认应用名称或文件定位动作。它们都显示默认动作的图标,在菜单的默认应用后标注“(默认)”,仅在自己的操作执行期间禁用,并通过短暂提示报告失败。文件树打开按钮以 `absolutePath` 传入的显示目录为目标,并与会话头部共享应用选择和启动状态。目录适配器使用已有的跨平台应用列表,将最后一次成功选择保存在 `dsh.open-in-app.choice` 中;原选择不可用时回退到第一个可用应用。
|
|
33
33
|
|
|
34
34
|
**在本地打开**快捷键捕获主会话的目录,并使用与头部按钮相同的已记住应用。按钮 tooltip 和 `aria-keyshortcuts` 显示当前有效绑定;Web 遵循[快捷键服务的平台默认值](../shortcuts/README.zh.md)。只有选中会话界面、且头部按钮有可打开的目录和已安装应用时,命令才执行。启动尚未结束时也会阻止命令。鼠标与键盘操作共享 controller 的启动状态,重复操作不会启动两次,也不会在启动期间更改已记住的选择。
|
|
35
35
|
|
|
@@ -45,7 +45,7 @@ kind: "package-reference"
|
|
|
45
45
|
<details>
|
|
46
46
|
<summary>实现内幕——点击展开</summary>
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
两个目录插槽均通过显式的 `absolutePath` 使用 `OpenInAppAction`。`conversation.session.header.utilities` 的注册函数读取会话 cwd,`sidebar.right.tab.files.actions` 则接收所属组件传入的显示目录。插件以一个 effect 注册 `open-in-app` 词典。一个页面生命周期的 controller([`src/client/controller.ts`](src/client/controller.ts))拥有每页一次的可用性读取、持久化选择的 snapshot store 与启动 POST;组件经 inject 的 `hooks` 隔间接收共享源,因此所有会话头部共享同一份事实。文档相对的路由形式与 wire 载荷类型来自主机包的浏览器安全子路径 `@deepseek-ai/dsh-host-open-in-app/shared`。controller 守卫执行中的启动,并发布所捕获的目录与状态;头部控件从该源派生延迟出现的等待态和短暂错误态。
|
|
49
49
|
|
|
50
50
|
目录和文件适配器把应用信息与操作交给 [`OpenTargetButton`](src/client/OpenTargetButton.tsx),由它统一管理菜单顺序、默认标记、图标、尺寸和操作反馈。文件标题栏和空态共用 `FileOpenTarget`,`OpenPathInjected.applications` 通过 [`open-path.ts`](src/client/open-path.ts) 查询 `session.workspacePathApplications`。打开操作使用 `session.openWorkspacePath`,Host 在启动前重新验证指定的关联应用。`FileRouteAction` 通过 `deliverables.file.actions` 和 `deliverables.review.file.actions` 为交付卡片和变更对比页提供同一控件,其认证路由保留会话文件校验。目录适配器继续使用已有的应用列表路由,文件查询失败或不可用时无需增加平台专用的界面实现。
|
|
51
51
|
|
|
@@ -94,4 +94,4 @@ Host 在打开或定位前通过当前文件系统验证路径。没有对应 Ho
|
|
|
94
94
|
|
|
95
95
|
</details>
|
|
96
96
|
|
|
97
|
-
**运行时不变式:** 不发布伴生入口。插件注册一个词典 effect
|
|
97
|
+
**运行时不变式:** 不发布伴生入口。插件注册一个词典 effect 和六个 slot 条目,HMR 安全性 spec 证明它们都会在资源释放时撤销;应用可用性、选择与桌面回答存储在控制器的快照存储中,不存在可能与之分歧的第二份副本。
|
package/lib/client.js
CHANGED
|
@@ -4,9 +4,9 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
7
8
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
8
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
-
let react = require("react");
|
|
10
10
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
11
|
/** Browser-relative form of {@link OPEN_IN_APP_APPS_PATH}. */
|
|
12
12
|
const OPEN_IN_APP_APPS_ROUTE = "/open-in-app/apps".slice(1);
|
|
@@ -364,12 +364,11 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
//#region lib/types/client/OpenInAppAction.js
|
|
365
365
|
/**
|
|
366
366
|
* Adapt the installed directory catalog to the shared opening control.
|
|
367
|
-
* @param props -
|
|
368
|
-
* @returns the shared control, or null without an eligible application
|
|
367
|
+
* @param props - displayed directory, installed catalog, and launch operations.
|
|
368
|
+
* @returns the shared control, or null without an eligible application.
|
|
369
369
|
*/
|
|
370
370
|
function OpenInAppAction(props) {
|
|
371
|
-
const {
|
|
372
|
-
const cwd = useSessions((state) => state.byId[sessionId]?.cwd);
|
|
371
|
+
const { absolutePath, useOpenInAppApps, useOpenInAppChoice, t } = props;
|
|
373
372
|
const available = useOpenInAppApps((apps) => apps);
|
|
374
373
|
const choice = useOpenInAppChoice((id) => id);
|
|
375
374
|
const operation = props.useOpenInAppLaunch((value) => value);
|
|
@@ -383,7 +382,7 @@ window.__ModuleLoader__.load({
|
|
|
383
382
|
}];
|
|
384
383
|
});
|
|
385
384
|
const preferred = apps.find((app) => app.id === choice) ?? apps[0];
|
|
386
|
-
if (preferred === void 0
|
|
385
|
+
if (preferred === void 0) return null;
|
|
387
386
|
return (0, react_jsx_runtime.jsx)(OpenTargetButton, {
|
|
388
387
|
kind: "directory",
|
|
389
388
|
applications: apps,
|
|
@@ -395,14 +394,14 @@ window.__ModuleLoader__.load({
|
|
|
395
394
|
execute: async (operation) => {
|
|
396
395
|
const id = operation.kind === "application" ? operation.id : preferred.id;
|
|
397
396
|
try {
|
|
398
|
-
await props.launch(id,
|
|
397
|
+
await props.launch(id, absolutePath);
|
|
399
398
|
} catch (_error) {
|
|
400
399
|
return "openError";
|
|
401
400
|
}
|
|
402
401
|
if (operation.kind === "application") props.choose(id);
|
|
403
402
|
return null;
|
|
404
403
|
}
|
|
405
|
-
},
|
|
404
|
+
}, absolutePath);
|
|
406
405
|
}
|
|
407
406
|
//#endregion
|
|
408
407
|
//#region lib/types/client/open-path.js
|
|
@@ -753,8 +752,8 @@ window.__ModuleLoader__.load({
|
|
|
753
752
|
"layout"
|
|
754
753
|
];
|
|
755
754
|
/**
|
|
756
|
-
* Client plugin body: register
|
|
757
|
-
*
|
|
755
|
+
* Client plugin body: register dictionaries, workspace directory controls, and
|
|
756
|
+
* document preview path controls.
|
|
758
757
|
* @param ctx - client root context.
|
|
759
758
|
*/
|
|
760
759
|
function apply(ctx) {
|
|
@@ -823,24 +822,39 @@ window.__ModuleLoader__.load({
|
|
|
823
822
|
};
|
|
824
823
|
}
|
|
825
824
|
}), "open-in-app: workspace command");
|
|
825
|
+
const directoryInjected = () => ({
|
|
826
|
+
hooks: {
|
|
827
|
+
openInAppApps: controller.apps,
|
|
828
|
+
openInAppChoice: controller.choice,
|
|
829
|
+
openInAppLaunch: controller.operation,
|
|
830
|
+
shortcuts: ctx.shortcuts.catalog
|
|
831
|
+
},
|
|
832
|
+
launch: (appId, path) => controller.launch(appId, path),
|
|
833
|
+
choose: (appId) => {
|
|
834
|
+
controller.choose(appId);
|
|
835
|
+
},
|
|
836
|
+
iconUrl: (appId) => `${OPEN_IN_APP_ICON_PREFIX_ROUTE}/${appId}`
|
|
837
|
+
});
|
|
838
|
+
function SessionOpenInAppAction(props) {
|
|
839
|
+
const { sessionId, useSessions } = props;
|
|
840
|
+
const cwd = useSessions((state) => state.byId[sessionId]?.cwd);
|
|
841
|
+
return cwd ? (0, react.createElement)(OpenInAppAction, {
|
|
842
|
+
...props,
|
|
843
|
+
absolutePath: cwd
|
|
844
|
+
}) : null;
|
|
845
|
+
}
|
|
826
846
|
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
827
847
|
name: "conversation.session.header.utilities",
|
|
828
848
|
id: "open-in-app",
|
|
829
849
|
order: -10,
|
|
830
850
|
locale: NS,
|
|
831
|
-
inject:
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
launch: (appId, path) => controller.launch(appId, path),
|
|
839
|
-
choose: (appId) => {
|
|
840
|
-
controller.choose(appId);
|
|
841
|
-
},
|
|
842
|
-
iconUrl: (appId) => `${OPEN_IN_APP_ICON_PREFIX_ROUTE}/${appId}`
|
|
843
|
-
})
|
|
851
|
+
inject: directoryInjected
|
|
852
|
+
}, SessionOpenInAppAction));
|
|
853
|
+
ctx.slots.inject("sidebar.right.tab.files.actions", () => ctx.slots.register({
|
|
854
|
+
name: "sidebar.right.tab.files.actions",
|
|
855
|
+
id: "open-in-app",
|
|
856
|
+
locale: NS,
|
|
857
|
+
inject: directoryInjected
|
|
844
858
|
}, OpenInAppAction));
|
|
845
859
|
const applications = (path, signal) => paths.applications(path, signal);
|
|
846
860
|
const pathInjected = () => ({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
|
|
2
|
-
import type { InjectFace, PropsLocale
|
|
2
|
+
import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
3
|
import type { OpenInAppLaunchState } from './controller.ts';
|
|
4
4
|
import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
|
|
5
5
|
import { NS } from './locales.ts';
|
|
6
|
-
/** Browser operations and state
|
|
6
|
+
/** Browser operations and state shared by the Session header and file tree contributions. */
|
|
7
7
|
export interface OpenInAppActionInjected {
|
|
8
8
|
hooks: {
|
|
9
9
|
openInAppApps: ObservableSnapshot<readonly string[] | null>;
|
|
@@ -15,12 +15,14 @@ export interface OpenInAppActionInjected {
|
|
|
15
15
|
choose: (appId: string) => void;
|
|
16
16
|
iconUrl: (appId: string) => string;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
export type OpenInAppActionProps =
|
|
18
|
+
/** Directory path, installed applications, and launch operations for the split button. */
|
|
19
|
+
export type OpenInAppActionProps = PropsLocale<typeof NS> & InjectFace<OpenInAppActionInjected> & {
|
|
20
|
+
absolutePath: string;
|
|
21
|
+
};
|
|
20
22
|
/**
|
|
21
23
|
* Adapt the installed directory catalog to the shared opening control.
|
|
22
|
-
* @param props -
|
|
23
|
-
* @returns the shared control, or null without an eligible application
|
|
24
|
+
* @param props - displayed directory, installed catalog, and launch operations.
|
|
25
|
+
* @returns the shared control, or null without an eligible application.
|
|
24
26
|
*/
|
|
25
27
|
export declare function OpenInAppAction(props: OpenInAppActionProps): React.JSX.Element | null;
|
|
26
28
|
//# sourceMappingURL=OpenInAppAction.d.ts.map
|
|
@@ -18,8 +18,8 @@ export type { OpenPathEmptyActionProps } from './OpenPathEmptyAction.tsx';
|
|
|
18
18
|
/** Required services: sessions, layout selection, the slot registry, copy, Remote calls, and shortcuts. */
|
|
19
19
|
export declare const inject: string[];
|
|
20
20
|
/**
|
|
21
|
-
* Client plugin body: register
|
|
22
|
-
*
|
|
21
|
+
* Client plugin body: register dictionaries, workspace directory controls, and
|
|
22
|
+
* document preview path controls.
|
|
23
23
|
* @param ctx - client root context.
|
|
24
24
|
*/
|
|
25
25
|
export declare function apply(ctx: ClientContext): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-open-in-app",
|
|
3
3
|
"description": "Web \"Open In...\" controls: the Session-header split button opening the workspace directory in an installed application, and the document preview's default-application controls for one file",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -54,25 +54,26 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/react": "~18.3.1",
|
|
56
56
|
"react": "^18.2.0",
|
|
57
|
-
"@deepseek-ai/dsh-api-gateway": "0.1.7-rc.2",
|
|
58
57
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
59
|
-
"@deepseek-ai/dsh-api-
|
|
60
|
-
"@deepseek-ai/dsh-api-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-client-
|
|
63
|
-
"@deepseek-ai/dsh-client-
|
|
64
|
-
"@deepseek-ai/dsh-client-
|
|
65
|
-
"@deepseek-ai/dsh-client-ui-
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.
|
|
67
|
-
"@deepseek-ai/dsh-client-ui-
|
|
68
|
-
"@deepseek-ai/dsh-client-ui-
|
|
69
|
-
"@deepseek-ai/dsh-client-ui-sidebar-
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.
|
|
71
|
-
"@deepseek-ai/dsh-
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-client-
|
|
58
|
+
"@deepseek-ai/dsh-api-gateway": "0.2.0-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-sidebar-documentpreview": "0.2.0-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-sidebar-files": "0.2.0-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-native-command": "0.2.0-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-client-shortcuts": "0.2.0-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-host-open-in-app": "0.2.0-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-deliverables": "0.2.0-rc.2"
|
|
76
77
|
},
|
|
77
78
|
"scripts": {
|
|
78
79
|
"bundle": "tsdown",
|