@michengai/dsh-automation 0.1.0 → 0.1.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.
@@ -1,9 +1,9 @@
1
1
  # 当前状态
2
2
 
3
- 更新时间:2026-08-16 22:40(Asia/Shanghai)
3
+ 更新时间:2026-08-18 08:40(Asia/Shanghai)
4
4
 
5
- - 阶段:I001 任务自动化插件首版实现,已补原生侧栏「定时」页签。
5
+ - 阶段:I001 任务自动化插件首版已发布 0.1.1,本地待发布 0.1.2 菜单互斥修复。
6
6
  - 活动迭代:[I001-任务自动化插件](..\01-当前工作\I001-任务自动化插件\00-迭代总览.md)
7
7
  - 有效风险:
8
8
  - Host 未运行时不会补跑历史 backlog。
9
- - 无人值守工具 allowlist 无法覆盖全部第三方 DSH tool。
9
+ - 无人值守工具 allowlist 无法覆盖全部第三方 DSH tool。
@@ -3,7 +3,7 @@
3
3
  - 状态:进行中
4
4
  - 负责人:维护者
5
5
  - 优先级:高
6
- - 更新时间:2026-08-16 22:40(Asia/Shanghai)
6
+ - 更新时间:2026-08-18 08:40(Asia/Shanghai)
7
7
 
8
8
  ## 目标与业务边界
9
9
 
@@ -16,6 +16,7 @@
16
16
  - [x] Web Automations 页
17
17
  - [x] 原生侧栏定时页签:包裹官方任务树,不依赖 dsh-codex-ui
18
18
  - [x] 单测与包契约
19
+ - [x] 定时侧栏会话菜单互斥,避免点第二条时与第一条重叠
19
20
  - [ ] 真实 Web Profile 组合验收
20
21
 
21
22
  ## 阻塞事项
@@ -31,7 +32,7 @@
31
32
 
32
33
  ## 已完成内容
33
34
 
34
- 仓库内已落地 Host 插件、Web 客户端、原生侧栏定时页签、文档和单测。
35
+ 仓库内已落地 Host 插件、Web 客户端、原生侧栏定时页签、文档和单测。定时会话更多菜单改为列表级互斥,并用固定定位弹出,避免两条菜单叠在一起。
35
36
 
36
37
  ## 待处理事项
37
38
 
@@ -39,9 +40,10 @@
39
40
 
40
41
  ## 关联提交
41
42
 
42
- 尚未发布提交。
43
+ 本地菜单互斥修复待随 0.1.2 发布。
43
44
 
44
45
  ## 阅读顺序
45
46
 
46
47
  1. [项目概览](..\..\00-交接入口\01-项目概览.md)
47
48
  2. [调度与执行模型](..\..\03-技术架构\01-调度与执行模型.md)
