@dsh-tom/dsh-css 0.2.1 → 0.2.8

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 CHANGED
@@ -22,7 +22,7 @@ npm publish --access public --registry=https://registry.npmjs.org/
22
22
  打开 **插件 → 添加插件 → 安装第三方插件**,在“包名或地址”输入:
23
23
 
24
24
  ```text
25
- D:\product\dsh-css\dsh-tom-dsh-css-0.2.1.tgz
25
+ D:\product\dsh-css\dsh-tom-dsh-css-0.2.8.tgz
26
26
  ```
27
27
 
28
28
  安装并启用,然后完全退出并重新打开 dsh。
@@ -32,7 +32,7 @@ D:\product\dsh-css\dsh-tom-dsh-css-0.2.1.tgz
32
32
  包发布到 npmjs 后,先卸载已安装的旧版,再打开 **插件 → 添加插件 → 安装第三方插件**,输入:
33
33
 
34
34
  ```text
35
- @dsh-tom/dsh-css@0.2.1
35
+ @dsh-tom/dsh-css@0.2.8
36
36
  ```
37
37
 
38
38
  选择 **安装源 → npm 官方源**,安装并启用,然后完全退出并重新打开 dsh。
package/dist/client.js CHANGED
@@ -29,10 +29,30 @@ __export(client_exports, {
29
29
  inject: () => inject2
30
30
  });
31
31
  module.exports = __toCommonJS(client_exports);
32
- var import_react6 = require("react");
32
+ var import_react8 = require("react");
33
33
 
34
34
  // theme.css
35
- var theme_default = `/* \u6DF1\u8272\u6A21\u5F0F\u914D\u8272\uFF0C\u4FDD\u7559\u5185\u7F6E\u6587\u5B57\u3001\u8FB9\u6846\u548C\u8BED\u6CD5\u9AD8\u4EAE\u989C\u8272\u3002 */
35
+ var theme_default = `.dsh-css-unread-dot {
36
+ display: inline-block;
37
+ flex: none;
38
+ width: 8px;
39
+ height: 8px;
40
+ border-radius: 50%;
41
+ background: var(--dsw-alias-label-secondary, #aaa);
42
+ }
43
+
44
+ /* \u4F1A\u8BDD\u64CD\u4F5C\u51FA\u73B0\u65F6\u9690\u85CF\u672A\u8BFB\u63D0\u793A\uFF1B\u9879\u76EE\u6811\u4FDD\u7559\u5DE6\u4FA7\u56FE\u6807\u4F4D\u7F6E\uFF0C\u907F\u514D\u6807\u9898\u8DF3\u52A8\u3002 */
45
+ .hIlkoa_sessionRow:is(:hover, .hIlkoa_menuOpen) > .hIlkoa_slot .dsh-css-unread-dot {
46
+ visibility: hidden;
47
+ }
48
+
49
+ /* \u83DC\u5355\u5173\u95ED\u53EF\u80FD\u6B8B\u7559\u5BBF\u4E3B\u7684\u6EDA\u52A8\u548C\u6E10\u9690\u72B6\u6001\uFF1B\u975E\u60AC\u505C\u65F6\u4ECE\u6807\u9898\u5F00\u5934\u6B63\u5E38\u663E\u793A\u3002 */
50
+ .hIlkoa_sessionRow:not(:hover):not(.hIlkoa_menuOpen) > .hIlkoa_title {
51
+ overflow: clip;
52
+ mask-image: none;
53
+ }
54
+
55
+ /* \u6DF1\u8272\u6A21\u5F0F\u914D\u8272\uFF0C\u4FDD\u7559\u5185\u7F6E\u6587\u5B57\u3001\u8FB9\u6846\u548C\u8BED\u6CD5\u9AD8\u4EAE\u989C\u8272\u3002 */
36
56
  html body[data-ds-dark-theme] {
37
57
  /* \u533A\u57DF 1\uFF1A\u5DE6\u4FA7\u4F1A\u8BDD\u5217\u8868\uFF1BWindows \u6807\u9898\u680F\u5728\u4E0B\u65B9\u5355\u72EC\u8BBE\u7F6E\u3002\u539F RGB(27, 27, 28)\u3002 */
38
58
  --dsw-specific-sidebar-fill: rgb(36, 37, 33) !important;
@@ -200,9 +220,10 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
200
220
  padding: 44px 0 0 12px;
201
221
  }
202
222
 
203
- /* \u5E73\u94FA\u4F1A\u8BDD\u6807\u9898\u4E0E\u201C\u4F1A\u8BDD\u201D\u5DE6\u5BF9\u9F50\uFF0C\u72B6\u6001\u56FE\u6807\u653E\u5230\u53F3\u4FA7\u3002 */
223
+ /* \u884C\u80CC\u666F\u5411\u5DE6\u5EF6\u4F38 4px\uFF0C\u7559\u8DB3\u6807\u9898\u5185\u8FB9\u8DDD\u5E76\u4E0E\u201C\u4F1A\u8BDD\u201D\u4FDD\u6301\u5DE6\u5BF9\u9F50\u3002 */
204
224
  & ._9lTDKa_flatList .hIlkoa_sessionRow {
205
- padding-inline-start: 4px;
225
+ margin-inline-start: -4px;
226
+ padding-inline-start: 8px;
206
227
 
207
228
  & > .hIlkoa_title {
208
229
  margin-left: 0;
@@ -212,7 +233,8 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
212
233
  order: 1;
213
234
  }
214
235
 
215
- & > .hIlkoa_slot:empty {
236
+ & > .hIlkoa_slot:empty,
237
+ &:is(:hover, .hIlkoa_menuOpen) > .hIlkoa_slot:has(.dsh-css-unread-dot) {
216
238
  display: none;
217
239
  }
218
240
  }
@@ -289,6 +311,15 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
289
311
  }
290
312
  }
