dsh-zen-remote 1.1.7 → 1.1.9
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/AGENTS.md +20 -0
- package/README.md +2 -2
- package/docs/interface.md +1 -2
- package/lib/client.js +382 -31
- package/lib/types/client/MobileHome.d.ts +3 -1
- package/lib/types/client/MobileHome.d.ts.map +1 -1
- package/lib/types/client/effects/workbench-ref-close.d.ts +29 -0
- package/lib/types/client/effects/workbench-ref-close.d.ts.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/styles/compat.css.d.ts +1 -1
- package/lib/types/client/styles/compat.css.d.ts.map +1 -1
- package/lib/types/client/styles/composer.css.d.ts +1 -1
- package/lib/types/client/styles/composer.css.d.ts.map +1 -1
- package/lib/types/client/styles/home.css.d.ts +1 -1
- package/lib/types/client/styles/home.css.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/MobileHome.tsx +183 -23
- package/src/client/effects/workbench-ref-close.ts +69 -0
- package/src/client/index.tsx +7 -0
- package/src/client/styles/compat.css.ts +53 -0
- package/src/client/styles/composer.css.ts +63 -28
- package/src/client/styles/home.css.ts +64 -0
package/AGENTS.md
CHANGED
|
@@ -57,3 +57,23 @@ pnpm test # 网关 node:test 用例 + 三个界面自检脚本 + 文档版
|
|
|
57
57
|
- **`lib/` 是产物,不手改**:改 `src/` → `pnpm build` → 提交 `lib/`。
|
|
58
58
|
- **桌面必须 no-op**:≥1024px 逐像素与未安装时一致。
|
|
59
59
|
是从真机事故里攒出来的,改相关代码前先读。
|
|
60
|
+
- **要藏宿主刚渲染出来的东西,别用 `requestAnimationFrame`**:React 在该帧的
|
|
61
|
+
rAF 阶段之后才提交 DOM,所以从 mutation 里排的 rAF 落在**下一帧**——中间那
|
|
62
|
+
一帧已经画到屏幕上,用户就看见闪。两个月内三次「晚一帧」的 bug(2026-08-18
|
|
63
|
+
回复要等回合结束才出现、08-22 工具调用行先显后折、08-25 issue #3 流式抖动)
|
|
64
|
+
都是这个根子。两条出路:能用选择器表达的交给样式表(`turn-fold.css.ts` 的
|
|
65
|
+
`BORN_FOLDED`,读宿主自己的 `data-chat-flow-kind` / `data-variant`,React
|
|
66
|
+
插入节点的同一次提交里就带着,第一帧就是隐藏的);表达不了的放进
|
|
67
|
+
MutationObserver 回调里同步做(`turn-fold.ts` 的 `markWholeRows`)——回调是
|
|
68
|
+
微任务,跑在这一帧渲染更新之前,两个方向都不会晚。rAF 只留给能容忍晚一帧的
|
|
69
|
+
活(全量重扫、插 chip、改文案)。
|
|
70
|
+
- **别用 `:has()` 写死宿主的 DOM 层数**:包裹层数不等于组件源码看上去的层数
|
|
71
|
+
——槽位会额外套一层(真实是 `flowItem > seat > AssistantMarkdown root >
|
|
72
|
+
body > 块`,比组件自身结构多一层)。深度猜错的选择器**静默失效**:测试全绿、
|
|
73
|
+
控制台干净,只有拿真界面量才看得出来。需要结构判断就用 JS 遍历
|
|
74
|
+
(`foldsWholeRow` 那种沿父节点上溯的写法),不假设层数。
|
|
75
|
+
- **改了界面就去真界面上量**:`turn-fold` 这类规则用自建的静态页面验证会给出
|
|
76
|
+
假阳性(合成 DOM 猜不对宿主的包裹层)。打开 `127.0.0.1:3080` 读现有会话的
|
|
77
|
+
`getComputedStyle` 即可,**不要为了验证去建会话或发消息**(耗真实 token,见
|
|
78
|
+
工作区 AGENTS.md 的硬约束)。注意浏览器标签在后台时 rAF 不触发,chip 会是 0
|
|
79
|
+
个——那是观测假象,不是回归,截图把窗口唤到前台再看。
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
<p align="center">
|
|
5
5
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-0B7285?style=flat-square" alt="MIT"></a>
|
|
6
|
-
<img src="https://img.shields.io/badge/release-v1.1.
|
|
6
|
+
<img src="https://img.shields.io/badge/release-v1.1.9-5B4CF0?style=flat-square" alt="v1.1.9">
|
|
7
7
|
<img src="https://img.shields.io/badge/DSH-Web%20Profile-5B4CF0?style=flat-square" alt="DSH Web Profile">
|
|
8
8
|
</p>
|
|
9
9
|
|
|
@@ -39,7 +39,7 @@ dsh plugin add dsh-zen-remote
|
|
|
39
39
|
```jsonc
|
|
40
40
|
{
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"dsh-zen-remote": "^1.1.
|
|
42
|
+
"dsh-zen-remote": "^1.1.9" // 本地开发换成 "link:/path/to/dsh-zen-remote"
|
|
43
43
|
},
|
|
44
44
|
"dsh": { "profile": { "bundles": [
|
|
45
45
|
"@deepseek-ai/dsh-base",
|
package/docs/interface.md
CHANGED
|
@@ -239,13 +239,12 @@ composer 最左的回形针打开的是**手机本地**的文件选择器(iOS
|
|
|
239
239
|
|
|
240
240
|
## 兼容插件
|
|
241
241
|
|
|
242
|
-
- [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)(
|
|
242
|
+
- [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)(移动端全宽抽屉,本插件在会话页头部给它留入口位;手机上文件树里点 @ 引用后代点开关自动收起面板——面板全屏盖住会话页,草稿变化看不见,收起本身就是点击反馈)——本机实测 **0.15.0**
|
|
243
243
|
- [@nanmicoder/dsh-agent-teams](https://github.com/NanmiCoder/dsh-agent-teams)(AgentTeams 活动浮层:手机端挪到会话头部下方、避开安全区,会话列表页隐藏;子代理会话头部保留可点的父会话面包屑用于切回)——本机实测 **0.1.9**
|
|
244
244
|
- [@ychris12138/dsh-usage-stats](https://github.com/Ychris12138/dsh-usage-stats)(用量与余额,主屏 chips 行可直接打开;旧包名 `dsh-usage-stats` 已废弃,两个名字同时留在 profile 里会因重复的 `usage-stats` 行让 DSH 起不来)——本机实测 **0.2.9**
|
|
245
245
|
- [@opendsh/dsh-plugin-scheduled-tasks](https://github.com/Ceelog/dsh-plugins)(定时任务,主屏 chips 行自动收割入口)——本机实测 **0.2.3**
|
|
246
246
|
- dsh-at-file(@文件引用;它和本插件的附件 chip 读的是同一份草稿 token,手机端会为一个上传文件同时画出缩略图和文件名两条,故隐藏它 `.dsh-uploads/` 下那几行,其余 @ 引用不动;桌面端不隐藏——那边本插件的 chip 是关掉的,它是唯一的呈现)——本机实测 **0.6.7**
|
|
247
247
|
- [@ace-zone/dsh-market](https://www.npmjs.com/package/@ace-zone/dsh-market)(插件市场;它的弹窗顶栏在手机上放不下,关闭的 × 被挤出面板外,故隐藏标语/版本号/官网链接三个装饰位并让标题省略号收缩,语言切换和 × 保留;认插件自己渲染的 `.dshm-ver[title="@ace-zone/dsh-market"]` 版本片,同名的其他"市场"插件不会被误伤)——本机实测 **0.1.66**
|
|
248
|
-
- [dsh-vision-toolkit](https://www.npmjs.com/package/@anionex/dsh-vision-toolkit)(图像 Q&A/OCR,配合手机端附件上传使用)
|
|
249
248
|
- [dsh-web-ui 全家桶](https://www.npmjs.com/package/@linxin666/dsh-web-ui-all)(文件树 / 预览 / 任务看板 / SSH / 宠物 / 会话统计 / 远程配对 / 设置)——沿用上游兼容规则,本次未扩展
|
|
250
249
|
|
|
251
250
|
## 安装
|
package/lib/client.js
CHANGED
|
@@ -885,6 +885,99 @@ const session_dot_ts_1 = require("./session-dot.js");
|
|
|
885
885
|
const MobileHomeChips_tsx_1 = require("./MobileHomeChips.js");
|
|
886
886
|
/** Phone breakpoint: below the tablet range, where the app-shell layout applies. */
|
|
887
887
|
const PHONE_QUERY = '(max-width: 767px)';
|
|
888
|
+
/** Swipe geometry: reveal width of the archive action / settle threshold. */
|
|
889
|
+
const SWIPE_REVEAL = 88;
|
|
890
|
+
const SWIPE_SETTLE = 44;
|
|
891
|
+
/**
|
|
892
|
+
* Direction-lock slop: total displacement before the gesture commits to
|
|
893
|
+
* horizontal (card drag) or vertical (native scroll) — decided ONCE per
|
|
894
|
+
* touch, the way native swipe rows do it. Before the lock nothing moves.
|
|
895
|
+
*/
|
|
896
|
+
const SWIPE_SLOP = 10;
|
|
897
|
+
/** Horizontal wins only when clearly flatter than ~35° (|dx| > 1.4·|dy|). */
|
|
898
|
+
const SWIPE_SLOPE = 1.4;
|
|
899
|
+
/**
|
|
900
|
+
* One session row with swipe-left-to-archive (user request, 2026-08-25;
|
|
901
|
+
* interaction redone after real-device feedback the same day).
|
|
902
|
+
*
|
|
903
|
+
* Native-pattern mechanics:
|
|
904
|
+
* - **One-shot direction lock.** The first ~10px of displacement decides the
|
|
905
|
+
* whole gesture: clearly-flat movement (|dx| > 1.4·|dy|) locks to the card
|
|
906
|
+
* drag, anything else locks to native vertical scroll and the card never
|
|
907
|
+
* moves again for that touch. The first version re-judged every move
|
|
908
|
+
* sample, so a curvy upward scroll could flip mid-gesture into a drag.
|
|
909
|
+
* - **Progressive reveal, iOS-mail style.** The wrapper clips (overflow:
|
|
910
|
+
* hidden) and the archive button is PARKED past the right edge, sliding in
|
|
911
|
+
* lock-step with the card (button shift = reveal + card shift). Nothing is
|
|
912
|
+
* pre-painted under the card, so a 1px drag shows a 1px slice of button
|
|
913
|
+
* edge — not a fully drawn button popping through a gap.
|
|
914
|
+
* - At most one row open at a time (parent owns `open`); tapping an open or
|
|
915
|
+
* mid-drag card closes it instead of navigating.
|
|
916
|
+
*
|
|
917
|
+
* Touch-only by design — the list only exists on the phone breakpoint.
|
|
918
|
+
*/
|
|
919
|
+
function SwipeRow({ open, onOpenChange, onArchive, archiveLabel, children }) {
|
|
920
|
+
const start = (0, react_1.useRef)(null);
|
|
921
|
+
const lock = (0, react_1.useRef)(null);
|
|
922
|
+
const [drag, setDrag] = (0, react_1.useState)(null);
|
|
923
|
+
// Ref mirror of `drag`: touchend must read the LATEST position even when
|
|
924
|
+
// the browser delivers move+end in one task (coalesced touch events) —
|
|
925
|
+
// the state value in this render's closure can be a frame stale.
|
|
926
|
+
const dragRef = (0, react_1.useRef)(null);
|
|
927
|
+
const moveTo = (value) => {
|
|
928
|
+
dragRef.current = value;
|
|
929
|
+
setDrag(value);
|
|
930
|
+
};
|
|
931
|
+
const base = open ? -SWIPE_REVEAL : 0;
|
|
932
|
+
const shift = drag ?? base;
|
|
933
|
+
const dragging = drag !== null;
|
|
934
|
+
const onTouchStart = (e) => {
|
|
935
|
+
const t = e.touches[0];
|
|
936
|
+
if (t === undefined)
|
|
937
|
+
return;
|
|
938
|
+
start.current = { x: t.clientX, y: t.clientY };
|
|
939
|
+
lock.current = null;
|
|
940
|
+
};
|
|
941
|
+
const onTouchMove = (e) => {
|
|
942
|
+
const s = start.current;
|
|
943
|
+
const t = e.touches[0];
|
|
944
|
+
if (s === null || t === undefined)
|
|
945
|
+
return;
|
|
946
|
+
const dx = t.clientX - s.x;
|
|
947
|
+
const dy = t.clientY - s.y;
|
|
948
|
+
if (lock.current === null) {
|
|
949
|
+
if (Math.hypot(dx, dy) < SWIPE_SLOP)
|
|
950
|
+
return;
|
|
951
|
+
lock.current = Math.abs(dx) > SWIPE_SLOPE * Math.abs(dy) ? 'h' : 'v';
|
|
952
|
+
if (lock.current === 'v')
|
|
953
|
+
return;
|
|
954
|
+
// Re-anchor so the card starts moving from here, not with a 10px jump.
|
|
955
|
+
start.current = { x: t.clientX, y: t.clientY };
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (lock.current === 'v')
|
|
959
|
+
return;
|
|
960
|
+
moveTo(Math.max(-SWIPE_REVEAL, Math.min(0, base + dx)));
|
|
961
|
+
};
|
|
962
|
+
const onTouchEnd = () => {
|
|
963
|
+
start.current = null;
|
|
964
|
+
if (lock.current !== 'h')
|
|
965
|
+
return;
|
|
966
|
+
const settled = (dragRef.current ?? base) < -SWIPE_SETTLE;
|
|
967
|
+
moveTo(null);
|
|
968
|
+
onOpenChange(settled);
|
|
969
|
+
};
|
|
970
|
+
const guardClick = (navigate) => {
|
|
971
|
+
// A tap on an open (or mid-drag) card closes it; navigation needs a
|
|
972
|
+
// second, clean tap. Matches every list app's swipe convention.
|
|
973
|
+
if (open || lock.current === 'h')
|
|
974
|
+
onOpenChange(false);
|
|
975
|
+
else
|
|
976
|
+
navigate();
|
|
977
|
+
};
|
|
978
|
+
const motion = dragging ? { transition: 'none' } : {};
|
|
979
|
+
return ((0, jsx_runtime_1.jsxs)("li", { "data-mobile-nav": "home-swipe", onTouchStart: onTouchStart, onTouchMove: onTouchMove, onTouchEnd: onTouchEnd, onTouchCancel: onTouchEnd, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "data-mobile-nav": "home-archive", tabIndex: open ? 0 : -1, "aria-hidden": !open, style: { transform: `translateX(${SWIPE_REVEAL + shift}px)`, ...motion }, onClick: onArchive, children: archiveLabel }), (0, jsx_runtime_1.jsx)("div", { "data-mobile-nav": "home-swipe-card", style: { transform: `translateX(${shift}px)`, ...motion }, children: children(guardClick) })] }));
|
|
980
|
+
}
|
|
888
981
|
/** Live matchMedia hook for the phone breakpoint. */
|
|
889
982
|
function usePhone() {
|
|
890
983
|
const [phone, setPhone] = (0, react_1.useState)(() => window.matchMedia(PHONE_QUERY).matches);
|
|
@@ -959,7 +1052,7 @@ function statusLine(row, dot, t) {
|
|
|
959
1052
|
* all navigation from the injected official actions; nothing here reads the
|
|
960
1053
|
* official DOM.
|
|
961
1054
|
*/
|
|
962
|
-
function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession, startSession, downloadSessionLog, t, }) {
|
|
1055
|
+
function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession, startSession, downloadSessionLog, archiveSession, t, }) {
|
|
963
1056
|
const phone = usePhone();
|
|
964
1057
|
const iconHref = useSiteIconHref();
|
|
965
1058
|
const [iconBroken, setIconBroken] = (0, react_1.useState)(false);
|
|
@@ -970,6 +1063,19 @@ function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession
|
|
|
970
1063
|
const sessions = useSessions((s) => s);
|
|
971
1064
|
const workspaces = useWorkspaces((s) => s);
|
|
972
1065
|
const [sheet, setSheet] = (0, react_1.useState)(null);
|
|
1066
|
+
/** The one row whose archive action is swiped open (null = none). */
|
|
1067
|
+
const [swipeOpen, setSwipeOpen] = (0, react_1.useState)(null);
|
|
1068
|
+
/** Archive with the same confirm gate the session-info card uses. */
|
|
1069
|
+
const onArchiveRow = (id) => {
|
|
1070
|
+
if (!window.confirm(t('infoArchiveConfirm'))) {
|
|
1071
|
+
setSwipeOpen(null);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
setSwipeOpen(null);
|
|
1075
|
+
// The store removes the row reactively (archivedSessionIds frame echo);
|
|
1076
|
+
// failures leave the row in place, which is the honest outcome.
|
|
1077
|
+
void archiveSession(id);
|
|
1078
|
+
};
|
|
973
1079
|
// Workspace of the current session — the untouched filter default.
|
|
974
1080
|
const currentWorkspaceId = (0, react_1.useMemo)(() => {
|
|
975
1081
|
const current = sessions.current;
|
|
@@ -994,8 +1100,23 @@ function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession
|
|
|
994
1100
|
.filter((row) => !row.blank && row.parentId === undefined && row.origin !== 'subagent')
|
|
995
1101
|
.filter((row) => !archived.has(row.id))
|
|
996
1102
|
.filter((row) => scope === null || scope.has(row.id))
|
|
1103
|
+
// A session that never completed a single turn is not established yet —
|
|
1104
|
+
// creating one from the FAB and backing out leaves these behind, and
|
|
1105
|
+
// they pile up as basename-titled rows (user report, 2026-08-25). The
|
|
1106
|
+
// host's sessionStats projection rides every list row; `turns` counts
|
|
1107
|
+
// turns with at least one CLOSED step, and cancelled steps count too,
|
|
1108
|
+
// so an interrupted first reply still shows. Keep when: it is the
|
|
1109
|
+
// current session (highlight row), it is running (first reply in
|
|
1110
|
+
// flight), it earned a title, or the projection is absent (no signal —
|
|
1111
|
+
// show rather than silently hide).
|
|
1112
|
+
.filter((row) => {
|
|
1113
|
+
if (row.id === sessions.current || row.running || row.title !== undefined)
|
|
1114
|
+
return true;
|
|
1115
|
+
const stats = row.projectionValues?.sessionStats;
|
|
1116
|
+
return stats?.turns === undefined || stats.turns > 0;
|
|
1117
|
+
})
|
|
997
1118
|
.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
998
|
-
}, [sessions.ids, sessions.byId, workspaces.archivedSessionIds, selectedWorkspace]);
|
|
1119
|
+
}, [sessions.ids, sessions.byId, sessions.current, workspaces.archivedSessionIds, selectedWorkspace]);
|
|
999
1120
|
// The session header's back button (session scope) cannot hold this
|
|
1000
1121
|
// store directly — a handle mounts under exactly one scope, and this one
|
|
1001
1122
|
// is already root-scoped here (see nav-store.ts) — so it dispatches
|
|
@@ -1046,7 +1167,7 @@ function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession
|
|
|
1046
1167
|
const dot = (0, session_dot_ts_1.dotState)(row);
|
|
1047
1168
|
const status = statusLine(row, dot, t);
|
|
1048
1169
|
const initial = row.displayTitle.trim().charAt(0).toUpperCase();
|
|
1049
|
-
return ((0, jsx_runtime_1.jsx)(
|
|
1170
|
+
return ((0, jsx_runtime_1.jsx)(SwipeRow, { open: swipeOpen === row.id, onOpenChange: (open) => setSwipeOpen(open ? row.id : null), onArchive: () => onArchiveRow(row.id), archiveLabel: t('infoArchive'), children: (guardClick) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", "data-mobile-nav": "home-row", "data-current": row.id === sessions.current ? '' : undefined, onClick: () => guardClick(() => enter(() => openSession(row.id))), children: [(0, jsx_runtime_1.jsx)("span", { "data-mobile-nav": "home-row-avatar", "aria-hidden": "true", children: dot !== undefined ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.StateDot, { state: dot, size: 10 }) : initial }), (0, jsx_runtime_1.jsxs)("span", { "data-mobile-nav": "home-row-body", children: [(0, jsx_runtime_1.jsx)("span", { "data-mobile-nav": "home-row-title", children: row.displayTitle }), status !== undefined && ((0, jsx_runtime_1.jsx)("span", { "data-mobile-nav": "home-row-status", children: status }))] }), (0, jsx_runtime_1.jsx)("time", { "data-mobile-nav": "home-row-time", dateTime: new Date(row.updatedAt).toISOString(), children: relativeTime(row.updatedAt) })] })) }, row.id));
|
|
1050
1171
|
}), rows.length === 0 && (0, jsx_runtime_1.jsx)("li", { "data-mobile-nav": "home-empty", children: t('noSessions') })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", "data-mobile-nav": "home-fab", "aria-label": t('newSession'), title: t('newSession'), onClick: () => enter(() => startSession(selectedWorkspace?.workspaceId)), children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.IconPlusOutline16, { size: 18 }), (0, jsx_runtime_1.jsx)("span", { children: t('newSession') })] }), sheet !== null && ((0, jsx_runtime_1.jsxs)("div", { "data-mobile-nav": "home-sheet-layer", children: [(0, jsx_runtime_1.jsx)("div", { "data-mobile-nav": "home-sheet-mask", role: "button", tabIndex: -1, "aria-label": t('close'), onClick: () => setSheet(null) }), (0, jsx_runtime_1.jsxs)("div", { "data-mobile-nav": "home-sheet", role: sheet === 'filter' ? 'menu' : undefined, children: [sheet === 'filter' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "data-mobile-nav": "home-sheet-title", children: t('switchWorkspace') }), (0, jsx_runtime_1.jsx)("button", { type: "button", role: "menuitem", "data-mobile-nav": "home-sheet-item", "data-selected": selected === 'all' ? '' : undefined, onClick: () => {
|
|
1051
1172
|
actions.filter('all');
|
|
1052
1173
|
setSheet(null);
|
|
@@ -3293,6 +3414,59 @@ exports.COMPAT_CSS = ` /* ---------- dsh-web-ui family compatibility ----------
|
|
|
3293
3414
|
padding: 6px 12px !important;
|
|
3294
3415
|
}
|
|
3295
3416
|
}
|
|
3417
|
+
|
|
3418
|
+
/* ---------- dsh-auto-approve compatibility ----------
|
|
3419
|
+
The composer.css.ts phone rules collapse the permission trigger to an
|
|
3420
|
+
icon-only pill, but the official permissionGlyphs table only draws
|
|
3421
|
+
icons for read-only / workspace-write — the auto-approve plugin's
|
|
3422
|
+
"auto" preset gets none, so its pill showed a bare chevron. The plugin
|
|
3423
|
+
injects its shield+sparkle svg into the MENU items itself (DOM,
|
|
3424
|
+
content-anchored), but the collapsed TRIGGER re-renders on every
|
|
3425
|
+
preset change, so a CSS pseudo-element is the survives-re-render fix
|
|
3426
|
+
(same reasoning as the old model Sparkle ::before above).
|
|
3427
|
+
|
|
3428
|
+
Presence gates, honestly stated (2026-08-25 review):
|
|
3429
|
+
- Trigger rule: aria-label is t('input.accessMode', {name}) with the
|
|
3430
|
+
raw preset display name embedded untranslated and (zh/en verified)
|
|
3431
|
+
at the END of the string — [aria-label$="Auto"] matches exactly the
|
|
3432
|
+
auto-approve preset being selected, and cannot match a future
|
|
3433
|
+
"Autopilot"-style preset. Plugin absent → the name never renders →
|
|
3434
|
+
inert. This is a DIRECT gate on the plugin's own preset name.
|
|
3435
|
+
- Menu-item rule: INDIRECT gate only. It matches "permission-menu item
|
|
3436
|
+
without an official icon span"; the official three presets all ship
|
|
3437
|
+
icon spans in the menu (verified live), so with the plugin absent
|
|
3438
|
+
nothing matches. Known boundary: another plugin's icon-less preset,
|
|
3439
|
+
or a host redesign dropping icon spans, would wrongly receive this
|
|
3440
|
+
shield. A direct gate is not possible: menu items carry no
|
|
3441
|
+
id/data attributes to anchor on, CSS cannot match text, and gating
|
|
3442
|
+
on the plugin's own JS-injected markers would kill this fallback in
|
|
3443
|
+
exactly the JS-did-not-run case it exists for. Paths are the plugin's own
|
|
3444
|
+
shield outline + four-point sparkle (permission-glyph.ts), stroke
|
|
3445
|
+
shield + filled sparkle, drawn as a currentColor mask. */
|
|
3446
|
+
@media (max-width: 767px) {
|
|
3447
|
+
/* Fallback for the MENU items too (real-phone report 2026-08-25: the
|
|
3448
|
+
plugin's DOM injection did not run in that page instance while this
|
|
3449
|
+
CSS did — bundle load timing differs per plugin). Any permission-menu
|
|
3450
|
+
item WITHOUT the official icon span gets the shield drawn by CSS;
|
|
3451
|
+
today that is exactly the plugin "auto" preset (the official three
|
|
3452
|
+
all ship icons in the menu). Self-coordinating with the plugin's own
|
|
3453
|
+
injection: once its svg lands it arrives inside a cloned icon span,
|
|
3454
|
+
the :not() stops matching, no double icon. */
|
|
3455
|
+
[data-slot="conversation.composer.bar"] [class$="_card"] > [class$="_row"] > [class$="_tools"] > [class$="_modes"] [role="menu"] button[role="menuitem"]:not(:has([class*="_itemIcon_"]))::before,
|
|
3456
|
+
[data-slot="conversation.composer.bar"] [class$="_card"] > [class$="_row"] > [class$="_tools"] > [class$="_modes"] button[class$="_trigger"][aria-label$="Auto"]::before {
|
|
3457
|
+
content: '';
|
|
3458
|
+
width: 16px;
|
|
3459
|
+
height: 16px;
|
|
3460
|
+
flex: none;
|
|
3461
|
+
background: currentColor;
|
|
3462
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z' fill='none' stroke='black' stroke-width='1.31831' stroke-linejoin='round'/%3E%3Cpath d='M8.35 5.4C8.35 7.2 7.15 8.4 5.35 8.4C7.15 8.4 8.35 9.6 8.35 11.4C8.35 9.6 9.55 8.4 11.35 8.4C9.55 8.4 8.35 7.2 8.35 5.4Z'/%3E%3C/svg%3E");
|
|
3463
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z' fill='none' stroke='black' stroke-width='1.31831' stroke-linejoin='round'/%3E%3Cpath d='M8.35 5.4C8.35 7.2 7.15 8.4 5.35 8.4C7.15 8.4 8.35 9.6 8.35 11.4C8.35 9.6 9.55 8.4 11.35 8.4C9.55 8.4 8.35 7.2 8.35 5.4Z'/%3E%3C/svg%3E");
|
|
3464
|
+
-webkit-mask-size: contain;
|
|
3465
|
+
mask-size: contain;
|
|
3466
|
+
-webkit-mask-repeat: no-repeat;
|
|
3467
|
+
mask-repeat: no-repeat;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3296
3470
|
`;
|
|
3297
3471
|
};
|
|
3298
3472
|
__modules["styles/misc.css.js"] = function (require, module, exports) {
|
|
@@ -3654,6 +3828,70 @@ exports.HOME_CSS = `/* ---------- phone app shell (< 768px) ---------- */
|
|
|
3654
3828
|
overscroll-behavior: contain;
|
|
3655
3829
|
-webkit-overflow-scrolling: touch;
|
|
3656
3830
|
}
|
|
3831
|
+
/* Swipe-to-archive wrapper (redone 2026-08-25, real-device round): iOS-mail
|
|
3832
|
+
mechanics. The wrapper CLIPS (overflow hidden, same 22px radius as the
|
|
3833
|
+
card) and the archive button is parked past the right edge, sliding in
|
|
3834
|
+
lock-step with the card — a 1px drag reveals a 1px slice, never a
|
|
3835
|
+
fully-drawn button behind a gap. Clipping would eat the card's cast
|
|
3836
|
+
shadow, so the light-theme shadow moves up to this wrapper (identical
|
|
3837
|
+
values; the dark theme's INSET hairline on the card survives clipping
|
|
3838
|
+
untouched). pan-y keeps vertical list scrolling native; the direction
|
|
3839
|
+
lock in MobileHome.tsx decides horizontal-vs-vertical once per touch. */
|
|
3840
|
+
[data-mobile-nav="home-swipe"] {
|
|
3841
|
+
position: relative;
|
|
3842
|
+
overflow: hidden;
|
|
3843
|
+
border-radius: 22px;
|
|
3844
|
+
/* The list is a flex column: without this, an overflowing list SHRINKS
|
|
3845
|
+
every row and the clip above slices the squashed card's bottom edge
|
|
3846
|
+
flat (real-device report, 2026-08-25). Rows keep natural height; the
|
|
3847
|
+
list scrolls. */
|
|
3848
|
+
flex: 0 0 auto;
|
|
3849
|
+
touch-action: pan-y;
|
|
3850
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 4px 12px rgba(0, 0, 0, .05);
|
|
3851
|
+
}
|
|
3852
|
+
body[data-ds-dark-theme] [data-mobile-nav="home-swipe"] {
|
|
3853
|
+
box-shadow: none;
|
|
3854
|
+
}
|
|
3855
|
+
/* Press = settle toward the page (mirrors the card's own :active swap,
|
|
3856
|
+
which the clip made invisible at wrapper level). */
|
|
3857
|
+
[data-mobile-nav="home-swipe"]:has([data-mobile-nav="home-row"]:active) {
|
|
3858
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
|
3859
|
+
}
|
|
3860
|
+
body[data-ds-dark-theme] [data-mobile-nav="home-swipe"]:has([data-mobile-nav="home-row"]:active) {
|
|
3861
|
+
box-shadow: none;
|
|
3862
|
+
}
|
|
3863
|
+
[data-mobile-nav="home-swipe-card"] {
|
|
3864
|
+
position: relative;
|
|
3865
|
+
z-index: 1;
|
|
3866
|
+
transition: transform .22s var(--ds-ease-out, ease-out);
|
|
3867
|
+
will-change: transform;
|
|
3868
|
+
}
|
|
3869
|
+
/* Inset pill, not a full-bleed slab (real-device feedback: hard edges).
|
|
3870
|
+
5px breathing room on three sides inside the 88px reveal; its own
|
|
3871
|
+
radius echoes the card family. It still slides in lock-step from past
|
|
3872
|
+
the right edge (transform driven from MobileHome.tsx). */
|
|
3873
|
+
[data-mobile-nav="home-archive"] {
|
|
3874
|
+
position: absolute;
|
|
3875
|
+
z-index: 0;
|
|
3876
|
+
top: 5px;
|
|
3877
|
+
bottom: 5px;
|
|
3878
|
+
right: 5px;
|
|
3879
|
+
width: 78px;
|
|
3880
|
+
border: none;
|
|
3881
|
+
border-radius: 17px;
|
|
3882
|
+
background: var(--dsw-alias-state-error-primary, #d4494c);
|
|
3883
|
+
color: #fff;
|
|
3884
|
+
font-size: 13px;
|
|
3885
|
+
font-weight: 600;
|
|
3886
|
+
cursor: pointer;
|
|
3887
|
+
-webkit-tap-highlight-color: transparent;
|
|
3888
|
+
transition: transform .22s var(--ds-ease-out, ease-out);
|
|
3889
|
+
will-change: transform;
|
|
3890
|
+
}
|
|
3891
|
+
[data-mobile-nav="home-archive"]:active {
|
|
3892
|
+
filter: brightness(.9);
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3657
3895
|
[data-mobile-nav="home-row"] {
|
|
3658
3896
|
display: flex;
|
|
3659
3897
|
align-items: center;
|
|
@@ -4502,6 +4740,10 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4502
4740
|
justify-content: flex-start !important;
|
|
4503
4741
|
gap: 5px !important;
|
|
4504
4742
|
padding: 2px 8px 8px !important;
|
|
4743
|
+
/* One line, always: the model pill is the only shrinkable item (its
|
|
4744
|
+
label ellipsizes), so wrapping would only ever push the send button
|
|
4745
|
+
to a second row (real-device report, 2026-08-25). */
|
|
4746
|
+
flex-wrap: nowrap !important;
|
|
4505
4747
|
}
|
|
4506
4748
|
${ROW} > [class$="_tools"],
|
|
4507
4749
|
${ROW} > [class$="_trailing"] {
|
|
@@ -4528,8 +4770,14 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4528
4770
|
the right edge, so no spacer element is needed. */
|
|
4529
4771
|
${MODEL} {
|
|
4530
4772
|
order: 4 !important;
|
|
4531
|
-
flex
|
|
4773
|
+
/* A definite flex-basis (not auto) breaks the circular sizing between
|
|
4774
|
+
this seat and the trigger's max-width:100%: the seat's width comes
|
|
4775
|
+
from flex resolution, the trigger fills up to it, the label
|
|
4776
|
+
ellipsizes inside. Under nowrap pressure this is the only
|
|
4777
|
+
min-width:0 seat, so it absorbs all shrink. */
|
|
4778
|
+
flex: 0 1 min(48vw, 200px) !important;
|
|
4532
4779
|
min-width: 0 !important;
|
|
4780
|
+
max-width: min(48vw, 200px) !important;
|
|
4533
4781
|
margin-right: auto !important;
|
|
4534
4782
|
}
|
|
4535
4783
|
/* Third-party input.right entries park next to the ring rather than
|
|
@@ -4546,11 +4794,11 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4546
4794
|
order: 7 !important;
|
|
4547
4795
|
}
|
|
4548
4796
|
|
|
4549
|
-
/* --- 3. permission
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
~44x30 icon button. This AGREES with (rather than fights) the official
|
|
4797
|
+
/* --- 3. permission as icon-only pill; model as name+effort pill ---
|
|
4798
|
+
(real-device round 2, 2026-08-17; model pill reworked 2026-08-25:
|
|
4799
|
+
the user wants to SEE which model/effort is active, so only the
|
|
4800
|
+
permission trigger stays icon-only — its label is hidden and it
|
|
4801
|
+
collapses to a plain ~44x30 icon button. This AGREES with (rather than fights) the official
|
|
4554
4802
|
container query (\`@container (width <= 460px) { .trigger:has(.triggerIcon)
|
|
4555
4803
|
.triggerLabel { display: none } }\`) that S3 had to override — no need
|
|
4556
4804
|
to override it back. Accessible name is unaffected: both official
|
|
@@ -4573,13 +4821,10 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4573
4821
|
accessible-name computation (empty generated content). S3's
|
|
4574
4822
|
rtl-ellipsis trick on the model label is simply inert under
|
|
4575
4823
|
display:none now; left alone rather than unpicked. */
|
|
4576
|
-
${PERM} [class$="_triggerLabel"]
|
|
4577
|
-
${MODEL} > [class$="_trigger"] > [class$="_triggerLabel"],
|
|
4578
|
-
${MODEL} > [class$="_trigger"] > [class$="_triggerEffort"] {
|
|
4824
|
+
${PERM} [class$="_triggerLabel"] {
|
|
4579
4825
|
display: none !important;
|
|
4580
4826
|
}
|
|
4581
|
-
${PERM} button[class$="_trigger"]
|
|
4582
|
-
${MODEL} > [class$="_trigger"] {
|
|
4827
|
+
${PERM} button[class$="_trigger"] {
|
|
4583
4828
|
background: var(--dsw-specific-selector, rgba(127, 127, 127, .12)) !important;
|
|
4584
4829
|
width: 44px !important;
|
|
4585
4830
|
height: 30px !important;
|
|
@@ -4591,29 +4836,47 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4591
4836
|
border-radius: 999px !important;
|
|
4592
4837
|
touch-action: manipulation !important;
|
|
4593
4838
|
}
|
|
4839
|
+
/* Model trigger: name + effort pill (2026-08-25 user request — the
|
|
4840
|
+
icon-only form hid which model was active). The effort text must stay
|
|
4841
|
+
whole; the model NAME is the elastic part and ellipsizes. max-width
|
|
4842
|
+
caps the pill so the row's elastic gap survives extreme model ids;
|
|
4843
|
+
the label rules below override the <=1023px grow rules in
|
|
4844
|
+
layout.css.ts (flex 1 1 auto) that would otherwise pad the pill. */
|
|
4845
|
+
${MODEL} > [class$="_trigger"] {
|
|
4846
|
+
background: var(--dsw-specific-selector, rgba(127, 127, 127, .12)) !important;
|
|
4847
|
+
height: 30px !important;
|
|
4848
|
+
width: auto !important;
|
|
4849
|
+
max-width: 100% !important;
|
|
4850
|
+
min-width: 0 !important;
|
|
4851
|
+
padding: 0 10px !important;
|
|
4852
|
+
gap: 4px !important;
|
|
4853
|
+
justify-content: flex-start !important;
|
|
4854
|
+
border-radius: 999px !important;
|
|
4855
|
+
touch-action: manipulation !important;
|
|
4856
|
+
font-size: 12px !important;
|
|
4857
|
+
}
|
|
4858
|
+
${MODEL} > [class$="_trigger"] > [class$="_triggerLabel"] {
|
|
4859
|
+
display: block !important;
|
|
4860
|
+
flex: 0 1 auto !important;
|
|
4861
|
+
min-width: 0 !important;
|
|
4862
|
+
overflow: hidden !important;
|
|
4863
|
+
text-overflow: ellipsis !important;
|
|
4864
|
+
white-space: nowrap !important;
|
|
4865
|
+
}
|
|
4866
|
+
${MODEL} > [class$="_trigger"] > [class$="_triggerEffort"] {
|
|
4867
|
+
display: block !important;
|
|
4868
|
+
flex: none !important;
|
|
4869
|
+
white-space: nowrap !important;
|
|
4870
|
+
}
|
|
4594
4871
|
/* PermissionSelect wraps its trigger in the Menu primitive's root span,
|
|
4595
4872
|
which must shrink to the icon button's fixed width. */
|
|
4596
4873
|
${PERM} > span:has(> button[class$="_trigger"]) {
|
|
4597
4874
|
flex: 0 0 auto !important;
|
|
4598
4875
|
min-width: 0 !important;
|
|
4599
4876
|
}
|
|
4600
|
-
/*
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
hook into. */
|
|
4604
|
-
${MODEL} > [class$="_trigger"]::before {
|
|
4605
|
-
content: '';
|
|
4606
|
-
width: 16px;
|
|
4607
|
-
height: 16px;
|
|
4608
|
-
flex: none;
|
|
4609
|
-
background: currentColor;
|
|
4610
|
-
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E");
|
|
4611
|
-
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E");
|
|
4612
|
-
-webkit-mask-size: contain;
|
|
4613
|
-
mask-size: contain;
|
|
4614
|
-
-webkit-mask-repeat: no-repeat;
|
|
4615
|
-
mask-repeat: no-repeat;
|
|
4616
|
-
}
|
|
4877
|
+
/* (2026-08-25) The model trigger's CSS-only Sparkle ::before icon is gone:
|
|
4878
|
+
the pill now shows the model name + effort text itself, so an icon would
|
|
4879
|
+
just eat label width. */
|
|
4617
4880
|
|
|
4618
4881
|
/* --- 4. both menus become bottom sheets ---
|
|
4619
4882
|
The permission menu is the Menu primitive (role=menu, absolute, side=top)
|
|
@@ -4731,6 +4994,16 @@ exports.COMPOSER_CSS = `/* ---------- phone composer (< 768px) ---------- */
|
|
|
4731
4994
|
overflow-x: auto !important;
|
|
4732
4995
|
overflow-y: hidden !important;
|
|
4733
4996
|
scrollbar-width: none !important;
|
|
4997
|
+
/* The dock is itself a flex ITEM of the composer stack, and the
|
|
4998
|
+
new-session hero pins that stack to a fixed height. overflow-x:auto
|
|
4999
|
+
above makes this a scroll container, which drops a flex item's
|
|
5000
|
+
automatic minimum size to 0 — so the hero squeezed the dock to ~5px
|
|
5001
|
+
and the attachment chips slid under the card drawn after it (only the
|
|
5002
|
+
top sliver of a thumbnail showed; reported from a real phone,
|
|
5003
|
+
2026-08-26). Refuse the squeeze: natural height, the stack centers a
|
|
5004
|
+
few px lower instead. In-chat the column is not height-pinned and this
|
|
5005
|
+
is a no-op. */
|
|
5006
|
+
flex-shrink: 0 !important;
|
|
4734
5007
|
}
|
|
4735
5008
|
[data-slot="conversation.input.dock"]::-webkit-scrollbar {
|
|
4736
5009
|
display: none !important;
|
|
@@ -7222,6 +7495,78 @@ function installAionuiCompat(ctx) {
|
|
|
7222
7495
|
}, 'dsh-mobile-nav: sheet rise animation replay');
|
|
7223
7496
|
}
|
|
7224
7497
|
};
|
|
7498
|
+
__modules["effects/workbench-ref-close.js"] = function (require, module, exports) {
|
|
7499
|
+
"use strict";
|
|
7500
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7501
|
+
exports.installWorkbenchRefClose = installWorkbenchRefClose;
|
|
7502
|
+
/** Phone breakpoint — same query every phone-only effect in this plugin uses. */
|
|
7503
|
+
const PHONE_QUERY = '(max-width: 767px)';
|
|
7504
|
+
/**
|
|
7505
|
+
* The file tree's per-row @-reference button (dsh-better-sidebar's
|
|
7506
|
+
* `_explorerRef` class suffix — same anchoring convention as every other
|
|
7507
|
+
* better-sidebar hook in this plugin: the plugin's own root marker plus a
|
|
7508
|
+
* stable class suffix, so the selector misses entirely when the plugin is
|
|
7509
|
+
* not installed). Covers the search results too: TreePanel renders both
|
|
7510
|
+
* through the same row-actions slot.
|
|
7511
|
+
*/
|
|
7512
|
+
const REF_SELECTOR = '[data-dsh-better-sidebar] [class$="_explorerRef"]';
|
|
7513
|
+
/** The panel's own (phone-hidden) toggle — the one official way to close it.
|
|
7514
|
+
* Same anchor gestures.ts and MobileSessionHeader.tsx already use. */
|
|
7515
|
+
const TOGGLE_SELECTOR = '[data-dsh-better-sidebar] button[class$="_toggleButton"]';
|
|
7516
|
+
/** Open-state read, shared convention with gestures.ts: the class ends in
|
|
7517
|
+
* "_panel" only while open ("_panelHidden" is appended once closed). */
|
|
7518
|
+
const PANEL_OPEN_SELECTOR = '[data-dsh-better-sidebar] [class$="_panel"]';
|
|
7519
|
+
/**
|
|
7520
|
+
* Phone: an @-file reference tap in the workbench closes the workbench.
|
|
7521
|
+
*
|
|
7522
|
+
* On a phone the panel is a full-screen surface over the conversation, so
|
|
7523
|
+
* after tapping a row's @ button the user is still looking at the file tree
|
|
7524
|
+
* — the ONLY feedback for the tap is a draft change on a composer they
|
|
7525
|
+
* cannot see (real-device report, 2026-08-26: reads as "nothing happened",
|
|
7526
|
+
* and the close pill can end up behind the software keyboard on top of it).
|
|
7527
|
+
* Closing the panel IS the feedback: the conversation comes back with the
|
|
7528
|
+
* fresh `@path` sitting in the composer.
|
|
7529
|
+
*
|
|
7530
|
+
* Capture phase on document, deliberately: the button's own React handler
|
|
7531
|
+
* calls stopPropagation() (its row would otherwise open the file), so a
|
|
7532
|
+
* bubble listener never hears the tap. Capture runs on the way DOWN, before
|
|
7533
|
+
* the target handler and its stopPropagation can matter — the aionui-compat
|
|
7534
|
+
* chevron listener set this precedent.
|
|
7535
|
+
*
|
|
7536
|
+
* The close is deferred one macrotask so the reference lands first: the
|
|
7537
|
+
* button's handler (which appends `@path` to the draft) runs at target
|
|
7538
|
+
* phase, synchronously inside this same event dispatch — by the time the
|
|
7539
|
+
* timeout fires the draft is written and the toggle click only changes what
|
|
7540
|
+
* is on screen. Desktop is untouched (checked per tap, not at install: a
|
|
7541
|
+
* resize mid-session must not leave a stale arm either way), where the
|
|
7542
|
+
* panel is a docked column and closing it after every reference would be
|
|
7543
|
+
* hostile to multi-file referencing.
|
|
7544
|
+
*/
|
|
7545
|
+
function installWorkbenchRefClose(ctx) {
|
|
7546
|
+
ctx.effect(() => {
|
|
7547
|
+
const phone = window.matchMedia(PHONE_QUERY);
|
|
7548
|
+
const onClick = (event) => {
|
|
7549
|
+
if (!phone.matches)
|
|
7550
|
+
return;
|
|
7551
|
+
const target = event.target;
|
|
7552
|
+
if (!(target instanceof Element))
|
|
7553
|
+
return;
|
|
7554
|
+
if (target.closest(REF_SELECTOR) === null)
|
|
7555
|
+
return;
|
|
7556
|
+
setTimeout(() => {
|
|
7557
|
+
// Re-check open state at fire time: the tap's own handler chain (or
|
|
7558
|
+
// a second tap) may already have closed the panel — the toggle is a
|
|
7559
|
+
// toggle, and blind-clicking it would REOPEN what just closed.
|
|
7560
|
+
if (document.querySelector(PANEL_OPEN_SELECTOR) === null)
|
|
7561
|
+
return;
|
|
7562
|
+
document.querySelector(TOGGLE_SELECTOR)?.click();
|
|
7563
|
+
}, 0);
|
|
7564
|
+
};
|
|
7565
|
+
document.addEventListener('click', onClick, true);
|
|
7566
|
+
return () => document.removeEventListener('click', onClick, true);
|
|
7567
|
+
}, 'dsh-mobile-nav: workbench @-reference closes the panel');
|
|
7568
|
+
}
|
|
7569
|
+
};
|
|
7225
7570
|
__modules["effects/header-status.js"] = function (require, module, exports) {
|
|
7226
7571
|
"use strict";
|
|
7227
7572
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -8292,6 +8637,7 @@ const index_ts_1 = require("./styles/index.js");
|
|
|
8292
8637
|
const debug_ts_1 = require("./debug.js");
|
|
8293
8638
|
const phone_chrome_ts_1 = require("./effects/phone-chrome.js");
|
|
8294
8639
|
const aionui_compat_ts_1 = require("./effects/aionui-compat.js");
|
|
8640
|
+
const workbench_ref_close_ts_1 = require("./effects/workbench-ref-close.js");
|
|
8295
8641
|
const header_status_ts_1 = require("./effects/header-status.js");
|
|
8296
8642
|
const gestures_ts_1 = require("./effects/gestures.js");
|
|
8297
8643
|
const turn_fold_ts_1 = require("./effects/turn-fold.js");
|
|
@@ -8333,6 +8679,9 @@ function apply(ctx) {
|
|
|
8333
8679
|
// stack more blank page on top of it.
|
|
8334
8680
|
(0, phone_chrome_ts_1.installSunkInset)(ctx);
|
|
8335
8681
|
(0, aionui_compat_ts_1.installAionuiCompat)(ctx);
|
|
8682
|
+
// Phone: tapping a file's @-reference in the workbench closes the panel —
|
|
8683
|
+
// the conversation returning with the fresh mention IS the tap feedback.
|
|
8684
|
+
(0, workbench_ref_close_ts_1.installWorkbenchRefClose)(ctx);
|
|
8336
8685
|
// Session header running-status dot (S2): no official element exists to
|
|
8337
8686
|
// reposition, so this reads ctx.sessions directly and self-draws via CSS.
|
|
8338
8687
|
(0, header_status_ts_1.installHeaderStatusDot)(ctx);
|
|
@@ -8462,6 +8811,8 @@ function apply(ctx) {
|
|
|
8462
8811
|
startSession: (workspaceId) => ctx.workspaces.startSession(workspaceId),
|
|
8463
8812
|
// S5 session-log chip — the same service call MobileDrawerFooter uses.
|
|
8464
8813
|
downloadSessionLog: (id) => ctx.sessionLogDownload.download(id),
|
|
8814
|
+
// Row swipe action — the same binding MobileSessionInfo archives with.
|
|
8815
|
+
archiveSession: (id) => ctx.workspaces.archiveSession(id),
|
|
8465
8816
|
}),
|
|
8466
8817
|
}, MobileHome_tsx_1.MobileHome));
|
|
8467
8818
|
// Session log download, relocated from the session header to the drawer
|
|
@@ -10,6 +10,8 @@ export type MobileHomeProps = PropsRuntime<'shell.overlay'> & PropsStore<ReturnT
|
|
|
10
10
|
startSession: (workspaceId?: WorkspaceId) => void;
|
|
11
11
|
/** Bound ctx.sessionLogDownload.download() — the session-log chip (S5). */
|
|
12
12
|
downloadSessionLog: (id: SessionId) => void;
|
|
13
|
+
/** Bound ctx.workspaces.archiveSession(id) — the row swipe action. */
|
|
14
|
+
archiveSession: (id: SessionId) => Promise<void>;
|
|
13
15
|
};
|
|
14
16
|
/**
|
|
15
17
|
* Phone home screen: the full-screen session list that owns the first level
|
|
@@ -20,5 +22,5 @@ export type MobileHomeProps = PropsRuntime<'shell.overlay'> & PropsStore<ReturnT
|
|
|
20
22
|
* all navigation from the injected official actions; nothing here reads the
|
|
21
23
|
* official DOM.
|
|
22
24
|
*/
|
|
23
|
-
export declare function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession, startSession, downloadSessionLog, t, }: MobileHomeProps): import("react").JSX.Element | null;
|
|
25
|
+
export declare function MobileHome({ useSessions, useWorkspaces, useStore, actions, openSession, startSession, downloadSessionLog, archiveSession, t, }: MobileHomeProps): import("react").JSX.Element | null;
|
|
24
26
|
//# sourceMappingURL=MobileHome.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileHome.d.ts","sourceRoot":"","sources":["../../../src/client/MobileHome.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MobileHome.d.ts","sourceRoot":"","sources":["../../../src/client/MobileHome.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAS7F,OAAO,KAAK,EAAE,SAAS,EAAkB,WAAW,EAAE,MAAM,wCAAwC,CAAA;AACpG,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAMjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAMpD,kEAAkE;AAClE,MAAM,MAAM,eAAe,GACvB,YAAY,CAAC,eAAe,CAAC,GAC7B,UAAU,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,GAC7C,WAAW,CAAC,OAAO,EAAE,CAAC,GACtB;IACA,mCAAmC;IACnC,WAAW,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IACpC,uDAAuD;IACvD,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,KAAK,IAAI,CAAA;IACjD,2EAA2E;IAC3E,kBAAkB,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;IAC3C,sEAAsE;IACtE,cAAc,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACjD,CAAA;AAkMH;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,aAAa,EACb,QAAQ,EACR,OAAO,EACP,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,CAAC,GACF,EAAE,eAAe,sCAgRjB"}
|