49
+
package/lib/client.js CHANGED
@@ -1873,6 +1873,49 @@ function isForeignSidebarHost(component) {
1873
1873
 
1874
1874
  // src/client/native-session-list.tsx
1875
1875
  var import_react4 = require("react");
1876
+ var import_react_dom2 = require("react-dom");
1877
+
1878
+ // src/client/native-session-menu.ts
1879
+ function resolveEventElement(target) {
1880
+ if (target == null || typeof target !== "object") return null;
1881
+ const node = target;
1882
+ if (node.nodeType === 3 || node.nodeType === 8) {
1883
+ return node.parentElement ?? null;
1884
+ }
1885
+ return node;
1886
+ }
1887
+ function shouldCloseNativeSessionMenu(target, keepInside) {
1888
+ const el = resolveEventElement(target);
1889
+ if (el == null) return true;
1890
+ return !keepInside.some((root) => {
1891
+ if (root == null || typeof root !== "object") return false;
1892
+ if (root === el) return true;
1893
+ const box = root;
1894
+ return typeof box.contains === "function" && box.contains(el);
1895
+ });
1896
+ }
1897
+ function nextOpenSessionMenuId(current, clicked) {
1898
+ return current === clicked ? null : clicked;
1899
+ }
1900
+ function nativeSessionMenuStyle(box, viewportWidth) {
1901
+ return {
1902
+ position: "fixed",
1903
+ zIndex: 1200,
1904
+ top: `${Math.round(box.bottom + 4)}px`,
1905
+ right: `${Math.max(8, Math.round(viewportWidth - box.right))}px`
1906
+ };
1907
+ }
1908
+ function relativeTime(value) {
1909
+ const ts = Date.parse(value || "");
1910
+ if (!Number.isFinite(ts)) return "";
1911
+ const delta = Math.max(0, Date.now() - ts);
1912
+ const min = Math.floor(delta / 6e4);
1913
+ if (min < 1) return "\u521A\u521A";
1914
+ if (min < 60) return String(min) + "\u5206\u949F";
1915
+ const hour = Math.floor(min / 60);
1916
+ if (hour < 24) return String(hour) + "\u5C0F\u65F6";
1917
+ return String(Math.floor(hour / 24)) + "\u5929";
1918
+ }
1876
1919
 
1877
1920
  // src/run-title.ts
1878
1921
  function formatRunStamp(iso) {
@@ -2050,17 +2093,6 @@ function pickWrappableWorkspacesEntry(entries) {
2050
2093
  // src/client/native-session-list.tsx
2051
2094
  var import_jsx_runtime5 = require("react/jsx-runtime");
2052
2095
  var EMPTY_SESSION_BY_ID = {};
2053
- function relativeTime(value) {
2054
- const ts = Date.parse(value || "");
2055
- if (!Number.isFinite(ts)) return "";
2056
- const delta = Math.max(0, Date.now() - ts);
2057
- const min = Math.floor(delta / 6e4);
2058
- if (min < 1) return "\u521A\u521A";
2059
- if (min < 60) return String(min) + "\u5206\u949F";
2060
- const hour = Math.floor(min / 60);
2061
- if (hour < 24) return String(hour) + "\u5C0F\u65F6";
2062
- return String(Math.floor(hour / 24)) + "\u5929";
2063
- }
2064
2096
  function NativeScheduleSessionList(props) {
2065
2097
  const { t, runtime, openSession, useSessions, useWorkspaces, renameSession, archiveSession, deleteSession, forkSession } = props;
2066
2098
  const state = (0, import_react4.useSyncExternalStore)(runtime.source.subscribe, runtime.source.getSnapshot, runtime.source.getSnapshot);
@@ -2068,6 +2100,7 @@ function NativeScheduleSessionList(props) {
2068
2100
  const sessionById = useSessions ? useSessions((snap) => snap?.byId ?? EMPTY_SESSION_BY_ID) : EMPTY_SESSION_BY_ID;
2069
2101
  const archivedIds = useWorkspaces ? useWorkspaces((snap) => snap?.archivedSessionIds ?? []) : [];
2070
2102
  const [folded, setFolded] = (0, import_react4.useState)({});
2103
+ const [openMenuId, setOpenMenuId] = (0, import_react4.useState)(null);
2071
2104
  (0, import_react4.useEffect)(() => {
2072
2105
  void runtime.refresh().catch(() => void 0);
2073
2106
  const timer = window.setInterval(() => {
@@ -2112,6 +2145,7 @@ function NativeScheduleSessionList(props) {
2112
2145
  "aria-label": t("session.deleteFolder"),
2113
2146
  onClick: (event) => {
2114
2147
  event.stopPropagation();
2148
+ setOpenMenuId(null);
2115
2149
  void (async () => {
2116
2150
  for (const session of group.sessions) {
2117
2151
  try {
@@ -2135,7 +2169,11 @@ function NativeScheduleSessionList(props) {
2135
2169
  updatedAt: session.updatedAt,
2136
2170
  running: session.running,
2137
2171
  selected: selectedId === session.id,
2172
+ menuOpen: openMenuId === session.id,
2173
+ onToggleMenu: () => setOpenMenuId((current) => nextOpenSessionMenuId(current, session.id)),
2174
+ onCloseMenu: () => setOpenMenuId((current) => current === session.id ? null : current),
2138
2175
  onOpen: () => {
2176
+ setOpenMenuId(null);
2139
2177
  openSession?.(session.id);
2140
2178
  void runtime.adoptSession?.(session.id).catch(() => void 0);
2141
2179
  },
@@ -2157,31 +2195,52 @@ function NativeScheduleSessionList(props) {
2157
2195
  ] }) });
2158
2196
  }
2159
2197
  function NativeSessionRow(props) {
2160
- const { t, id, title, updatedAt, running, selected, onOpen, onDelete, renameSession, archiveSession, forkSession } = props;
2161
- const [menu, setMenu] = (0, import_react4.useState)(false);
2198
+ const { t, id, title, updatedAt, running, selected, menuOpen, onToggleMenu, onCloseMenu, onOpen, onDelete, renameSession, archiveSession, forkSession } = props;
2199
+ const rowRef = (0, import_react4.useRef)(null);
2200
+ const menuRef = (0, import_react4.useRef)(null);
2162
2201
  const [renaming, setRenaming] = (0, import_react4.useState)(false);
2163
2202
  const [draft, setDraft] = (0, import_react4.useState)(title);
2203
+ const [menuStyle, setMenuStyle] = (0, import_react4.useState)({});
2164
2204
  (0, import_react4.useEffect)(() => {
2165
2205
  setDraft(title);
2166
2206
  }, [title]);
2167
2207
  (0, import_react4.useEffect)(() => {
2168
- if (!menu) return;
2208
+ if (!menuOpen) return;
2169
2209
  const close = (event) => {
2170
- const target = event.target;
2171
- if (target !== null && event.target.closest(".dsh-st-n-sess.is-menu") !== null) return;
2172
- setMenu(false);
2210
+ if (!shouldCloseNativeSessionMenu(event.target, [rowRef.current, menuRef.current])) return;
2211
+ onCloseMenu();
2212
+ };
2213
+ const onKey = (event) => {
2214
+ if (event.key === "Escape") onCloseMenu();
2173
2215
  };
2174
2216
  document.addEventListener("mousedown", close);
2217
+ document.addEventListener("keydown", onKey);
2175
2218
  return () => {
2176
2219
  document.removeEventListener("mousedown", close);
2220
+ document.removeEventListener("keydown", onKey);
2177
2221
  };
2178
- }, [menu]);
2222
+ }, [menuOpen, onCloseMenu]);
2223
+ (0, import_react4.useLayoutEffect)(() => {
2224
+ if (!menuOpen) return;
2225
+ const update = () => {
2226
+ const box = rowRef.current?.getBoundingClientRect();
2227
+ if (box === void 0) return;
2228
+ setMenuStyle(nativeSessionMenuStyle(box, window.innerWidth));
2229
+ };
2230
+ update();
2231
+ window.addEventListener("resize", update);
2232
+ document.addEventListener("scroll", update, true);
2233
+ return () => {
2234
+ window.removeEventListener("resize", update);
2235
+ document.removeEventListener("scroll", update, true);
2236
+ };
2237
+ }, [menuOpen]);
2179
2238
  const run = (action) => {
2180
2239
  void Promise.resolve(action()).catch(() => void 0);
2181
2240
  };
2182
- const rowClass = "dsh-st-n-sess" + (selected ? " is-on" : "") + (menu ? " is-menu" : "");
2241
+ const rowClass = "dsh-st-n-sess" + (selected ? " is-on" : "") + (menuOpen ? " is-menu" : "");
2183
2242
  if (renaming) {
2184
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: rowClass, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { className: "dsh-st-n-rename", value: draft, autoFocus: true, "aria-label": t("session.rename"), onChange: (event) => setDraft(event.target.value), onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: rowClass, ref: rowRef, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { className: "dsh-st-n-rename", value: draft, autoFocus: true, "aria-label": t("session.rename"), onChange: (event) => setDraft(event.target.value), onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
2185
2244
  if (event.key === "Enter") {
2186
2245
  event.preventDefault();
2187
2246
  setRenaming(false);
@@ -2205,7 +2264,17 @@ function NativeSessionRow(props) {
2205
2264
  { id: "archive", label: t("session.archive"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArchiveIcon, { width: 16, height: 16 }), go: () => run(() => archiveSession?.(id)) },
2206
2265
  { id: "delete", label: t("session.delete"), icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TrashIcon, { width: 16, height: 16 }), danger: true, go: () => run(() => onDelete()) }
2207
2266
  ];
2208
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: rowClass, role: "treeitem", tabIndex: 0, "aria-selected": selected, onClick: onOpen, onKeyDown: (event) => {
2267
+ const menu = menuOpen && typeof document !== "undefined" ? (0, import_react_dom2.createPortal)(
2268
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: menuRef, className: "dsh-st-n-menu is-float", "data-n-menu": id, style: menuStyle, onMouseDown: (event) => event.stopPropagation(), onClick: (event) => event.stopPropagation(), children: menuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", className: item.danger === true ? "danger" : void 0, onClick: () => {
2269
+ onCloseMenu();
2270
+ item.go();
2271
+ }, children: [
2272
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2273
+ item.label
2274
+ ] }, item.id)) }),
2275
+ document.body
2276
+ ) : null;
2277
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: rowClass, ref: rowRef, role: "treeitem", tabIndex: 0, "aria-selected": selected, "data-n-menu-root": id, onClick: onOpen, onKeyDown: (event) => {
2209
2278
  if (event.key === "Enter" || event.key === " ") {
2210
2279
  event.preventDefault();
2211
2280
  onOpen();
@@ -2213,22 +2282,16 @@ function NativeSessionRow(props) {
2213
2282
  }, onContextMenu: (event) => {
2214
2283
  event.preventDefault();
2215
2284
  event.stopPropagation();
2216
- setMenu((open) => !open);
2285
+ onToggleMenu();
2217
2286
  }, children: [
2218
2287
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-slot", children: running ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RunningStateDot, {}) : null }),
2219
2288
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-title", children: title }),
2220
2289
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-time", children: relativeTime(updatedAt) }),
2221
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "dsh-st-n-ico", "aria-label": title + " \u66F4\u591A", onClick: (event) => {
2290
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-acts", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "dsh-st-n-ico", "aria-label": title + " \u66F4\u591A", onMouseDown: (event) => event.stopPropagation(), onClick: (event) => {
2222
2291
  event.stopPropagation();
2223
- setMenu((open) => !open);
2292
+ onToggleMenu();
2224
2293
  }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EllipsisIcon, { width: 16, height: 16 }) }) }),
2225
- menu && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dsh-st-n-menu", onClick: (event) => event.stopPropagation(), children: menuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", className: item.danger === true ? "danger" : void 0, onClick: () => {
2226
- setMenu(false);
2227
- item.go();
2228
- }, children: [
2229
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dsh-st-n-mi", children: item.icon }),
2230
- item.label
2231
- ] }, item.id)) })
2294
+ menu
2232
2295
  ] });
