dsh-canvas-workspace 0.2.4-experimental.3 → 0.2.4-experimental.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/README.md +9 -1
- package/dist/client.js +121 -22
- package/dist/client.js.map +4 -4
- package/package.json +18 -17
package/README.md
CHANGED
|
@@ -25,9 +25,15 @@
|
|
|
25
25
|
|
|
26
26
|
Canvas 只在显示层处理历史和布局,不修改已有 DSH 会话事件。图片通过宿主授权的资源加载接口读取,不从展示文字推测附件。普通发送由原生输入框负责;测试不发送真实模型请求。
|
|
27
27
|
|
|
28
|
+
## DSH 0.2.2 边界兼容
|
|
29
|
+
|
|
30
|
+
修复新版宿主新增主面板包装层后,画布左侧仍保留原生侧栏轨道而裁切内容的问题。侧栏展开或收起进入画布都使用完整主区域,退出后恢复原来的侧栏状态、宽度和未发送草稿;不通过取消全局 `overflow` 掩盖边界。
|
|
31
|
+
|
|
32
|
+
`node tests/frame-bounds.browser.mjs` 覆盖新旧主区域结构、不同侧栏/右栏宽度,并验证旧选择器会复现空白边界。`node tests/frame-bounds.host.mjs` 使用隔离的真实宿主,覆盖展开、收起、再展开进入与退出画布。可通过 `DSH_TEST_NODE`、`DSH_TEST_CLI`、`CHROMIUM_PATH` 指定测试运行时,不读取个人会话或调用模型。
|
|
33
|
+
|
|
28
34
|
## 开发与检查
|
|
29
35
|
|
|
30
|
-
要求 Node.js `^22.19.0 || >=24`。当前开发依赖固定为 DSH `0.2.
|
|
36
|
+
要求 Node.js `^22.19.0 || >=24`。当前开发依赖固定为 DSH `0.2.2-rc.1` 与 Cordis `4.1.0`;声明支持 `0.2.0-rc.2`、`0.2.1-rc.2`、`0.2.1-rc.3` 与 `0.2.2-rc.1`,其它版本需单独验证。源码修复版已适配 rc.1 新增的原生图片拖放接口,文件传输仍由宿主负责;旧宿主没有此服务时不阻塞画布加载。注意:该声明针对当前源码构建,不代表旧的 npm `0.2.4-experimental.2` 已包含这些适配。
|
|
31
37
|
|
|
32
38
|
```bash
|
|
33
39
|
npm ci
|
|
@@ -44,6 +50,8 @@ npm run build
|
|
|
44
50
|
|
|
45
51
|
桌面版的 `npm run test:host` 使用已安装的 macOS DSH、全新隔离 profile 和合成历史,覆盖原生画布页签与侧栏恢复。首次运行可用 `npm install --prefix .research/pnpm --ignore-scripts pnpm@11.28.5` 准备局部测试工具;不会改全局 pnpm 或个人 DSH 配置。
|
|
46
52
|
|
|
53
|
+
`npm run test:touch` 增加两个画布入口的持续单指拖动回归(每轮 30 个采样、连续 3 轮,并检查松手后无回跳),避免只发送一次 move 漏掉触摸事件可取消性变化。相机移动不再重复渲染未变化的历史 Markdown/IUI 内容;12 条历史回复、60 次移动的 DOM 回归从 720 次正文渲染降为仅初始 12 次。
|
|
54
|
+
|
|
47
55
|
`npm run test:touch` 使用 Chromium CDP 的真实多触点输入,覆盖横屏、竖屏、198×242 小屏及桌面视口,两个画布入口、双指中心缩放/平移、触点增减与替换、原生滚动/控件边界及取消恢复。浏览器已接管的滚动不会再转换为画布手势。本地自动化不等于 iOS Safari 真机验证;本次未做 iPhone、iPad 或 Apple Watch 真机测试。插件只调整页面内画布,不能修改或隐藏 Safari 的系统地址栏、标签栏与工具栏。
|
|
48
56
|
|
|
49
57
|
`npm run dev` 启动独立 MOCK 演示。浏览器测试使用本地 Vite 页面,不连接用户当前运行中的 DSH;宿主测试脚本另需隔离的官方 DSH 测试环境。`npm run build` 生成宿主插件 `dist/` 与本地演示 `site/`。仓库包含可直接由现有 DSH 插件链接读取的 `dist/`,`site/` 和测试截图、日志不提交。
|
package/dist/client.js
CHANGED
|
@@ -418,7 +418,7 @@ function resolveWorkspacePath(cwd2, path2) {
|
|
|
418
418
|
if (isAbsoluteWorkspacePath(path2)) return path2;
|
|
419
419
|
if (cwd2 === void 0 || cwd2 === "") return path2;
|
|
420
420
|
const separator = isWindowsStylePath(cwd2) && cwd2.includes("\\") ? "\\" : "/";
|
|
421
|
-
return `${cwd2.replace(/[/\\]+$/, "")}${separator}${path2.replace(/^[/\\]+/, "")}`;
|
|
421
|
+
return `${cwd2.replace(/[/\\]+$/, "")}${separator}${path2.replace(/^(?:\.?[/\\])+/, "")}`;
|
|
422
422
|
}
|
|
423
423
|
function fileMediaUrl(base, path2) {
|
|
424
424
|
if (!/^https?:/u.test(base) && !base.startsWith("dsh-app://app/") || !isAbsoluteWorkspacePath(path2) || /^[/\\]{2}/u.test(path2) || /[\u0000-\u001f\u007f]/u.test(path2)) return void 0;
|
|
@@ -468,6 +468,28 @@ async function resolveNativeImageResource(base, cwd2, request) {
|
|
|
468
468
|
return { ok: true, value: { src, alt } };
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
// src/host/native-file-drag.ts
|
|
472
|
+
function createNativeFileDrag(ctx) {
|
|
473
|
+
let actions;
|
|
474
|
+
ctx.inject(["fileDrag"], (scope) => {
|
|
475
|
+
const current2 = scope.fileDrag.actions;
|
|
476
|
+
actions = current2;
|
|
477
|
+
scope.effect(() => () => {
|
|
478
|
+
if (actions === current2) actions = void 0;
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
prepare(source, signal) {
|
|
483
|
+
if (!actions)
|
|
484
|
+
return Promise.reject(new Error("DSH file drag service unavailable"));
|
|
485
|
+
return actions.prepare(source, signal);
|
|
486
|
+
},
|
|
487
|
+
reportFailure() {
|
|
488
|
+
actions?.reportFailure();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
471
493
|
// src/client.tsx
|
|
472
494
|
var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
473
495
|
|
|
@@ -878,7 +900,8 @@ function useTouchViewport(stage, options) {
|
|
|
878
900
|
cancelRef.current = cancel;
|
|
879
901
|
const sample = (event) => {
|
|
880
902
|
if ((event.type === "touchstart" || event.type === "touchmove") && !event.cancelable) {
|
|
881
|
-
cancel();
|
|
903
|
+
if (gesture.current.active) cancel();
|
|
904
|
+
else eligible.clear();
|
|
882
905
|
return;
|
|
883
906
|
}
|
|
884
907
|
const rect = element3.getBoundingClientRect();
|
|
@@ -24429,7 +24452,7 @@ function ReplyBody({
|
|
|
24429
24452
|
] })
|
|
24430
24453
|
] });
|
|
24431
24454
|
}
|
|
24432
|
-
|
|
24455
|
+
var ReplyContent = (0, import_react12.memo)(function ReplyContent2(props) {
|
|
24433
24456
|
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
24434
24457
|
ReplyBoundary,
|
|
24435
24458
|
{
|
|
@@ -24438,7 +24461,7 @@ function ReplyContent(props) {
|
|
|
24438
24461
|
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ReplyBody, { ...props }, props.messageId)
|
|
24439
24462
|
}
|
|
24440
24463
|
);
|
|
24441
|
-
}
|
|
24464
|
+
});
|
|
24442
24465
|
|
|
24443
24466
|
// src/ui/UserMessageMedia.tsx
|
|
24444
24467
|
var import_react13 = require("react");
|
|
@@ -24885,6 +24908,25 @@ function ChatCanvas({
|
|
|
24885
24908
|
if (rail && typeof window.requestAnimationFrame === "function")
|
|
24886
24909
|
rail.scrollTo({ top: rail.scrollTop, behavior: "instant" });
|
|
24887
24910
|
};
|
|
24911
|
+
(0, import_react15.useLayoutEffect)(() => {
|
|
24912
|
+
const rail = userRail.current;
|
|
24913
|
+
if (!rail) return;
|
|
24914
|
+
const updateEdges = () => {
|
|
24915
|
+
rail.dataset.overflowStart = String(rail.scrollTop > 1);
|
|
24916
|
+
rail.dataset.overflowEnd = String(
|
|
24917
|
+
rail.scrollTop + rail.clientHeight < rail.scrollHeight - 1
|
|
24918
|
+
);
|
|
24919
|
+
};
|
|
24920
|
+
updateEdges();
|
|
24921
|
+
rail.addEventListener("scroll", updateEdges, { passive: true });
|
|
24922
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(updateEdges);
|
|
24923
|
+
observer?.observe(rail);
|
|
24924
|
+
for (const child of rail.children) observer?.observe(child);
|
|
24925
|
+
return () => {
|
|
24926
|
+
rail.removeEventListener("scroll", updateEdges);
|
|
24927
|
+
observer?.disconnect();
|
|
24928
|
+
};
|
|
24929
|
+
}, [pairScope, userMessageIds]);
|
|
24888
24930
|
(0, import_react15.useLayoutEffect)(() => {
|
|
24889
24931
|
const ids = pairs.userMessages.map((message) => message.id);
|
|
24890
24932
|
const previous3 = seenRailMessages.current;
|
|
@@ -26065,6 +26107,16 @@ function ChatCanvas({
|
|
|
26065
26107
|
iuiActions.current.set(key, pending);
|
|
26066
26108
|
return pending;
|
|
26067
26109
|
};
|
|
26110
|
+
const iuiActionRef = (0, import_react15.useRef)(onIuiAction);
|
|
26111
|
+
iuiActionRef.current = onIuiAction;
|
|
26112
|
+
const handleIuiAction = (0, import_react15.useCallback)(
|
|
26113
|
+
(action) => iuiActionRef.current(action),
|
|
26114
|
+
[]
|
|
26115
|
+
);
|
|
26116
|
+
const handleIuiResource = (0, import_react15.useMemo)(
|
|
26117
|
+
() => resolveImage ? (request) => resolveImage(request, state.sessionId) : void 0,
|
|
26118
|
+
[resolveImage, state.sessionId]
|
|
26119
|
+
);
|
|
26068
26120
|
const stop = async () => {
|
|
26069
26121
|
const active = running.current;
|
|
26070
26122
|
if (!active) return;
|
|
@@ -26469,8 +26521,8 @@ function ChatCanvas({
|
|
|
26469
26521
|
iuiEnabled,
|
|
26470
26522
|
renderMarkdown,
|
|
26471
26523
|
renderImagePreview,
|
|
26472
|
-
onIuiAction,
|
|
26473
|
-
onIuiResource:
|
|
26524
|
+
onIuiAction: handleIuiAction,
|
|
26525
|
+
onIuiResource: handleIuiResource
|
|
26474
26526
|
}
|
|
26475
26527
|
),
|
|
26476
26528
|
reply.status === "running" && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
@@ -30387,29 +30439,34 @@ var styles_default = `.canvas-app {
|
|
|
30387
30439
|
border-radius: 20px;
|
|
30388
30440
|
pointer-events: none;
|
|
30389
30441
|
}
|
|
30442
|
+
/* A soft reading scrim, not a grey rectangular pane over the canvas. */
|
|
30390
30443
|
.canvas-app .chat-user-history-glass {
|
|
30391
30444
|
position: absolute;
|
|
30392
|
-
/* Keep blur inside the rounded message-list background, without a top halo. */
|
|
30393
30445
|
inset: 0;
|
|
30394
30446
|
z-index: 0;
|
|
30395
30447
|
border-radius: inherit;
|
|
30396
30448
|
clip-path: inset(0 round 20px);
|
|
30397
|
-
background:
|
|
30398
|
-
|
|
30399
|
-
|
|
30449
|
+
background: linear-gradient(
|
|
30450
|
+
to right,
|
|
30451
|
+
transparent,
|
|
30452
|
+
color-mix(in srgb, var(--cw-bg) 42%, transparent) 28%,
|
|
30453
|
+
color-mix(in srgb, var(--cw-bg) 72%, transparent)
|
|
30454
|
+
);
|
|
30455
|
+
-webkit-backdrop-filter: blur(12px);
|
|
30456
|
+
backdrop-filter: blur(12px);
|
|
30400
30457
|
-webkit-mask-image:
|
|
30401
30458
|
linear-gradient(
|
|
30402
30459
|
to right,
|
|
30403
30460
|
transparent,
|
|
30404
|
-
#000
|
|
30405
|
-
#000 calc(100% -
|
|
30461
|
+
#000 28px,
|
|
30462
|
+
#000 calc(100% - 12px),
|
|
30406
30463
|
transparent
|
|
30407
30464
|
),
|
|
30408
30465
|
linear-gradient(
|
|
30409
30466
|
to bottom,
|
|
30410
30467
|
transparent,
|
|
30411
|
-
#000
|
|
30412
|
-
#000 calc(100% -
|
|
30468
|
+
#000 24px,
|
|
30469
|
+
#000 calc(100% - 24px),
|
|
30413
30470
|
transparent
|
|
30414
30471
|
);
|
|
30415
30472
|
-webkit-mask-composite: source-in;
|
|
@@ -30417,15 +30474,15 @@ var styles_default = `.canvas-app {
|
|
|
30417
30474
|
linear-gradient(
|
|
30418
30475
|
to right,
|
|
30419
30476
|
transparent,
|
|
30420
|
-
#000
|
|
30421
|
-
#000 calc(100% -
|
|
30477
|
+
#000 28px,
|
|
30478
|
+
#000 calc(100% - 12px),
|
|
30422
30479
|
transparent
|
|
30423
30480
|
),
|
|
30424
30481
|
linear-gradient(
|
|
30425
30482
|
to bottom,
|
|
30426
30483
|
transparent,
|
|
30427
|
-
#000
|
|
30428
|
-
#000 calc(100% -
|
|
30484
|
+
#000 24px,
|
|
30485
|
+
#000 calc(100% - 24px),
|
|
30429
30486
|
transparent
|
|
30430
30487
|
);
|
|
30431
30488
|
mask-composite: intersect;
|
|
@@ -30454,6 +30511,39 @@ var styles_default = `.canvas-app {
|
|
|
30454
30511
|
-webkit-user-select: none;
|
|
30455
30512
|
user-select: none;
|
|
30456
30513
|
}
|
|
30514
|
+
/* Overflow is a scroll affordance, not a hard cut through a blue bubble.
|
|
30515
|
+
Keep fades proportional on tiny watch/landscape rails. */
|
|
30516
|
+
.canvas-app .chat-user-history {
|
|
30517
|
+
--rail-fade-start: 0px;
|
|
30518
|
+
--rail-fade-end: 0px;
|
|
30519
|
+
-webkit-mask-image: linear-gradient(
|
|
30520
|
+
to bottom,
|
|
30521
|
+
transparent,
|
|
30522
|
+
#000 var(--rail-fade-start),
|
|
30523
|
+
#000 calc(100% - var(--rail-fade-end)),
|
|
30524
|
+
transparent
|
|
30525
|
+
);
|
|
30526
|
+
mask-image: linear-gradient(
|
|
30527
|
+
to bottom,
|
|
30528
|
+
transparent,
|
|
30529
|
+
#000 var(--rail-fade-start),
|
|
30530
|
+
#000 calc(100% - var(--rail-fade-end)),
|
|
30531
|
+
transparent
|
|
30532
|
+
);
|
|
30533
|
+
}
|
|
30534
|
+
.canvas-app .chat-user-history[data-overflow-start="true"] {
|
|
30535
|
+
--rail-fade-start: min(24px, 16%);
|
|
30536
|
+
}
|
|
30537
|
+
.canvas-app .chat-user-history[data-overflow-end="true"] {
|
|
30538
|
+
--rail-fade-end: min(24px, 16%);
|
|
30539
|
+
}
|
|
30540
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
30541
|
+
.canvas-app .chat-user-history-glass {
|
|
30542
|
+
background: var(--cw-surface);
|
|
30543
|
+
-webkit-backdrop-filter: none;
|
|
30544
|
+
backdrop-filter: none;
|
|
30545
|
+
}
|
|
30546
|
+
}
|
|
30457
30547
|
.canvas-app .chat-user-message-group {
|
|
30458
30548
|
/* Give percentage bubble limits a definite rail width, not shrink-to-fit. */
|
|
30459
30549
|
width: 100%;
|
|
@@ -31251,6 +31341,9 @@ var styles_default = `.canvas-app {
|
|
|
31251
31341
|
}
|
|
31252
31342
|
|
|
31253
31343
|
/* Match public slot/region markers only while our native View is mounted.
|
|
31344
|
+
DSH 0.2.2 wraps the main slot in MainPanel. Match the frame child containing
|
|
31345
|
+
the slot, not only a direct slot child, so the hidden sidebar leaves no
|
|
31346
|
+
phantom clipping column. Do not remove the real viewport clipping boundary.
|
|
31254
31347
|
Keep the right track and stored widths intact; exit restores the host layout. */
|
|
31255
31348
|
:has(> [data-shell-overlay]):has([data-canvas-conversation-view])
|
|
31256
31349
|
> :has(> [data-slot="sidebar"]),
|
|
@@ -31261,7 +31354,7 @@ var styles_default = `.canvas-app {
|
|
|
31261
31354
|
display: none;
|
|
31262
31355
|
}
|
|
31263
31356
|
:has(> [data-shell-overlay]):has([data-canvas-conversation-view])
|
|
31264
|
-
> :has(
|
|
31357
|
+
> :has([data-slot="main"]) {
|
|
31265
31358
|
grid-column: 1 / 3;
|
|
31266
31359
|
grid-row: 1;
|
|
31267
31360
|
border-left: none;
|
|
@@ -31516,14 +31609,17 @@ function NativeNavigationIcon() {
|
|
|
31516
31609
|
function NativeSendIcon() {
|
|
31517
31610
|
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(import_dsh_client_ui_primitives2.IconSendOutlineMedium, { size: 16 });
|
|
31518
31611
|
}
|
|
31519
|
-
function renderNativeMarkdown(text8, streaming
|
|
31612
|
+
function renderNativeMarkdown(text8, streaming, cwd2, fileDrag) {
|
|
31520
31613
|
const pathImages = typeof document === "undefined" ? void 0 : createNativePathImages(document.baseURI, cwd2);
|
|
31521
31614
|
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
31522
31615
|
import_dsh_client_ui_primitives2.MarkdownDelegateProvider,
|
|
31523
31616
|
{
|
|
31524
31617
|
fileImages: pathImages ? {
|
|
31525
31618
|
resolve: pathImages.resolve,
|
|
31526
|
-
labels: imageLabels
|
|
31619
|
+
labels: imageLabels,
|
|
31620
|
+
// Before 0.2.2 the renderer ignores this extra property. Current
|
|
31621
|
+
// hosts receive their own transfer service through the adapter.
|
|
31622
|
+
fileDrag
|
|
31527
31623
|
} : void 0,
|
|
31528
31624
|
children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
31529
31625
|
import_dsh_client_ui_primitives2.MarkdownText,
|
|
@@ -31680,7 +31776,8 @@ function CanvasChat(props) {
|
|
|
31680
31776
|
renderMarkdown: (text8, streaming, sessionId) => renderNativeMarkdown(
|
|
31681
31777
|
text8,
|
|
31682
31778
|
streaming,
|
|
31683
|
-
sessionId ? hostSessions.find((session) => session.id === sessionId)?.cwd : void 0
|
|
31779
|
+
sessionId ? hostSessions.find((session) => session.id === sessionId)?.cwd : void 0,
|
|
31780
|
+
props.fileDrag
|
|
31684
31781
|
),
|
|
31685
31782
|
renderImagePreview: renderNativeImagePreview,
|
|
31686
31783
|
loadUserImage: (attachment, sessionId) => props.loadUserImage(sessionId, attachment),
|
|
@@ -31728,12 +31825,14 @@ function CanvasConversationView(props) {
|
|
|
31728
31825
|
) });
|
|
31729
31826
|
}
|
|
31730
31827
|
function apply(ctx) {
|
|
31828
|
+
const fileDrag = createNativeFileDrag(ctx);
|
|
31731
31829
|
const adapter = createDshAdapter(ctx);
|
|
31732
31830
|
const demo = new URLSearchParams(location.search).get("canvasMock") === "1";
|
|
31733
31831
|
const selected = demo ? createMockAdapter() : adapter;
|
|
31734
31832
|
const returnToNative = () => ctx.layout.selectPanel(null);
|
|
31735
31833
|
const accountActions = createNativeAccountActions(returnToNative);
|
|
31736
31834
|
const canvasFace = () => ({
|
|
31835
|
+
fileDrag,
|
|
31737
31836
|
adapter: selected,
|
|
31738
31837
|
mode: selected.kind,
|
|
31739
31838
|
onExit: returnToNative,
|