291
313
 
314
+ .dsh-css-date-heading {
315
+ padding: 16px 4px 6px;
316
+ color: var(--dsw-alias-label-tertiary);
317
+ font-size: 12px;
318
+ line-height: 18px;
319
+
320
+ &:first-child { padding-top: 6px; }
321
+ }
322
+
292
323
  /* \u60AC\u6D6E\u5361\u7247\u7531\u5BBF\u4E3B\u901A\u8FC7 portal \u6302\u5728\u4FA7\u680F\u5916\u3002 */
293
324
  .dsh-css-project {
294
325
  display: none;
@@ -1050,8 +1081,210 @@ function registerCacheHitPrecision(ctx) {
1050
1081
  });
1051
1082
  }
1052
1083
 
1084
+ // src/date-groups.js
1085
+ var import_react3 = require("react");
1086
+ var DAY = 864e5;
1087
+ var calendarDay = (date5) => Date.UTC(date5.getFullYear(), date5.getMonth(), date5.getDate()) / DAY;
1088
+ function dateHeading(timestamp, now) {
1089
+ const date5 = new Date(timestamp);
1090
+ const day = calendarDay(date5);
1091
+ const age = calendarDay(new Date(now)) - day;
1092
+ const label = ["\u4ECA\u5929", "\u6628\u5929", "\u524D\u5929"][age] ?? `${String(date5.getMonth() + 1).padStart(2, "0")}-${String(date5.getDate()).padStart(2, "0")}`;
1093
+ return { day, label };
1094
+ }
1095
+ function mapElements(value, visit2) {
1096
+ if (Array.isArray(value)) return import_react3.Children.map(value, (child) => mapElements(child, visit2));
1097
+ if (!(0, import_react3.isValidElement)(value)) return value;
1098
+ const replacement = visit2(value);
1099
+ if (replacement !== value) return replacement;
1100
+ return "children" in value.props ? (0, import_react3.cloneElement)(value, {}, mapElements(value.props.children, visit2)) : value;
1101
+ }
1102
+ function DateViewOptions({ renderMenu, dateMode, ...props }) {
1103
+ const menu = renderMenu(props);
1104
+ const items = menu.props.items.flatMap((item) => item.id === "flat" ? [item, {
1105
+ id: "date",
1106
+ label: "\u6309\u65E5\u671F",
1107
+ icon: (0, import_react3.createElement)("svg", {
1108
+ width: 16,
1109
+ height: 16,
1110
+ viewBox: "0 0 24 24",
1111
+ fill: "none",
1112
+ stroke: "currentColor",
1113
+ strokeWidth: 1.7,
1114
+ strokeLinecap: "round",
1115
+ strokeLinejoin: "round",
1116
+ "aria-hidden": true
1117
+ }, (0, import_react3.createElement)("path", { d: "M8 3v4M16 3v4M3 10h18M5 5h14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2Z" }))
1118
+ }] : [item]);
1119
+ return (0, import_react3.cloneElement)(menu, {
1120
+ items,
1121
+ selectedIds: menu.props.selectedIds.map((id) => dateMode && id === "flat" ? "date" : id),
1122
+ onSelect: (id) => {
1123
+ if (id !== "date") return menu.props.onSelect(id);
1124
+ props.onGroupPick("date");
1125
+ menu.props.onClose();
1126
+ }
1127
+ });
1128
+ }
1129
+ function DateFlatList({ renderList, enabled, ...props }) {
1130
+ const [clock, setClock] = (0, import_react3.useState)(Date.now);
1131
+ (0, import_react3.useEffect)(() => {
1132
+ if (!enabled) return;
1133
+ const now2 = /* @__PURE__ */ new Date();
1134
+ const midnight = new Date(now2.getFullYear(), now2.getMonth(), now2.getDate() + 1);
1135
+ const timer = setTimeout(() => setClock(Date.now()), midnight - now2);
1136
+ return () => clearTimeout(timer);
1137
+ }, [enabled, clock]);
1138
+ const tree = renderList(props);
1139
+ if (!enabled) return tree;
1140
+ const now = Date.now();
1141
+ return mapElements(tree, (element) => {
1142
+ if (!element.props.className?.split(" ").includes("_9lTDKa_flatList")) return element;
1143
+ let previousDay;
1144
+ const children = mapElements(element.props.children, (row) => {
1145
+ const node2 = row.props.node;
1146
+ if (!node2 || node2.pinned || node2.blank) return row;
1147
+ const { day, label } = dateHeading(node2.updatedAt, now);
1148
+ if (day === previousDay) return row;
1149
+ previousDay = day;
1150
+ return [(0, import_react3.createElement)("div", {
1151
+ key: `date:${node2.id}`,
1152
+ className: "dsh-css-date-heading",
1153
+ role: "presentation"
1154
+ }, label), row];
1155
+ });
1156
+ return (0, import_react3.cloneElement)(element, {}, children);
1157
+ });
1158
+ }
1159
+ function registerDateGroups(ctx) {
1160
+ const slot = "sidebar.workspaces";
1161
+ ctx.slots.inject(slot, () => {
1162
+ const original = ctx.slots.entries(slot)[0];
1163
+ if (!original) return;
1164
+ const renderBrowser = original.component;
1165
+ function DateWorkspaceBrowser(props) {
1166
+ const dateMode = props.useStore((state) => state.groupBy === "date");
1167
+ const tree = renderBrowser({
1168
+ ...props,
1169
+ useStore: (selector) => props.useStore((state) => selector(state.groupBy === "date" ? { ...state, groupBy: "flat" } : state))
1170
+ });
1171
+ return mapElements(tree, (element) => {
1172
+ if (element.props.onGroupPick && element.props.onOrderPick) {
1173
+ return (0, import_react3.createElement)(DateViewOptions, {
1174
+ ...element.props,
1175
+ key: element.key ?? void 0,
1176
+ renderMenu: element.type,
1177
+ dateMode
1178
+ });
1179
+ }
1180
+ if (element.props.sessionIds && element.props.setSessionOrder) {
1181
+ return (0, import_react3.createElement)(DateFlatList, {
1182
+ ...element.props,
1183
+ key: element.key ?? void 0,
1184
+ renderList: element.type,
1185
+ enabled: dateMode
1186
+ });
1187
+ }
1188
+ return element;
1189
+ });
1190
+ }
1191
+ const priority = Math.max(...ctx.slots.entries(slot).map((entry) => entry.options.priority ?? 0)) + 1;
1192
+ const dispose = ctx.slots.register({ name: slot, priority }, renderBrowser);
1193
+ original.component = DateWorkspaceBrowser;
1194
+ return () => {
1195
+ original.component = renderBrowser;
1196
+ dispose();
1197
+ };
1198
+ });
1199
+ }
1200
+
1201
+ // src/unread.js
1202
+ var import_react4 = require("react");
1203
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
1204
+
1205
+ // src/unread-store.js
1206
+ var STORAGE_KEY = "dsh-css.unread.v1";
1207
+ function createUnreadStore(sessions, storage) {
1208
+ let snapshot = /* @__PURE__ */ new Set();
1209
+ const listeners = /* @__PURE__ */ new Set();
1210
+ try {
1211
+ const saved = JSON.parse((storage ?? window.localStorage).getItem(STORAGE_KEY) ?? "[]");
1212
+ if (Array.isArray(saved)) snapshot = new Set(saved.filter((id) => typeof id === "string"));
1213
+ } catch {
1214
+ }
1215
+ const publish = (next) => {
1216
+ snapshot = next;
1217
+ try {
1218
+ (storage ?? window.localStorage).setItem(STORAGE_KEY, JSON.stringify([...next]));
1219
+ } catch {
1220
+ console.warn("[dsh-css] \u672A\u8BFB\u6807\u8BB0\u65E0\u6CD5\u4FDD\u5B58\uFF0C\u91CD\u542F\u540E\u53EF\u80FD\u4E22\u5931\u3002");
1221
+ }
1222
+ for (const listener of listeners) listener();
1223
+ };
1224
+ const mainCount = (row) => row?.retainedBy?.mainView ?? 0;
1225
+ return {
1226
+ getSnapshot: () => snapshot,
1227
+ subscribe(listener) {
1228
+ listeners.add(listener);
1229
+ return () => listeners.delete(listener);
1230
+ },
1231
+ setUnread(id, unread) {
1232
+ if (snapshot.has(id) === unread) return;
1233
+ const next = new Set(snapshot);
1234
+ if (unread) next.add(id);
1235
+ else next.delete(id);
1236
+ publish(next);
1237
+ },
1238
+ watch() {
1239
+ let previous = sessions.getSnapshot().byId;
1240
+ return sessions.subscribe(() => {
1241
+ const current = sessions.getSnapshot().byId;
1242
+ const next = new Set(snapshot);
1243
+ for (const id of snapshot) {
1244
+ if (mainCount(current[id]) > mainCount(previous[id])) next.delete(id);
1245
+ }
1246
+ previous = current;
1247
+ if (next.size !== snapshot.size) publish(next);
1248
+ });
1249
+ }
1250
+ };
1251
+ }
1252
+
1253
+ // src/unread.js
1254
+ function UnreadMenuItem({ sessionId, useUnread, useWorkspaces, useMenuOpenState, setUnread }) {
1255
+ const unread = useUnread((state) => state.has(sessionId));
1256
+ const archived = useWorkspaces((state) => state.archivedSessionIds.includes(sessionId));
1257
+ const [, setMenuOpen] = useMenuOpenState();
1258
+ if (archived) return null;
1259
+ return (0, import_react4.createElement)(import_dsh_client_ui_primitives2.MenuItemButton, {
1260
+ icon: unread ? (0, import_react4.createElement)(import_dsh_client_ui_primitives2.IconCheckOutlineRegular, { size: 14 }) : (0, import_react4.createElement)("span", { className: "dsh-css-unread-dot", "aria-hidden": true }),
1261
+ onSelect: () => {
1262
+ setUnread(sessionId, !unread);
1263
+ setMenuOpen(false);
1264
+ }
1265
+ }, unread ? "\u6807\u8BB0\u4E3A\u5DF2\u8BFB" : "\u6807\u8BB0\u4E3A\u672A\u8BFB");
1266
+ }
1267
+ function UnreadIndicator({ sessionId, useUnread }) {
1268
+ return useUnread((state) => state.has(sessionId)) ? (0, import_react4.createElement)("span", { className: "dsh-css-unread-dot", role: "img", "aria-label": "\u672A\u8BFB", title: "\u672A\u8BFB" }) : null;
1269
+ }
1270
+ function registerUnread(ctx) {
1271
+ const unread = createUnreadStore(ctx.sessions.list);
1272
+ ctx.effect(() => unread.watch());
1273
+ ctx.slots.inject("sidebar.workspaces.session.menu.item", () => ctx.slots.register({
1274
+ name: "sidebar.workspaces.session.menu.item",
1275
+ id: "dsh-css-unread",
1276
+ order: 250,
1277
+ inject: () => ({ setUnread: unread.setUnread, hooks: { unread, workspaces: ctx.workspaces.list } })
1278
+ }, UnreadMenuItem));
1279
+ ctx.slots.inject("sidebar.session.row.leading", () => ctx.slots.register({
1280
+ name: "sidebar.session.row.leading",
1281
+ id: "dsh-css-unread",
1282
+ inject: () => ({ hooks: { unread } })
1283
+ }, UnreadIndicator));
1284
+ }
1285
+
1053
1286
  // src/cost/client.js