2233
2296
  }
2234
2297
 
@@ -2556,7 +2619,7 @@ var CSS_TEXT = `
2556
2619
  .dsh-st-shell-body>*{min-width:0;flex:1}.dsh-st-official-tree{display:flex;min-height:0;flex:1;overflow:hidden}.dsh-st-official-tree>*{min-width:0;flex:1}
2557
2620
  .dsh-st-rail-head.is-static{cursor:default;font-weight:600}
2558
2621
  .dsh-st-rail-session.is-on{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}
2559
- .dsh-st-n,.dsh-st-n *{box-sizing:border-box}.dsh-st-n{box-sizing:border-box;display:flex;flex:1;min-width:0;max-width:100%;min-height:0;flex-direction:column;padding:0;padding-right:var(--dsh-sidebar-inline-padding,12px);color:var(--dsw-alias-label-primary,inherit);font:14px/20px inherit;overflow:hidden}.dsh-st-n-head{flex:none;display:flex;align-items:center;height:36px;padding-left:4px;margin:2px 0 4px;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-head-label{font-size:14px;line-height:20px}.dsh-st-n-tree{flex:1;min-width:0;max-width:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0 0 16px 4px;user-select:none}.dsh-st-n-empty{padding:14px 8px;color:var(--dsw-alias-label-tertiary,#8b8f98);font-size:12px}.dsh-st-n-group{min-width:0;max-width:100%}.dsh-st-n-row,.dsh-st-n-sess{display:flex;align-items:center;max-width:100%;border-radius:8px;padding:0 8px;cursor:pointer;border:0;background:transparent;color:var(--dsw-alias-label-primary,inherit);text-align:left;font:14px/20px inherit}.dsh-st-n-row{height:34px;gap:6px}.dsh-st-n-sess{height:32px;gap:0;position:relative;width:100%;appearance:none}.dsh-st-n-row:hover,.dsh-st-n-sess:hover,.dsh-st-n-sess.is-on,.dsh-st-n-sess.is-menu{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-slot{flex:none;width:16px;height:20px;display:inline-flex;align-items:center;justify-content:center}.dsh-st-n-row .dsh-st-n-chevron{display:none;color:var(--dsw-alias-label-caption,#ADB2B8)}.dsh-st-n-row:hover .dsh-st-n-chevron{display:inline-flex}.dsh-st-n-row:hover .dsh-st-n-folder{display:none}.dsh-st-n-arrow{transition:transform .15s ease}.dsh-st-n-arrow.is-open{transform:rotate(90deg)}.dsh-st-n-title{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}.dsh-st-n-sess .dsh-st-n-title{margin:0 6px 0 4px}.dsh-st-n-time{flex:none;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#81858C);white-space:nowrap}.dsh-st-n-acts{flex:none;display:none;align-items:center;gap:12px}.dsh-st-n-row:hover .dsh-st-n-acts,.dsh-st-n-sess:hover .dsh-st-n-acts,.dsh-st-n-row.is-menu .dsh-st-n-acts,.dsh-st-n-sess.is-menu .dsh-st-n-acts{display:inline-flex}.dsh-st-n-sess:hover .dsh-st-n-time,.dsh-st-n-sess.is-menu .dsh-st-n-time{display:none}.dsh-st-n-ico{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#81858C);padding:0;cursor:pointer}.dsh-st-n-ico:hover{color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-menu{position:absolute;right:8px;top:calc(100% + 4px);z-index:1100;min-width:218px;max-width:360px;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36))}.dsh-st-n-menu button{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:0;border-radius:10px;background:transparent;cursor:pointer;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary,inherit);text-align:left}.dsh-st-n-menu button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-mi{display:inline-flex;flex:none;width:16px;height:16px;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-menu button.danger{color:var(--dsw-alias-state-error-primary,#f85149)}.dsh-st-n-menu button.danger .dsh-st-n-mi{color:inherit}.dsh-st-n-menu button.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(248,81,73,.12))}.dsh-st-n-rename{flex:1;min-width:0;margin:0 6px 0 4px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-button-elevated-fill,rgba(255,255,255,.04));color:inherit;font:inherit;padding:0 2px}
2622
+ .dsh-st-n,.dsh-st-n *{box-sizing:border-box}.dsh-st-n{box-sizing:border-box;display:flex;flex:1;min-width:0;max-width:100%;min-height:0;flex-direction:column;padding:0;padding-right:var(--dsh-sidebar-inline-padding,12px);color:var(--dsw-alias-label-primary,inherit);font:14px/20px inherit;overflow:hidden}.dsh-st-n-head{flex:none;display:flex;align-items:center;height:36px;padding-left:4px;margin:2px 0 4px;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-head-label{font-size:14px;line-height:20px}.dsh-st-n-tree{flex:1;min-width:0;max-width:100%;min-height:0;overflow-x:hidden;overflow-y:auto;padding:0 0 16px 4px;user-select:none}.dsh-st-n-empty{padding:14px 8px;color:var(--dsw-alias-label-tertiary,#8b8f98);font-size:12px}.dsh-st-n-group{min-width:0;max-width:100%}.dsh-st-n-row,.dsh-st-n-sess{display:flex;align-items:center;max-width:100%;border-radius:8px;padding:0 8px;cursor:pointer;border:0;background:transparent;color:var(--dsw-alias-label-primary,inherit);text-align:left;font:14px/20px inherit}.dsh-st-n-row{height:34px;gap:6px}.dsh-st-n-sess{height:32px;gap:0;position:relative;width:100%;appearance:none}.dsh-st-n-row:hover,.dsh-st-n-sess:hover,.dsh-st-n-sess.is-on,.dsh-st-n-sess.is-menu{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-slot{flex:none;width:16px;height:20px;display:inline-flex;align-items:center;justify-content:center}.dsh-st-n-row .dsh-st-n-chevron{display:none;color:var(--dsw-alias-label-caption,#ADB2B8)}.dsh-st-n-row:hover .dsh-st-n-chevron{display:inline-flex}.dsh-st-n-row:hover .dsh-st-n-folder{display:none}.dsh-st-n-arrow{transition:transform .15s ease}.dsh-st-n-arrow.is-open{transform:rotate(90deg)}.dsh-st-n-title{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px}.dsh-st-n-sess .dsh-st-n-title{margin:0 6px 0 4px}.dsh-st-n-time{flex:none;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary,#81858C);white-space:nowrap}.dsh-st-n-acts{flex:none;display:none;align-items:center;gap:12px}.dsh-st-n-row:hover .dsh-st-n-acts,.dsh-st-n-sess:hover .dsh-st-n-acts,.dsh-st-n-row.is-menu .dsh-st-n-acts,.dsh-st-n-sess.is-menu .dsh-st-n-acts{display:inline-flex}.dsh-st-n-sess:hover .dsh-st-n-time,.dsh-st-n-sess.is-menu .dsh-st-n-time{display:none}.dsh-st-n-ico{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#81858C);padding:0;cursor:pointer}.dsh-st-n-ico:hover{color:var(--dsw-alias-label-primary,inherit)}.dsh-st-n-menu.is-float{position:fixed;z-index:1200}.dsh-st-n-menu{position:absolute;right:8px;top:calc(100% + 4px);z-index:1100;min-width:218px;max-width:360px;box-sizing:border-box;padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,rgba(255,255,255,.12));border-radius:12px;background:var(--dsw-specific-menu,#1c2128);box-shadow:var(--dsw-shadow-lv3,0 8px 24px rgba(0,0,0,.36))}.dsh-st-n-menu button{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:0;border-radius:10px;background:transparent;cursor:pointer;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary,inherit);text-align:left}.dsh-st-n-menu button:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.06))}.dsh-st-n-mi{display:inline-flex;flex:none;width:16px;height:16px;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858C)}.dsh-st-n-menu button.danger{color:var(--dsw-alias-state-error-primary,#f85149)}.dsh-st-n-menu button.danger .dsh-st-n-mi{color:inherit}.dsh-st-n-menu button.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(248,81,73,.12))}.dsh-st-n-rename{flex:1;min-width:0;margin:0 6px 0 4px;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-button-elevated-fill,rgba(255,255,255,.04));color:inherit;font:inherit;padding:0 2px}
2560
2623
  `;
2561
2624
  function installStyles() {
2562
2625
  const existing = document.getElementById(STYLE_ID);