dsh-bots 0.2.16 → 0.2.18

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.
Files changed (2) hide show
  1. package/lib/client.js +58 -10
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -284,6 +284,12 @@
284
284
  .dbs-navBody{display:flex;flex-direction:column;min-height:0;flex:1}
285
285
  .dbs-botsBody{overflow-y:auto;scrollbar-gutter:stable;padding-right:var(--dsh-sidebar-inline-padding,8px)}
286
286
  .dbs-navBodyErr{padding:6px 12px;font-size:12px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
287
+ .dbs-secHead{position:relative}
288
+ .dbs-secHead .dbs-rowActions{display:none}
289
+ .dbs-secHead:hover .dbs-rowActions,.dbs-secHead:focus-within .dbs-rowActions{display:inline-flex}
290
+ .dbs-secMenu{position:absolute;top:100%;right:0;z-index:40;min-width:148px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:4px;box-shadow:0 12px 40px rgba(0,0,0,.22);animation:dbs-modal-in .14s ease-out}
291
+ .dbs-secMenuItem{display:flex;align-items:center;gap:8px;width:100%;padding:6px 10px;border-radius:8px;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary);cursor:pointer;background:none;border:none;text-align:left}
292
+ .dbs-secMenuItem:hover{background:var(--dsw-alias-interactive-bg-hover)}
287
293
  .dbs-prow,.dbs-srow{cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding:0 8px;display:flex;box-sizing:border-box}
288
294
  /* Native mirror (dsh-client-ui-sidebar/workspace): header-class rows outdent
289
295
  their label 4px (pl:4 → x=16 from the window edge) while list rows keep
@@ -464,6 +470,7 @@
464
470
  'chat.stop.noop': '当前没有进行中的生成',
465
471
  'media.openFailed': '打开失败,文件可能已移动或被删除',
466
472
  'chat.members.manage': '管理成员',
473
+ 'chat.members.cap': '已达群成员上限 6 人(引擎限制)——请先移除一名成员再添加',
467
474
  'chat.settings': '设置',
468
475
  'chat.settings.title': '会话设置',
469
476
  'chat.settings.name': '名称',
@@ -488,6 +495,8 @@
488
495
  'list.loading': '加载中…',
489
496
  'list.empty': '还没有 Bot,点 + 新建。',
490
497
  'section.groups': '群聊',
498
+ 'list.more': '更多操作',
499
+ 'list.refresh': '刷新列表',
491
500
  'section.singles': '单聊',
492
501
  'chat.group': '群聊 · {n} 名成员',
493
502
  'chat.single': '单聊',
@@ -576,6 +585,7 @@
576
585
  'chat.stop.noop': 'No generation in progress',
577
586
  'media.openFailed': 'Open failed — the file may have moved or been deleted',
578
587
  'chat.members.manage': 'Manage members',
588
+ 'chat.members.cap': 'Group cap is 6 members (engine limit) — remove one before adding',
579
589
  'chat.settings': 'Settings',
580
590
  'chat.settings.title': 'Session settings',
581
591
  'chat.settings.name': 'Name',
@@ -600,6 +610,8 @@
600
610
  'list.loading': 'Loading…',
601
611
  'list.empty': 'No bots yet — use + to create one.',
602
612
  'section.groups': 'Groups',
613
+ 'list.more': 'More actions',
614
+ 'list.refresh': 'Refresh list',
603
615
  'section.singles': 'Direct',
604
616
  'chat.group': 'Group · {n} members',
605
617
  'chat.single': 'Direct',
@@ -789,6 +801,7 @@
789
801
  /** Manage-members dialog: group agentId | null (system modal). */
790
802
  manageMembers: null,
791
803
  settingsAgentId: null,
804
+ sectionMenu: null,
792
805
  /** Bumped on a language switch so module-scope `t` output re-renders. */
793
806
  localeRev: 0,
794
807
  };
@@ -1127,19 +1140,32 @@
1127
1140
  * are: creating the first bot or group never requires a trip to the
1128
1141
  * footer. `addAction` opens the same system-style modal as before.
1129
1142
  */
1130
- function sectionRows(label, list, addAction) {
1143
+ function sectionRows(label, list, addAction, menuKind, menuItems) {
1144
+ const menuOpen = s.sectionMenu === menuKind;
1131
1145
  return e('div', { key: label }, e('div', {
1132
- className: 'dbs-navBodyErr',
1146
+ className: 'dbs-navBodyErr dbs-secHead',
1133
1147
  // Native sectionHeader outdent: label at x=16 (12 sidebar
1134
1148
  // padding + 4), aligned with the group headers. The previous
1135
1149
  // 12px here pushed section labels 8px right of the native
1136
1150
  // baseline, making both sections read horizontally off.
1137
1151
  style: { padding: '4px 8px 2px 4px', display: 'flex', alignItems: 'center' },
1138
- }, e('span', { style: { flex: 1 } }, label), e(Button, {
1152
+ }, e('span', { style: { flex: 1 } }, label),
1153
+ // Native workspace pattern: header actions appear on hover
1154
+ // (.dbs-rowActions is hover-gated in .dbs-secHead CSS).
1155
+ e('div', { className: 'dbs-rowActions', style: { alignItems: 'center', gap: 2 } }, e(Button, {
1139
1156
  variant: 'ghost', size: 'sm', title: addAction.title, 'aria-label': addAction.title,
1140
1157
  icon: Ico('IconPlusOutline16', { size: 14 }),
1141
1158
  onClick: addAction.onClick,
1142
- })), list.map(row));
1159
+ }), e(Button, {
1160
+ variant: 'ghost', size: 'sm', title: t('list.more'), 'aria-label': t('list.more'),
1161
+ icon: Ico('IconEllipsisOutline16', { size: 14 }),
1162
+ onClick: () => patch({ sectionMenu: menuOpen ? null : menuKind }),
1163
+ })), menuOpen
1164
+ ? e(React.Fragment, null, e('div', { style: { position: 'fixed', inset: 0, zIndex: 39 }, onClick: () => patch({ sectionMenu: null }) }), e('div', { className: 'dbs-secMenu', onClick: (ev) => { ev.stopPropagation(); } }, menuItems.map((item) => e('button', {
1165
+ key: item.label, className: 'dbs-secMenuItem', type: 'button',
1166
+ onClick: () => { patch({ sectionMenu: null }); item.onClick(); },
1167
+ }, item.label))))
1168
+ : null), list.map(row));
1143
1169
  }
