dsh-dock 0.5.0 → 0.7.0

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/src/client.jsx CHANGED
@@ -24,12 +24,13 @@ import { feature as fHeartbeat } from "../features/heartbeat/view.js";
24
24
  import { feature as fTheme } from "../features/theme/view.js";
25
25
  import { feature as fBalance } from "../features/balance/view.js";
26
26
  import { feature as fAnimation } from "../features/animation/view.jsx";
27
+ import { feature as fMobileRelay } from "../features/mobile-relay/view.jsx";
27
28
 
28
29
  const name = "dsh-dock";
29
- const DOCK_VERSION = "0.5.0";
30
+ const DOCK_VERSION = "0.7.0";
30
31
 
31
32
  // ---- 内置功能注册表:新功能 = features/<id>/ 加模块 + 这里 import 一行 ----
32
- const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fHeartbeat, fTheme, fBalance, fAnimation];
33
+ const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fHeartbeat, fTheme, fBalance, fAnimation, fMobileRelay];
33
34
  // 规划占位(路线图):接入后移除并建 features/<id>/ 模块
34
35
  const PLANNED_FEATURES = [];
35
36
  const PLANNED_NOTES = {};
@@ -107,9 +108,13 @@ const SHELL_CSS = [
107
108
  ".dock-badge{flex:none;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:3px 10px;font-size:12px;}",
108
109
  ".dock-body{border-top:1px solid var(--dsw-alias-border-l1);padding-top:8px;color:var(--dsw-alias-label-secondary);display:flex;flex-direction:column;gap:4px;}",
109
110
  // 侧栏入口按钮(docke2- 前缀)+ 功能坞弹出面板(dockm- 前缀,仿 dsh 设置的居中模态:遮罩 + 对话框,左导航 + 右内容)
110
- ".docke2-btn{box-sizing:border-box;cursor:pointer;display:inline-flex;align-items:center;gap:6px;border:none;background:transparent;color:var(--dsw-alias-label-secondary);border-radius:10px;height:32px;font-family:inherit;font-size:13px;line-height:32px;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
111
- ".docke2-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
111
+ // 侧栏入口按钮(docke2- 前缀):与 dsh 设置按钮(.VOzbGW_trigger)几何逐条对齐——
112
+ // 宽栏同为整宽左对齐行按钮(高 42、margin 4/-2、padding 0 10px 0 8px、圆角 12),窄栏同为 36 圆钮居中,
113
+ // 使两按钮上下成列、左右边缘完全对齐(此前右对齐/叠放都会错位)。
114
+ ".docke2-btn{box-sizing:border-box;cursor:pointer;display:flex;align-items:center;gap:8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);border-radius:12px;width:calc(100% + 4px);height:42px;margin:4px -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
115
+ ".docke2-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
112
116
  ".docke2-btn.docke2-on{color:var(--dsw-alias-accent,#4d9fff);background:var(--dsw-alias-accent-soft,color-mix(in srgb,var(--dsw-alias-accent,#4d9fff) 12%,transparent));}",
117
+ ".docke2-btn.docke2-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;border-radius:50%;justify-content:center;gap:0;}",
113
118
  ".docke2-label{white-space:nowrap;overflow:hidden;}",
114
119
  ".dockm-backdrop{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--dsw-alias-bg-layer-1) 55%,transparent);backdrop-filter:blur(4px);pointer-events:auto;animation:dockm-fade .15s var(--ds-ease-in-out);}",
115
120
  "@keyframes dockm-fade{from{opacity:0}to{opacity:1}}",
@@ -168,14 +173,25 @@ const SHELL_CSS = [
168
173
  // 弹层页脚:文字标签 + 开关 成组
169
174
  ".dockm-foot-sw{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:8px;}",
170
175
  ".dockm-foot-swlabel{font-size:11px;color:var(--dsw-alias-label-tertiary);}",
171
- ".dockm-foot-swlabel.on{color:var(--dsw-alias-state-success-primary);}",
176
+ ".dockm-foot-swlabel.on{color:var(--dsw-alias-state-success-primary);}",
177
+ // 窄屏:功能坞是一级工作台而不是被压扁的桌面弹窗。全屏承接安全区,导航改横向,
178
+ // 禁用桌面拖拽/缩放,所有导航和关闭动作保持至少 44px 的可点击面积。
179
+ "@media (max-width:680px){.dockm-backdrop{align-items:stretch;justify-content:stretch;background:var(--dsw-alias-bg-layer-2);backdrop-filter:none}.dockm-dialog,.dockm-dialog.dockm-max{position:fixed!important;left:0!important;top:0!important;right:auto!important;bottom:auto!important;width:100dvw!important;height:100dvh!important;min-height:100dvh;border:0;border-radius:0;box-shadow:none}.dockm-dialog.dockm-min .dockm-body{display:flex}.dockm-head{min-height:60px;box-sizing:border-box;padding:calc(env(safe-area-inset-top) + 8px) 12px 8px;cursor:default;touch-action:manipulation}.dockm-sub,.dockm-win,.dockm-resize{display:none}.dockm-title{font-size:16px}.dockm-close{width:44px;height:44px;font-size:17px}.dockm-body{flex-direction:column;min-height:0}.dockm-nav{box-sizing:border-box;width:auto;max-width:100%;min-height:56px;flex-direction:row;gap:6px;padding:6px 10px;overflow-x:auto;overflow-y:hidden;border-right:0;border-bottom:1px solid var(--dsw-alias-border-l1);scrollbar-width:none;overscroll-behavior-x:contain}.dockm-nav::-webkit-scrollbar{display:none}.dockm-nav-item{flex:none;min-height:44px;padding:0 12px;font-size:13px;touch-action:manipulation}.dockm-nav-item .dockm-badge{display:none}.dockm-content{padding:16px max(16px,env(safe-area-inset-right)) calc(20px + env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));gap:14px;overscroll-behavior:contain}.dockm-content-head{gap:5px}.dockm-name{font-size:16px}.dockm-desc{font-size:13px;line-height:1.55}.dockm-foot{padding-top:12px;font-size:11px}.dockm-foot-sw{width:100%;margin-left:0;justify-content:space-between}.dock-sw{width:44px;height:26px}.dock-sw::after{top:3px;left:3px;width:18px;height:18px}.dock-sw.on::after{transform:translateX(18px)}.dockh-grid{grid-template-columns:1fr;gap:10px}.dockh-card{padding:12px}.dockh-desc{font-size:13px}.dockh-go{font-size:12px}}",
180
+ "@media (prefers-reduced-motion:reduce){.dockm-backdrop,.dockm-dialog{animation:none}.dockm-nav-item,.dockm-close,.dockm-win,.dock-sw{transition:none}}",
172
181
  ".dockh-desc{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5;}",
173
182
  ".dockh-stat{color:var(--dsw-alias-label-tertiary);font-size:12px;border-top:1px solid var(--dsw-alias-border-l1);padding-top:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
174
183
  ".dockh-foot{display:flex;align-items:center;gap:8px;}",
175
184
  ".dockh-go{color:var(--dsw-alias-label-tertiary);font-size:11px;}",
176
185
  // 会话输入区工具行 chips(dockchip- 前缀):余额/用量随身小控件,挂在模型选择器左侧
177
- ".dockchip-row{display:inline-flex;align-items:center;gap:4px;min-width:0;}",
178
- ".dockchip{display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:8px;padding:2px 8px;font-family:inherit;font-size:11px;line-height:18px;white-space:nowrap;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
186
+ // 溢出兼容(两层防护,缺一不可):
187
+ // 1) 宽度上限:宿主 .row 是 flex-wrap:wrap 且换行优先于收缩,chips 一长 tools 整行变宽,
188
+ // 右侧模型选择器/发送键被挤到第二行(左右错位)。宿主 .row 带 container-type:inline-size,
189
+ // 用容器单位 cqw 让上限随输入卡宽度自适应(窄卡少占、宽卡多占);不支持 cqw 的老内核退回固定 280px。
190
+ // 2) 截断:超限部分用省略号截断(完整数值在 title 悬浮提示里),防 chips 凸出输入卡圆角(悬空)。
191
+ ".dockchip-row{display:inline-flex;align-items:center;gap:4px;min-width:0;flex:0 1 auto;overflow:hidden;max-width:280px;}",
192
+ "@supports (width:1cqw){.dockchip-row{max-width:min(280px,36cqw);}}",
193
+ ".dockchip{display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:8px;padding:2px 8px;font-family:inherit;font-size:11px;line-height:18px;white-space:nowrap;min-width:0;overflow:hidden;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
194
+ ".dockchip > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}",
179
195
  ".dockchip:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
180
196
  ".dockchip .dockchip-dot{width:6px;height:6px;border-radius:50%;flex:none;}",
181
197
  ".dockchip.err{color:var(--dsw-alias-state-error-primary);}"
@@ -221,8 +237,9 @@ function useExternalVersion() {
221
237
  // ---- 弹层窗口几何记忆(页面生命周期内):拖动/缩放后记住位置与尺寸,重开还原 ----
222
238
  let lastGeom = { x: null, y: null, w: null, h: null };
223
239
 
224
- function DockIcon() {
225
- return react.createElement("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.3, "aria-hidden": true },
240
+ function DockIcon(props) {
241
+ const size = (props && props.size) || 16;
242
+ return react.createElement("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.3, "aria-hidden": true },
226
243
  react.createElement("rect", { x: 2.5, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
227
244
  react.createElement("rect", { x: 9, y: 2.5, width: 4.4, height: 4.4, rx: 1.2 }),
228
245
  react.createElement("rect", { x: 2.5, y: 9, width: 4.4, height: 4.4, rx: 1.2 }),
@@ -235,18 +252,12 @@ function DockEntry(props) {
235
252
  react.useEffect(() => subscribePanel(() => setOpen(panelNav.open)), []);
236
253
  return react.createElement("button", {
237
254
  type: "button",
238
- className: "docke2-btn" + (open ? " docke2-on" : ""),
239
- style: wide
240
- // 与设置按钮精确同框:设置 trigger 宽栏 = 42px 高、margin-top 4 → 下移 46px、高度同为 42,
241
- // 使本按钮的四条边与设置按钮完全重合(同一水平线);rail = 36px、margin-top 8 → 下移 44px、高 36 亦然。
242
- // zIndex 保证盖在满宽设置按钮之上(其右侧为空白区,不遮齿轮图标)
243
- ? { marginLeft: "auto", transform: "translateY(46px)", zIndex: 1, height: 42, lineHeight: "42px", padding: "0 12px" }
244
- : { transform: "translateY(44px)", zIndex: 1, width: 36, height: 36, justifyContent: "center", padding: 0 },
255
+ className: "docke2-btn" + (open ? " docke2-on" : "") + (wide ? "" : " docke2-rail"),
245
256
  title: "功能坞",
246
257
  "aria-label": "功能坞",
247
258
  "aria-expanded": open,
248
259
  onClick: () => setPanelOpen(!open)
249
- }, react.createElement(DockIcon, null), wide ? react.createElement("span", { className: "docke2-label" }, "功能坞") : null);
260
+ }, react.createElement(DockIcon, { size: wide ? 16 : 18 }), wide ? react.createElement("span", { className: "docke2-label" }, "功能坞") : null);
250
261
  }
251
262
 
252
263
  // ---- 功能坞弹出面板:shell.overlay(仿 dsh 设置:居中模态 = 遮罩 + 对话框) ----
@@ -605,7 +616,22 @@ export function apply(ctx) {
605
616
  slots.inject("conversation.input.left", () => slots.register(
606
617
  { name: "conversation.input.left", id: "dsh-dock-chips", order: 10, label: "功能坞" },
607
618
  (zone) => react.createElement(DockChips, Object.assign({}, zone, { ctx: ctxRef.current }))));
619
+ // 手机接力链接使用 fragment,避免配对码进入服务器日志或 Referer。插件加载后立即消费并
620
+ // 清掉 fragment,再自动打开对应功能;DSH 会话本身仍由宿主原生会话服务继续承接。
621
+ try {
622
+ if (typeof window !== "undefined") {
623
+ const match = window.location.hash.match(/(?:^#|&)dsh-mobile-relay=([^&]+)/);
624
+ if (match) {
625
+ const launch = decodeURIComponent(match[1]);
626
+ if (/^[A-Za-z0-9_-]+\.[A-F0-9]{10}$/i.test(launch)) {
627
+ sessionStorage.setItem("dsh-dock/mobile-relay/launch/v1", launch);
628
+ window.history.replaceState(null, "", window.location.pathname + window.location.search);
629
+ setTimeout(() => openPanel("mobile-relay"), 0);
630
+ }
631
+ }
632
+ }
633
+ } catch { /* 私密模式禁用 storage/history 时保持手动入口可用 */ }
608
634
  }
609
635
 
610
636
  export const inject = ["timer"];
611
- export { dockBridge };
637
+ export { dockBridge };
package/src/host-core.js CHANGED
@@ -32,6 +32,9 @@ export const DockConfig = z.object({
32
32
  // 钉钉群机器人推送(宿主侧直发,浏览器关着也能推;事件跟随 notifyOnComplete/notifyOnError)
33
33
  dingtalkEnabled: z.boolean().default(false),
34
34
  dingtalkWebhook: z.string().default(''),
35
+ // 飞书群机器人推送(宿主侧直发,浏览器关着也能推;事件跟随 notifyOnComplete/notifyOnError)
36
+ feishuEnabled: z.boolean().default(false),
37
+ feishuWebhook: z.string().default(''),
35
38
  }).default({}),
36
39
  })
37
40
 
Binary file