1054
- var import_react5 = require("react");
1287
+ var import_react7 = require("react");
1055
1288
 
1056
1289
  // src/cost/pricing.js
1057
1290
  var PROJECTION_KEY = "dshTomCost";
@@ -1089,7 +1322,7 @@ function formatMoney(units) {
1089
1322
  }
1090
1323
 
1091
1324
  // src/cost/panel.js
1092
- var import_react3 = require("react");
1325
+ var import_react5 = require("react");
1093
1326
  var import_react_dom = require("react-dom");
1094
1327
 
1095
1328
  // src/cost/panel-styles.js
@@ -1165,8 +1398,8 @@ function costRows(totals, daily, day = beijingDay()) {
1165
1398
  ];
1166
1399
  }
1167
1400
  function useBeijingDay() {
1168
- const [day, setDay] = (0, import_react3.useState)(beijingDay);
1169
- (0, import_react3.useEffect)(() => {
1401
+ const [day, setDay] = (0, import_react5.useState)(beijingDay);
1402
+ (0, import_react5.useEffect)(() => {
1170
1403
  let timer;
1171
1404
  const refresh = () => {
1172
1405
  const current = beijingDay();
@@ -1180,7 +1413,7 @@ function useBeijingDay() {
1180
1413
  return day;
1181
1414
  }
1182
1415
  function CostIcon() {
1183
- return (0, import_react3.createElement)(
1416
+ return (0, import_react5.createElement)(
1184
1417
  "svg",
1185
1418
  {
1186
1419
  viewBox: "0 0 24 24",
@@ -1193,18 +1426,18 @@ function CostIcon() {
1193
1426
  strokeLinejoin: "round",
1194
1427
  "aria-hidden": true
1195
1428
  },
1196
- (0, import_react3.createElement)("circle", { cx: 12, cy: 12, r: 9 }),
1197
- (0, import_react3.createElement)("path", { d: "m8.5 7.5 3.5 4 3.5-4M8.5 12h7M8.5 15h7M12 11.5V18" })
1429
+ (0, import_react5.createElement)("circle", { cx: 12, cy: 12, r: 9 }),
1430
+ (0, import_react5.createElement)("path", { d: "m8.5 7.5 3.5 4 3.5-4M8.5 12h7M8.5 15h7M12 11.5V18" })
1198
1431
  );
1199
1432
  }
1200
1433
  function CostPanel({ totals, daily, balance }) {
1201
1434
  const day = useBeijingDay();
1202
1435
  const rows = costRows(totals, daily, day);
1203
- const [open2, setOpen] = (0, import_react3.useState)(false);
1204
- const anchor2 = (0, import_react3.useRef)(null);
1205
- const panel = (0, import_react3.useRef)(null);
1206
- const dialogId = (0, import_react3.useId)();
1207
- (0, import_react3.useLayoutEffect)(() => {
1436
+ const [open2, setOpen] = (0, import_react5.useState)(false);
1437
+ const anchor2 = (0, import_react5.useRef)(null);
1438
+ const panel = (0, import_react5.useRef)(null);
1439
+ const dialogId = (0, import_react5.useId)();
1440
+ (0, import_react5.useLayoutEffect)(() => {
1208
1441
  if (!open2) return;
1209
1442
  const element = panel.current;
1210
1443
  const document2 = element.ownerDocument;
@@ -1243,11 +1476,11 @@ function CostPanel({ totals, daily, balance }) {
1243
1476
  document2.removeEventListener("keydown", dismissEscape);
1244
1477
  };
1245
1478
  }, [open2]);
1246
- return (0, import_react3.createElement)(
1479
+ return (0, import_react5.createElement)(
1247
1480
  "span",
1248
1481
  { className: "dsh-tom-cost-anchor", "data-composer-stat": "cost" },
1249
- (0, import_react3.createElement)("style", { "data-dsh-tom-cost-styles": "" }, panelStyles),
1250
- (0, import_react3.createElement)("button", {
1482
+ (0, import_react5.createElement)("style", { "data-dsh-tom-cost-styles": "" }, panelStyles),
1483
+ (0, import_react5.createElement)("button", {
1251
1484
  ref: anchor2,
1252
1485
  type: "button",
1253
1486
  className: "dsh-tom-cost-pill",
@@ -1257,8 +1490,8 @@ function CostPanel({ totals, daily, balance }) {
1257
1490
  "aria-controls": open2 ? dialogId : void 0,
1258
1491
  "aria-label": `\u4F1A\u8BDD\u7D2F\u8BA1\u8D39\u7528 ${rows[1][1]}`,
1259
1492
  onClick: () => setOpen(!open2)
1260
- }, (0, import_react3.createElement)(CostIcon), (0, import_react3.createElement)("span", { className: "dsh-tom-cost-label" }, rows[1][1])),
1261
- open2 && (0, import_react_dom.createPortal)((0, import_react3.createElement)(
1493
+ }, (0, import_react5.createElement)(CostIcon), (0, import_react5.createElement)("span", { className: "dsh-tom-cost-label" }, rows[1][1])),
1494
+ open2 && (0, import_react_dom.createPortal)((0, import_react5.createElement)(
1262
1495
  "div",
1263
1496
  {
1264
1497
  ref: panel,
@@ -1268,11 +1501,11 @@ function CostPanel({ totals, daily, balance }) {
1268
1501
  className: "dsh-tom-cost-panel",
1269
1502
  style: { visibility: "hidden", left: 0, top: 0 }
1270
1503
  },
1271
- (0, import_react3.createElement)("div", { className: "dsh-tom-cost-title" }, (0, import_react3.createElement)(CostIcon), "\u8D39\u7528\u660E\u7EC6"),
1272
- (0, import_react3.createElement)("div", { className: "dsh-tom-cost-rule", "aria-hidden": true }),
1273
- (0, import_react3.createElement)("dl", { className: "dsh-tom-cost-details" }, rows.flatMap(([label, value]) => [
1274
- (0, import_react3.createElement)("dt", { key: label }, label),
1275
- (0, import_react3.createElement)("dd", { key: `${label}-value` }, value)
1504
+ (0, import_react5.createElement)("div", { className: "dsh-tom-cost-title" }, (0, import_react5.createElement)(CostIcon), "\u8D39\u7528\u660E\u7EC6"),
1505
+ (0, import_react5.createElement)("div", { className: "dsh-tom-cost-rule", "aria-hidden": true }),
1506
+ (0, import_react5.createElement)("dl", { className: "dsh-tom-cost-details" }, rows.flatMap(([label, value]) => [
1507
+ (0, import_react5.createElement)("dt", { key: label }, label),
1508
+ (0, import_react5.createElement)("dd", { key: `${label}-value` }, value)
1276
1509
  ]))
1277
1510
  ), anchor2.current.ownerDocument.body)
1278
1511
  );
@@ -21219,7 +21452,7 @@ function createBalanceStore(readBalance) {
21219
21452
  }
21220
21453
 
21221
21454
  // src/cost/settings.js
21222
- var import_react4 = require("react");
21455
+ var import_react6 = require("react");
21223
21456
 
21224
21457
  // src/cost/settings-styles.js
21225
21458
  var settingsStyles = `
@@ -21275,62 +21508,62 @@ function usageSummary(row) {
21275
21508
  function CostSettings({ useCostReport }) {
21276
21509
  const report = useCostReport((value2) => value2);
21277
21510
  const day = useBeijingDay();
21278
- const [hideZero, setHideZero] = (0, import_react4.useState)(true);
21511
+ const [hideZero, setHideZero] = (0, import_react6.useState)(true);
21279
21512
  const value = report.status === "ready" && report.value.day === day ? report.value : void 0;
21280
21513
  const status = report.status === "error" ? "\u8BFB\u53D6\u5931\u8D25" : "\u52A0\u8F7D\u4E2D\u2026";
21281
21514
  const rows = value ? visibleHistory(value.history, hideZero) : [];
21282
21515
  const summaries = [["today", "\u4ECA\u65E5\u8D39\u7528"], ["week", "\u672C\u5468\u8D39\u7528"], ["month", "\u672C\u6708\u8D39\u7528"]];
21283
- return (0, import_react4.createElement)(
21516
+ return (0, import_react6.createElement)(
21284
21517
  "section",
21285
21518
  { className: "dsh-tom-cost-settings", "aria-label": "\u8D39\u7528" },
21286
- (0, import_react4.createElement)("style", null, settingsStyles),
21287
- (0, import_react4.createElement)("div", { className: "dsh-tom-cost-cards" }, summaries.map(([key, label]) => {
21519
+ (0, import_react6.createElement)("style", null, settingsStyles),
21520
+ (0, import_react6.createElement)("div", { className: "dsh-tom-cost-cards" }, summaries.map(([key, label]) => {
21288
21521
  const row = value?.[key];
21289
- return (0, import_react4.createElement)(
21522
+ return (0, import_react6.createElement)(
21290
21523
  "div",
21291
21524
  { key, className: "dsh-tom-cost-card" },
21292
- (0, import_react4.createElement)("div", { className: "dsh-tom-cost-card-label" }, label),
21293
- (0, import_react4.createElement)("div", { className: "dsh-tom-cost-amount" }, row ? costValue(row.totals, row.failedSessions) : status),
21294
- (0, import_react4.createElement)("div", { className: "dsh-tom-cost-usage" }, usageSummary(row))
21525
+ (0, import_react6.createElement)("div", { className: "dsh-tom-cost-card-label" }, label),
21526
+ (0, import_react6.createElement)("div", { className: "dsh-tom-cost-amount" }, row ? costValue(row.totals, row.failedSessions) : status),
21527
+ (0, import_react6.createElement)("div", { className: "dsh-tom-cost-usage" }, usageSummary(row))
21295
21528
  );
21296
21529
  })),
21297
- (0, import_react4.createElement)(
21530
+ (0, import_react6.createElement)(
21298
21531
  "div",
21299
21532
  { className: "dsh-tom-cost-history-heading" },
21300
- (0, import_react4.createElement)("h3", null, "\u5386\u53F2\u8BB0\u5F55", (0, import_react4.createElement)("span", null, "\u6700\u8FD1 30 \u5929")),
21301
- (0, import_react4.createElement)(
21533
+ (0, import_react6.createElement)("h3", null, "\u5386\u53F2\u8BB0\u5F55", (0, import_react6.createElement)("span", null, "\u6700\u8FD1 30 \u5929")),
21534
+ (0, import_react6.createElement)(
21302
21535
  "label",
21303
21536
  { className: "dsh-tom-cost-filter" },
21304
- (0, import_react4.createElement)("input", { type: "checkbox", checked: hideZero, onChange: (event) => setHideZero(event.target.checked) }),
21537
+ (0, import_react6.createElement)("input", { type: "checkbox", checked: hideZero, onChange: (event) => setHideZero(event.target.checked) }),
21305
21538
  "\u8FC7\u6EE4\u96F6\u8D39\u7528\u65E5\u671F"
21306
21539
  )
21307
21540
  ),
21308
- (0, import_react4.createElement)(
21541
+ (0, import_react6.createElement)(
21309
21542
  "div",
21310
21543
  { className: "dsh-tom-cost-table-wrap", tabIndex: 0, role: "region", "aria-label": "\u6BCF\u65E5\u8D39\u7528\u5386\u53F2\u8BB0\u5F55" },
21311
- (0, import_react4.createElement)(
21544
+ (0, import_react6.createElement)(
21312
21545
  "table",
21313
21546
  { className: "dsh-tom-cost-table" },
21314
- (0, import_react4.createElement)("thead", null, (0, import_react4.createElement)("tr", null, ["\u65E5\u671F", "\u8C03\u7528", "\u8F93\u5165 tok", "\u7F13\u5B58 tok", "\u8F93\u51FA tok", "\u8D39\u7528"].map((label) => (0, import_react4.createElement)("th", { key: label, scope: "col" }, label)))),
21315
- (0, import_react4.createElement)("tbody", null, value && rows.length ? rows.map((row) => {
21547
+ (0, import_react6.createElement)("thead", null, (0, import_react6.createElement)("tr", null, ["\u65E5\u671F", "\u8C03\u7528", "\u8F93\u5165 tok", "\u7F13\u5B58 tok", "\u8F93\u51FA tok", "\u8D39\u7528"].map((label) => (0, import_react6.createElement)("th", { key: label, scope: "col" }, label)))),
21548
+ (0, import_react6.createElement)("tbody", null, value && rows.length ? rows.map((row) => {
21316
21549
  const totals = row.totals;
21317
21550
  const unavailable = row.failedSessions > 0 && calls(totals) === 0;
21318
- return (0, import_react4.createElement)(
21551
+ return (0, import_react6.createElement)(
21319
21552
  "tr",
21320
21553
  { key: row.day },
21321
- (0, import_react4.createElement)("td", null, row.day),
21322
- (0, import_react4.createElement)("td", null, unavailable ? "\u2014" : calls(totals).toLocaleString("zh-CN")),
21323
- ...["inputTokens", "cacheTokens", "outputTokens"].map((key) => (0, import_react4.createElement)("td", {
21554
+ (0, import_react6.createElement)("td", null, row.day),
21555
+ (0, import_react6.createElement)("td", null, unavailable ? "\u2014" : calls(totals).toLocaleString("zh-CN")),
21556
+ ...["inputTokens", "cacheTokens", "outputTokens"].map((key) => (0, import_react6.createElement)("td", {
21324
21557
  key,
21325
21558
  title: unavailable ? void 0 : totals[key].toLocaleString("zh-CN")
21326
21559
  }, unavailable ? "\u2014" : formatTokens(totals[key]))),
21327
- (0, import_react4.createElement)("td", null, costValue(totals, row.failedSessions))
21560
+ (0, import_react6.createElement)("td", null, costValue(totals, row.failedSessions))
21328
21561
  );
21329
- }) : (0, import_react4.createElement)("tr", null, (0, import_react4.createElement)("td", { colSpan: 6, className: "dsh-tom-cost-empty", role: "status" }, value ? "\u6700\u8FD1 30 \u5929\u6682\u65E0\u975E\u96F6\u8D39\u7528\u8BB0\u5F55" : status)))
21562
+ }) : (0, import_react6.createElement)("tr", null, (0, import_react6.createElement)("td", { colSpan: 6, className: "dsh-tom-cost-empty", role: "status" }, value ? "\u6700\u8FD1 30 \u5929\u6682\u65E0\u975E\u96F6\u8D39\u7528\u8BB0\u5F55" : status)))
21330
21563
  )
21331
21564
  ),
21332
- (0, import_react4.createElement)("p", { className: "dsh-tom-cost-note" }, "\u5317\u4EAC\u65F6\u95F4 \xB7 \u672C\u5468\u4ECE\u5468\u4E00\u5F00\u59CB \xB7 \u8D39\u7528\u6309\u672C\u673A\u4F1A\u8BDD\u8BB0\u5F55\u4F30\u7B97"),
21333
- value && value.history.some(incomplete) && (0, import_react4.createElement)(
21565
+ (0, import_react6.createElement)("p", { className: "dsh-tom-cost-note" }, "\u5317\u4EAC\u65F6\u95F4 \xB7 \u672C\u5468\u4ECE\u5468\u4E00\u5F00\u59CB \xB7 \u8D39\u7528\u6309\u672C\u673A\u4F1A\u8BDD\u8BB0\u5F55\u4F30\u7B97"),
21566
+ value && value.history.some(incomplete) && (0, import_react6.createElement)(
21334
21567
  "p",
21335
21568
  { className: "dsh-tom-cost-note", role: "status" },
21336
21569
  "\u90E8\u5206\u8BB0\u5F55\u65E0\u6CD5\u8BFB\u53D6\u6216\u8BA1\u4EF7\uFF0C\u76F8\u5173\u91D1\u989D\u53EF\u80FD\u4E0D\u5B8C\u6574\u3002"
@@ -21344,7 +21577,7 @@ function CostMount({ useProjection, useDailyCost, useAccountBalance, sessionId }
21344
21577
  const totals = useProjection(PROJECTION_KEY);
21345
21578
  const daily = useDailyCost((value) => value);
21346
21579
  const balance = useAccountBalance((value) => value);
21347
- return (0, import_react5.createElement)(CostPanel, { key: sessionId, totals, daily, balance });
21580
+ return (0, import_react7.createElement)(CostPanel, { key: sessionId, totals, daily, balance });
21348
21581
  }
21349
21582
  async function apply(ctx) {
21350
21583
  await ctx.remote.$mount(dailyRemote);
@@ -21380,10 +21613,10 @@ function ProjectHover({ sessionId, useWorkspaces, useSessions }) {
21380
21613
  const cwd = useSessions((state) => state.byId[sessionId]?.cwd);
21381
21614
  const path = workspace?.path || cwd;
21382
21615
  const name = workspace?.title || path?.split(/[\\/]/).filter(Boolean).at(-1) || path || "\u672A\u5F52\u5C5E\u9879\u76EE";
21383
- return (0, import_react6.createElement)(
21616
+ return (0, import_react8.createElement)(
21384
21617
  "div",
21385
21618
  { className: "dsh-css-project", title: path },
21386
- (0, import_react6.createElement)("svg", {
21619
+ (0, import_react8.createElement)("svg", {
21387
21620
  width: 14,
21388
21621
  height: 14,
21389
21622
  viewBox: "0 0 24 24",
@@ -21392,8 +21625,8 @@ function ProjectHover({ sessionId, useWorkspaces, useSessions }) {
21392
21625
  strokeWidth: 1.7,
21393
21626
  strokeLinejoin: "round",
21394
21627
  "aria-hidden": true
21395
- }, (0, import_react6.createElement)("path", { d: "M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Zm0 2h18" })),
21396
- (0, import_react6.createElement)("span", null, name)
21628
+ }, (0, import_react8.createElement)("path", { d: "M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Zm0 2h18" })),
21629
+ (0, import_react8.createElement)("span", null, name)
21397
21630
  );
21398
21631
  }
21399
21632
  function applySidebarWidth(layout) {
@@ -21446,7 +21679,9 @@ async function apply2(ctx) {
21446
21679
  });
21447
21680
  ctx.effect(() => applySidebarWidth(ctx.layout));
21448
21681
  registerCacheHitPrecision(ctx);
21682
+ registerDateGroups(ctx);
21449
21683
  registerProjects(ctx);
21684
+ registerUnread(ctx);
21450
21685
  ctx.slots.inject("sidebar.session.row.hover", () => ctx.slots.register({
21451
21686
  name: "sidebar.session.row.hover",
21452
21687
  id: "dsh-css-project",
package/dist/index.js CHANGED
@@ -20064,10 +20064,33 @@ function apply(ctx) {
20064
20064
  });
20065
20065
  }
20066
20066
 
20067
+ // src/progress.js
20068
+ var PROGRESS_SECTION = "dsh-tom:progress-updates";
20069
+ var instructions = `# \u4EFB\u52A1\u8FDB\u5C55\u64AD\u62A5
20070
+ \u6267\u884C\u9700\u8981\u8C03\u67E5\u3001\u4FEE\u6539\u3001\u9A8C\u8BC1\u6216\u591A\u6B65\u5DE5\u5177\u64CD\u4F5C\u7684\u4EFB\u52A1\u65F6\uFF0C\u4E3B\u52A8\u5411\u7528\u6237\u7B80\u77ED\u6C47\u62A5\u8FDB\u5C55\uFF1A
20071
+ - \u7B2C\u4E00\u6B21\u8C03\u7528\u5DE5\u5177\u524D\uFF0C\u7528\u4E00\u4E24\u53E5\u8BDD\u8BF4\u660E\u63A5\u4E0B\u6765\u51C6\u5907\u505A\u4EC0\u4E48\u3002
20072
+ - \u83B7\u5F97\u5173\u952E\u53D1\u73B0\u3001\u8C03\u6574\u65B9\u5411\u6216\u8FDB\u5165\u9A8C\u8BC1\u9636\u6BB5\u65F6\uFF0C\u7B80\u77ED\u8BF4\u660E\u5DF2\u786E\u8BA4\u7684\u7ED3\u679C\u4EE5\u53CA\u4E0B\u4E00\u6B65\u3002\u8FDE\u7EED\u591A\u8F6E\u5DE5\u5177\u64CD\u4F5C\u65F6\u9002\u65F6\u66F4\u65B0\uFF0C\u4E0D\u8981\u59CB\u7EC8\u6C89\u9ED8\u76F4\u5230\u7ED3\u675F\uFF1B\u65E0\u9700\u6BCF\u6B21\u8C03\u7528\u5DE5\u5177\u90FD\u6C47\u62A5\uFF0C\u4E5F\u4E0D\u8981\u91CD\u590D\u7A7A\u6CDB\u7684\u201C\u6B63\u5728\u5904\u7406\u201D\u3002
20073
+ - \u8FDB\u5C55\u5FC5\u987B\u4F5C\u4E3A\u7528\u6237\u53EF\u89C1\u7684\u666E\u901A\u52A9\u624B\u6B63\u6587\u8F93\u51FA\uFF0C\u4E0D\u8981\u53EA\u653E\u5728\u601D\u8003\u5185\u5BB9\u3001\u5DE5\u5177\u53C2\u6570\u6216\u6700\u7EC8\u56DE\u7B54\u91CC\u3002\u9700\u8981\u7EE7\u7EED\u6267\u884C\u65F6\uFF0C\u5728\u540C\u4E00\u54CD\u5E94\u4E2D\u5148\u8F93\u51FA\u8FDB\u5C55\u6B63\u6587\uFF0C\u518D\u53D1\u8D77\u5B9E\u9645\u5DE5\u5177\u8C03\u7528\uFF1B\u4E0D\u8981\u4EC5\u8BF4\u201C\u63A5\u4E0B\u6765\u4F1A\u505A\u201D\u5C31\u7ED3\u675F\u4EFB\u52A1\u6216\u7B49\u5F85\u786E\u8BA4\u3002
20074
+ - \u53EA\u62A5\u544A\u771F\u5B9E\u89C2\u5BDF\u3001\u5DF2\u7ECF\u5B8C\u6210\u7684\u64CD\u4F5C\u548C\u660E\u786E\u7684\u4E0B\u4E00\u6B65\uFF0C\u4E0D\u8981\u63D0\u524D\u58F0\u79F0\u6210\u529F\uFF0C\u4E0D\u8981\u5C55\u793A\u5185\u90E8\u63A8\u7406\u8FC7\u7A0B\u3002\u9ED8\u8BA4\u4F7F\u7528\u7528\u6237\u7684\u8BED\u8A00\uFF0C\u6BCF\u6B21\u4E00\u4E24\u53E5\u8BDD\u3002
20075
+ - \u7B80\u5355\u95EE\u7B54\u76F4\u63A5\u56DE\u7B54\u3002\u7528\u6237\u660E\u786E\u8981\u6C42\u4E0D\u64AD\u62A5\u3001\u4EC5\u7ED9\u7ED3\u8BBA\u6216\u7279\u5B9A\u8F93\u51FA\u683C\u5F0F\u65F6\uFF0C\u9075\u5FAA\u7528\u6237\u8981\u6C42\u3002
20076
+ - \u4EFB\u52A1\u5B8C\u6210\u540E\u6309\u539F\u6709\u65B9\u5F0F\u7ED9\u51FA\u6700\u7EC8\u56DE\u7B54\uFF0C\u4E0D\u8981\u628A\u4E2D\u9014\u64AD\u62A5\u91CD\u65B0\u62FC\u6210\u4E00\u4EFD\u91CD\u590D\u7684\u603B\u7ED3\u3002`;
20077
+ function registerProgressUpdates(ctx) {
20078
+ ctx.systemPrompt.section({
20079
+ name: PROGRESS_SECTION,
20080
+ order: 10300,
20081
+ interpolate: false,
20082
+ text: ({ agent }) => agent && agent.session.header.origin !== "subagent" ? instructions : ""
20083
+ });
20084
+ }
20085
+
20067
20086
  // index.js
20068
20087
  var name = "dsh-css";
20088
+ function apply2(ctx) {
20089
+ apply(ctx);
20090
+ ctx.inject(["systemPrompt"], registerProgressUpdates);
20091
+ }
20069
20092
  export {
20070
- apply,
20093
+ apply2 as apply,
20071
20094
  inject,
20072
20095
  name
20073
20096
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dsh-tom/dsh-css",
3
- "version": "0.2.1",
4
- "description": "为 DeepSeek Harness 调整深色界面,增加项目管理、会话费用和费用历史统计。",
3
+ "version": "0.2.8",
4
+ "description": "为 DeepSeek Harness 调整深色界面,增加项目管理、费用统计和任务进展播报。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "exports": {
@@ -43,7 +43,7 @@
43
43
  "scripts": {
44
44
  "build": "node scripts/build.mjs",
45
45
  "test": "node --test test/*.test.mjs",
46
- "check": "node --check index.js && node --check src/client.js && node --check src/projects.js && node --check scripts/build.mjs && npm run build && node --check dist/index.js && node --check dist/client.js && npm test",
46
+ "check": "node --check index.js && node --check src/client.js && node --check src/projects.js && node --check src/progress.js && node --check scripts/build.mjs && npm run build && node --check dist/index.js && node --check dist/client.js && npm test",
47
47
  "prepack": "npm run check"
48
48
  },
49
49
  "license": "MIT",
@@ -65,6 +65,7 @@
65
65
  "@deepseek-ai/dsh-typert-registry": "0.2.1-rc.2",
66
66
  "@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.2",
67
67
  "@deepseek-ai/dsh-session-projection": "0.2.1-rc.2",
68
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
68
69
  "esbuild": "0.28.2",
69
70
  "jsdom": "^27.0.0",
70
71
  "react": "18.3.1",
package/theme.css CHANGED
@@ -1,3 +1,23 @@
1
+ .dsh-css-unread-dot {
2
+ display: inline-block;
3
+ flex: none;
4
+ width: 8px;
5
+ height: 8px;
6
+ border-radius: 50%;
7
+ background: var(--dsw-alias-label-secondary, #aaa);
8
+ }
9
+
10
+ /* 会话操作出现时隐藏未读提示;项目树保留左侧图标位置,避免标题跳动。 */
11
+ .hIlkoa_sessionRow:is(:hover, .hIlkoa_menuOpen) > .hIlkoa_slot .dsh-css-unread-dot {
12
+ visibility: hidden;
13
+ }
14
+
15
+ /* 菜单关闭可能残留宿主的滚动和渐隐状态;非悬停时从标题开头正常显示。 */
16
+ .hIlkoa_sessionRow:not(:hover):not(.hIlkoa_menuOpen) > .hIlkoa_title {
17
+ overflow: clip;
18
+ mask-image: none;
19
+ }
20
+
1
21
  /* 深色模式配色,保留内置文字、边框和语法高亮颜色。 */
2
22
  html body[data-ds-dark-theme] {
3
23
  /* 区域 1:左侧会话列表;Windows 标题栏在下方单独设置。原 RGB(27, 27, 28)。 */
@@ -166,9 +186,10 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
166
186
  padding: 44px 0 0 12px;
167
187
  }
168
188
 
169
- /* 平铺会话标题与“会话”左对齐,状态图标放到右侧。 */
189
+ /* 行背景向左延伸 4px,留足标题内边距并与“会话”保持左对齐。 */
170
190
  & ._9lTDKa_flatList .hIlkoa_sessionRow {
171
- padding-inline-start: 4px;
191
+ margin-inline-start: -4px;
192
+ padding-inline-start: 8px;
172
193
 
173
194
  & > .hIlkoa_title {
174
195
  margin-left: 0;
@@ -178,7 +199,8 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
178
199
  order: 1;
179
200
  }
180
201
 
181
- & > .hIlkoa_slot:empty {
202
+ & > .hIlkoa_slot:empty,
203
+ &:is(:hover, .hIlkoa_menuOpen) > .hIlkoa_slot:has(.dsh-css-unread-dot) {
182
204
  display: none;
183
205
  }
184
206
  }
@@ -255,6 +277,15 @@ html[data-windows-titlebar] body[data-ds-dark-theme] ._2H3hWW_root:not(._2H3hWW_
255
277
  }
256
278
  }
257
279
 
280
+ .dsh-css-date-heading {
281
+ padding: 16px 4px 6px;
282
+ color: var(--dsw-alias-label-tertiary);
283
+ font-size: 12px;
284
+ line-height: 18px;
285
+
286
+ &:first-child { padding-top: 6px; }
287
+ }
288
+
258
289
  /* 悬浮卡片由宿主通过 portal 挂在侧栏外。 */
259
290
  .dsh-css-project {
260
291
  display: none;