1144
1170
  // No footer: the gateway status moved to the Bots group-header dot's
1145
1171
  // hover tooltip (0.2.8) — see SidebarNav's StateDot `title`.
@@ -1153,12 +1179,18 @@
1153
1179
  ? sectionRows(t('section.groups'), groups, {
1154
1180
  title: t('group.new'),
1155
1181
  onClick: () => patch({ create: 'group', createName: '', createMembers: {}, createWorking: false, error: null }),
1156
- })
1182
+ }, 'groups', [
1183
+ { label: t('group.new'), onClick: () => patch({ create: 'group', createName: '', createMembers: {}, createWorking: false, error: null }) },
1184
+ { label: t('list.refresh'), onClick: () => { void refreshAgents(); } },
1185
+ ])
1157
1186
  : null, s.agentsLoaded
1158
1187
  ? sectionRows(t('section.singles'), singles, {
1159
1188
  title: t('bot.new'),
1160
1189
  onClick: () => patch({ create: 'bot', createName: '', createDesc: '', createWorking: false, error: null }),
1161
- })
1190
+ }, 'singles', [
1191
+ { label: t('bot.new'), onClick: () => patch({ create: 'bot', createName: '', createDesc: '', createWorking: false, error: null }) },
1192
+ { label: t('list.refresh'), onClick: () => { void refreshAgents(); } },
1193
+ ])
1162
1194
  : null);
1163
1195
  }
1164
1196
  // =========================================================
@@ -2149,8 +2181,24 @@
2149
2181
  return null;
2150
2182
  const singles = s.agents.filter((a) => !a.isGroup && a.isHiddenFromSidebar !== true);
2151
2183
  const pickedIds = picked === null ? [] : Object.keys(picked).filter((k) => picked[k]);
2184
+ // Engine hard cap (sdk-bots agents.ts GROUP_MAX_MEMBERS = 6): the glue
2185
+ // slice(0, 6)-truncates the roster SILENTLY — an over-cap save writes
2186
+ // successfully and just drops whoever ranked past 6. Surface it here
2187
+ // instead of letting a save pretend to work.
2188
+ const MEMBER_CAP = 6;
2189
+ const overCap = pickedIds.length > MEMBER_CAP;
2190
+ function toggle(id) {
2191
+ if (picked === null)
2192
+ return;
2193
+ if (picked[id] !== true && pickedIds.length >= MEMBER_CAP) {
2194
+ setErr(t('chat.members.cap'));
2195
+ return;
2196
+ }
2197
+ setErr(null);
2198
+ setPicked({ ...picked, [id]: !picked[id] });
2199
+ }
2152
2200
  async function submit() {
2153
- if (working || picked === null)
2201
+ if (working || picked === null || overCap)
2154
2202
  return;
2155
2203
  setWorking(true);
2156
2204
  setErr(null);
@@ -2186,16 +2234,16 @@
2186
2234
  key: m.id,
2187
2235
  className: 'dbs-member' + (picked[m.id] ? ' checked' : ''),
2188
2236
  style: { cursor: 'pointer', padding: '4px 6px', borderRadius: 8 },
2189
- onClick: () => setPicked({ ...picked, [m.id]: !picked[m.id] }),
2237
+ onClick: () => toggle(m.id),
2190
2238
  }, e('input', { type: 'checkbox', checked: Boolean(picked[m.id]), readOnly: true }), e(Avatar, { agent: m, size: 18 }), e('span', { className: 'dbs-title' }, m.name))), singles.length > 0
2191
- ? e('div', { className: 'dbs-meta', style: { padding: '6px 6px 0' } }, t('chat.group', { n: pickedIds.length }))
2239
+ ? e('div', { className: 'dbs-meta', style: { padding: '6px 6px 0' } }, t('chat.group', { n: pickedIds.length }) + ' / ' + String(MEMBER_CAP))
2192
2240
  : null), err !== null
2193
2241
  ? e('div', { className: 'dbs-error', onClick: () => { setErr(null); } }, err)
2194
2242
  : null), e('div', { className: 'dbs-modalFooter' }, e(Button, {
2195
2243
  variant: 'ghost', size: 'sm', disabled: working,
2196
2244
  onClick: () => patch({ manageMembers: null }),
2197
2245
  }, t('action.cancel')), e(Button, {
2198
- variant: 'primary', size: 'sm', disabled: picked === null || working,
2246
+ variant: 'primary', size: 'sm', disabled: picked === null || working || overCap,
2199
2247
  onClick: () => void submit(),
2200
2248
  }, working ? t('action.saving') : t('action.save')))));
2201
2249
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bots",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "Multi-bot workbench for DeepSeek Harness: bridges the sdk-bots orchestration gateway (group chats, single bots) into the dsh web shell with official-styled UI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",