@botharness/ui 1.2.0 → 1.2.1

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/lib/client.js CHANGED
@@ -11164,6 +11164,10 @@ window.__ModuleLoader__.load({
11164
11164
  "message.code.copy": "复制代码",
11165
11165
  "message.code.copied": "已复制",
11166
11166
  "message.footnotes": "脚注",
11167
+ "message.audio.play": "播放音频",
11168
+ "message.audio.pause": "暂停音频",
11169
+ "message.audio.player": "音频播放器",
11170
+ "message.audio.seek": "拖动以快进或后退",
11167
11171
  "message.locate": "定位这条消息",
11168
11172
  "message.reply": "回复",
11169
11173
  "message.replyingTo": "回复 {author}",
@@ -13446,6 +13450,10 @@ My relationship with you, established lore and topics to avoid: [fill in].
13446
13450
  "message.code.copy": "Copy code",
13447
13451
  "message.code.copied": "Copied",
13448
13452
  "message.footnotes": "Footnotes",
13453
+ "message.audio.play": "Play audio",
13454
+ "message.audio.pause": "Pause audio",
13455
+ "message.audio.player": "Audio player",
13456
+ "message.audio.seek": "Seek",
13449
13457
  "message.locate": "Locate message",
13450
13458
  "message.reply": "Reply",
13451
13459
  "message.replyingTo": "Replying to {author}",
@@ -17272,6 +17280,49 @@ My relationship with you, established lore and topics to avoid: [fill in].
17272
17280
  });
17273
17281
  }
17274
17282
  //#endregion
17283
+ //#region packages/client/src/client/group-avatar-stack.tsx
17284
+ const MAX_STACKED_FACES = 4;
17285
+ const FACE_SCALE = [
17286
+ .64,
17287
+ .6,
17288
+ .43
17289
+ ];
17290
+ function groupAvatarStackBots(members, bots) {
17291
+ const faces = [];
17292
+ for (const slug of members) {
17293
+ const bot = bots.get(slug);
17294
+ if (bot !== void 0) faces.push(bot);
17295
+ if (faces.length === MAX_STACKED_FACES) break;
17296
+ }
17297
+ return faces;
17298
+ }
17299
+ function GroupChannelIcon({ channel, bots, className, groupClassName, size, hashSize, t }) {
17300
+ const faces = channel.avatar ? [] : groupAvatarStackBots(channel.members, bots);
17301
+ const tile = channel.avatar !== void 0 || faces.length > 0;
17302
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17303
+ className: tile ? `${className} ${groupClassName}` : className,
17304
+ "aria-hidden": "true",
17305
+ children: channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
17306
+ className: "bh-group-avatar-image",
17307
+ src: channel.avatar,
17308
+ alt: ""
17309
+ }) : faces.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashIcon, { size: hashSize }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17310
+ className: "bh-group-avatar-stack",
17311
+ "data-count": faces.length,
17312
+ children: faces.map((bot) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
17313
+ t,
17314
+ personaBotId: bot.slug,
17315
+ name: bot.displayName,
17316
+ src: bot.avatar,
17317
+ appearance: bot.appearance,
17318
+ size: Math.round(size * FACE_SCALE[Math.min(faces.length, 3) - 1]),
17319
+ indicator: false,
17320
+ still: true
17321
+ }, bot.slug))
17322
+ })
17323
+ });
17324
+ }
17325
+ //#endregion
17275
17326
  //#region packages/client/src/client/inbox-icon.tsx
17276
17327
  function InboxIcon({ size = 18 }) {
17277
17328
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
@@ -20448,7 +20499,7 @@ My relationship with you, established lore and topics to avoid: [fill in].
20448
20499
  })
20449
20500
  });
20450
20501
  }
20451
- function ChannelRow({ channel, preview, selected, multiSelected, onActivate, shortcut, showShortcutHints, drag, onMenu, onPinDragStart, onPinDragEnd, t }) {
20502
+ function ChannelRow({ channel, bots, preview, selected, multiSelected, onActivate, shortcut, showShortcutHints, drag, onMenu, onPinDragStart, onPinDragEnd, t }) {
20452
20503
  const marker = drag?.marker ?? null;
20453
20504
  const markerClass = marker === "before" ? " bh-drop-before" : marker === "after" ? " bh-drop-after" : "";
20454
20505
  const sourceClass = drag?.source === true ? " bh-drag-source" : "";
@@ -20502,14 +20553,14 @@ My relationship with you, established lore and topics to avoid: [fill in].
20502
20553
  });
20503
20554
  },
20504
20555
  children: [
20505
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
20506
- className: `bh-channel-slot${channel.avatar ? " bh-group-channel-slot" : ""}`,
20507
- "aria-hidden": "true",
20508
- children: channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
20509
- className: "bh-group-avatar-image",
20510
- src: channel.avatar,
20511
- alt: ""
20512
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashIcon, { size: 18 })
20556
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelIcon, {
20557
+ channel,
20558
+ bots,
20559
+ className: "bh-channel-slot",
20560
+ groupClassName: "bh-group-channel-slot",
20561
+ size: 34,
20562
+ hashSize: 18,
20563
+ t
20513
20564
  }),
20514
20565
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
20515
20566
  className: "bh-body",
@@ -20532,7 +20583,7 @@ My relationship with you, established lore and topics to avoid: [fill in].
20532
20583
  ]
20533
20584
  });
20534
20585
  }
20535
- function RailChannel({ channel, bot, activity, selected, shortcut, showShortcutHints, summary, actions, t }) {
20586
+ function RailChannel({ channel, bots, bot, activity, selected, shortcut, showShortcutHints, summary, actions, t }) {
20536
20587
  const title = bot?.displayName ?? channel.name;
20537
20588
  const open = () => {
20538
20589
  bot === void 0 ? actions.openChannel(channel.id) : actions.openBot(bot.slug);
@@ -20550,14 +20601,14 @@ My relationship with you, established lore and topics to avoid: [fill in].
20550
20601
  "aria-current": selected ? "page" : void 0,
20551
20602
  "aria-keyshortcuts": shortcut,
20552
20603
  onClick: open,
20553
- children: [bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
20554
- className: `bh-rail-channel-icon${channel.avatar ? " bh-group-channel-icon" : ""}`,
20555
- "aria-hidden": "true",
20556
- children: channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
20557
- className: "bh-group-avatar-image",
20558
- src: channel.avatar,
20559
- alt: ""
20560
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashIcon, { size: 18 })
20604
+ children: [bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelIcon, {
20605
+ channel,
20606
+ bots,
20607
+ className: "bh-rail-channel-icon",
20608
+ groupClassName: "bh-group-channel-icon",
20609
+ size: 32,
20610
+ hashSize: 18,
20611
+ t
20561
20612
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
20562
20613
  t,
20563
20614
  personaBotId: bot.slug,
@@ -20579,14 +20630,14 @@ My relationship with you, established lore and topics to avoid: [fill in].
20579
20630
  children: [
20580
20631
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
20581
20632
  className: "bh-rail-preview-head",
20582
- children: [bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
20583
- className: `bh-rail-preview-icon${channel.avatar ? " bh-group-channel-icon" : ""}`,
20584
- "aria-hidden": "true",
20585
- children: channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
20586
- className: "bh-group-avatar-image",
20587
- src: channel.avatar,
20588
- alt: ""
20589
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashIcon, { size: 16 })
20633
+ children: [bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelIcon, {
20634
+ channel,
20635
+ bots,
20636
+ className: "bh-rail-preview-icon",
20637
+ groupClassName: "bh-group-channel-icon",
20638
+ size: 24,
20639
+ hashSize: 16,
20640
+ t
20590
20641
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
20591
20642
  t,
20592
20643
  personaBotId: bot.slug,
@@ -21159,6 +21210,7 @@ My relationship with you, established lore and topics to avoid: [fill in].
21159
21210
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelRow, {
21160
21211
  t,
21161
21212
  channel,
21213
+ bots: botBySlug,
21162
21214
  preview,
21163
21215
  selected: selectedChannel === channel.id,
21164
21216
  multiSelected: selectedChannelSet.has(channel.id),
@@ -21176,6 +21228,7 @@ My relationship with you, established lore and topics to avoid: [fill in].
21176
21228
  const bot = channel.botSlug === void 0 ? void 0 : botBySlug.get(channel.botSlug);
21177
21229
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RailChannel, {
21178
21230
  channel,
21231
+ bots: botBySlug,
21179
21232
  bot,
21180
21233
  activity: bot === void 0 ? void 0 : personaBotActivity(state, bot),
21181
21234
  selected: selectedChannel === channel.id || bot !== void 0 && selectedBot === bot.slug,
@@ -21548,14 +21601,14 @@ My relationship with you, established lore and topics to avoid: [fill in].
21548
21601
  });
21549
21602
  },
21550
21603
  children: [
21551
- bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
21552
- className: `bh-pinned-channel-icon${channel.avatar ? " bh-group-pinned-channel-icon" : ""}`,
21553
- "aria-hidden": "true",
21554
- children: channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
21555
- className: "bh-group-avatar-image",
21556
- src: channel.avatar,
21557
- alt: ""
21558
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashIcon, { size: 24 })
21604
+ bot === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelIcon, {
21605
+ channel,
21606
+ bots: botBySlug,
21607
+ className: "bh-pinned-channel-icon",
21608
+ groupClassName: "bh-group-pinned-channel-icon",
21609
+ size: 54,
21610
+ hashSize: 24,
21611
+ t
21559
21612
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
21560
21613
  t,
21561
21614
  personaBotId: bot.slug,
@@ -29571,6 +29624,47 @@ My relationship with you, established lore and topics to avoid: [fill in].
29571
29624
  }
29572
29625
  };
29573
29626
  //#endregion
29627
+ //#region packages/client/src/client/group-channel-header.tsx
29628
+ function GroupChannelHeader({ channel, title, bots, expanded, t, onOpenActivity, onToggleProfile }) {
29629
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
29630
+ className: "bh-channel-island bh-group-channel-header",
29631
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
29632
+ label: t("groupProfile.liveActivity"),
29633
+ side: "bottom",
29634
+ portal: true,
29635
+ delayMs: 350,
29636
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
29637
+ type: "button",
29638
+ className: "bh-group-channel-avatar",
29639
+ "aria-label": t("groupProfile.liveActivity"),
29640
+ "aria-haspopup": "dialog",
29641
+ "aria-expanded": expanded,
29642
+ onClick: onOpenActivity,
29643
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelIcon, {
29644
+ channel,
29645
+ bots,
29646
+ className: "bh-channel-slot",
29647
+ groupClassName: "bh-group-channel-slot",
29648
+ size: 34,
29649
+ hashSize: 18,
29650
+ t
29651
+ })
29652
+ })
29653
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
29654
+ type: "button",
29655
+ className: "bh-group-channel-name",
29656
+ "aria-haspopup": "dialog",
29657
+ "aria-expanded": expanded,
29658
+ "aria-label": t("groupProfile.openAvatar", { name: channel.name }),
29659
+ onClick: onToggleProfile,
29660
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
29661
+ className: "bh-title",
29662
+ children: title
29663
+ })
29664
+ })]
29665
+ });
29666
+ }
29667
+ //#endregion
29574
29668
  //#region packages/client/src/client/companion-motion.ts
29575
29669
  var CompanionMotion = class {
29576
29670
  point = {
@@ -30403,68 +30497,6 @@ My relationship with you, established lore and topics to avoid: [fill in].
30403
30497
  }, `${state.capacity.layers}:${state.capacity.retention}`);
30404
30498
  }
30405
30499
  //#endregion
30406
- //#region packages/client/src/client/group-channel-header.tsx
30407
- function GroupChannelHeader({ channel, title, members, expanded, t, onOpenActivity, onToggleProfile, companion }) {
30408
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
30409
- className: "bh-channel-island bh-group-channel-header",
30410
- children: [
30411
- channel.avatar ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
30412
- className: "bh-group-avatar-image bh-group-avatar-topbar",
30413
- src: channel.avatar,
30414
- alt: ""
30415
- }) : members.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
30416
- className: "bh-channel-mark bh-channel-mark-sm",
30417
- "aria-hidden": "true",
30418
- children: "#"
30419
- }) : null,
30420
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotFacepile, {
30421
- items: members,
30422
- indicator: false,
30423
- size: 22,
30424
- t,
30425
- renderAvatar: (item, avatar) => {
30426
- const label = item.name + " · " + personaBotPresentationSummary(item.state ?? "idle", item.activity, item.attention, t);
30427
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
30428
- className: "bh-companion-chip",
30429
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
30430
- label,
30431
- side: "bottom",
30432
- portal: true,
30433
- delayMs: 350,
30434
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
30435
- type: "button",
30436
- className: "bh-avatar-facepile-button",
30437
- "aria-label": label,
30438
- "aria-haspopup": "dialog",
30439
- "aria-expanded": expanded,
30440
- onClick: onOpenActivity,
30441
- children: avatar
30442
- })
30443
- }), companion ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompanionPin, {
30444
- companion,
30445
- botId: item.personaBotId,
30446
- name: item.name,
30447
- t
30448
- }) : null]
30449
- }, item.personaBotId);
30450
- }
30451
- }),
30452
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
30453
- type: "button",
30454
- className: "bh-group-channel-name",
30455
- "aria-haspopup": "dialog",
30456
- "aria-expanded": expanded,
30457
- "aria-label": t("groupProfile.openAvatar", { name: channel.name }),
30458
- onClick: onToggleProfile,
30459
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
30460
- className: "bh-title",
30461
- children: title
30462
- })
30463
- })
30464
- ]
30465
- });
30466
- }
30467
- //#endregion
30468
30500
  //#region packages/client/src/client/group-activity-rows.tsx
30469
30501
  function GroupActivityRows({ items, t }) {
30470
30502
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: items.map((item) => {
@@ -32004,6 +32036,183 @@ My relationship with you, established lore and topics to avoid: [fill in].
32004
32036
  }) });
32005
32037
  }
32006
32038
  //#endregion
32039
+ //#region packages/client/src/client/message-audio.tsx
32040
+ const PLAY_EVENT = "bh-message-audio-play";
32041
+ const SPECTRUM_BARS = 48;
32042
+ function formatAudioTime(seconds) {
32043
+ if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
32044
+ const total = Math.floor(seconds);
32045
+ return `${Math.floor(total / 60)}:${String(total % 60).padStart(2, "0")}`;
32046
+ }
32047
+ function spectrumBars(name, size) {
32048
+ let seed = 2166136261;
32049
+ const key = `${name}:${size}`;
32050
+ for (let index = 0; index < key.length; index += 1) {
32051
+ seed ^= key.charCodeAt(index);
32052
+ seed = Math.imul(seed, 16777619);
32053
+ }
32054
+ const bars = [];
32055
+ let state = seed >>> 0;
32056
+ for (let index = 0; index < SPECTRUM_BARS; index += 1) {
32057
+ state = Math.imul(state, 1664525) + 1013904223 >>> 0;
32058
+ bars.push(.2 + state % 100 / 100 * .8);
32059
+ }
32060
+ return bars;
32061
+ }
32062
+ function MessageAudio({ name, size, url, t }) {
32063
+ const instanceId = (0, react.useId)();
32064
+ const audioRef = (0, react.useRef)(null);
32065
+ const spectrumRef = (0, react.useRef)(null);
32066
+ const [playing, setPlaying] = (0, react.useState)(false);
32067
+ const [current, setCurrent] = (0, react.useState)(0);
32068
+ const [duration, setDuration] = (0, react.useState)();
32069
+ const bars = (0, react.useMemo)(() => spectrumBars(name, size), [name, size]);
32070
+ const pauseMount = useMountedResource((node) => {
32071
+ const pauseOthers = (event) => {
32072
+ if (node.isConnected && event.detail !== instanceId) audioRef.current?.pause();
32073
+ };
32074
+ window.addEventListener(PLAY_EVENT, pauseOthers);
32075
+ return () => window.removeEventListener(PLAY_EVENT, pauseOthers);
32076
+ }, [instanceId]);
32077
+ const toggle = () => {
32078
+ const audio = audioRef.current;
32079
+ if (audio === null || url === void 0) return;
32080
+ if (audio.paused) {
32081
+ window.dispatchEvent(new CustomEvent(PLAY_EVENT, { detail: instanceId }));
32082
+ audio.play().catch(() => setPlaying(false));
32083
+ } else audio.pause();
32084
+ };
32085
+ const seek = (value) => {
32086
+ const audio = audioRef.current;
32087
+ if (audio === null || !Number.isFinite(value)) return;
32088
+ audio.currentTime = value;
32089
+ setCurrent(value);
32090
+ };
32091
+ const ready = url !== void 0 && duration !== void 0 && duration > 0;
32092
+ const seekFromClientX = (clientX) => {
32093
+ const strip = spectrumRef.current;
32094
+ if (strip === null || !ready) return;
32095
+ const rect = strip.getBoundingClientRect();
32096
+ if (rect.width <= 0) return;
32097
+ const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
32098
+ seek(ratio * (duration ?? 0));
32099
+ };
32100
+ const stepSeek = (delta) => {
32101
+ if (!ready) return;
32102
+ seek(Math.min(duration ?? 0, Math.max(0, current + delta)));
32103
+ };
32104
+ const progress = ready ? current / (duration ?? 1) : 0;
32105
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
32106
+ className: "bh-message-audio",
32107
+ ref: pauseMount,
32108
+ children: [
32109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
32110
+ type: "button",
32111
+ className: "bh-message-audio-play",
32112
+ "aria-label": `${playing ? t("message.audio.pause") : t("message.audio.play")}: ${name}`,
32113
+ "aria-pressed": playing,
32114
+ disabled: url === void 0,
32115
+ onClick: toggle,
32116
+ children: playing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPauseOutlineRegular, { size: 16 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutlineRegular, { size: 16 })
32117
+ }),
32118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
32119
+ className: "bh-message-file-copy",
32120
+ children: [
32121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32122
+ className: "bh-message-file-name",
32123
+ title: name,
32124
+ children: name
32125
+ }),
32126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
32127
+ className: "bh-message-audio-meta",
32128
+ children: [
32129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
32130
+ formatAudioTime(current),
32131
+ " /",
32132
+ " ",
32133
+ duration === void 0 ? "--:--" : formatAudioTime(duration)
32134
+ ] }),
32135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32136
+ "aria-hidden": "true",
32137
+ children: "·"
32138
+ }),
32139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(size) })
32140
+ ]
32141
+ }),
32142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
32143
+ ref: spectrumRef,
32144
+ className: "bh-message-audio-spectrum",
32145
+ role: "slider",
32146
+ tabIndex: ready ? 0 : -1,
32147
+ "aria-label": `${t("message.audio.seek")}: ${name}`,
32148
+ "aria-valuemin": 0,
32149
+ "aria-valuemax": Math.round(duration ?? 0),
32150
+ "aria-valuenow": Math.round(current),
32151
+ "aria-valuetext": `${formatAudioTime(current)} / ${duration === void 0 ? "--:--" : formatAudioTime(duration)}`,
32152
+ "aria-disabled": !ready,
32153
+ onPointerDown: (event) => {
32154
+ if (!ready) return;
32155
+ event.currentTarget.setPointerCapture(event.pointerId);
32156
+ seekFromClientX(event.clientX);
32157
+ },
32158
+ onPointerMove: (event) => {
32159
+ if (event.buttons === 1) seekFromClientX(event.clientX);
32160
+ },
32161
+ onKeyDown: (event) => {
32162
+ if (event.key === "ArrowLeft") {
32163
+ event.preventDefault();
32164
+ stepSeek(-5);
32165
+ } else if (event.key === "ArrowRight") {
32166
+ event.preventDefault();
32167
+ stepSeek(5);
32168
+ } else if (event.key === "Home") {
32169
+ event.preventDefault();
32170
+ seek(0);
32171
+ } else if (event.key === "End") {
32172
+ event.preventDefault();
32173
+ if (duration !== void 0) seek(duration);
32174
+ }
32175
+ },
32176
+ children: bars.map((height, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32177
+ className: "bh-message-audio-bar",
32178
+ ...index / bars.length <= progress ? { "data-played": true } : {},
32179
+ style: { height: `${Math.round(height * 100)}%` }
32180
+ }, index))
32181
+ })
32182
+ ]
32183
+ }),
32184
+ url === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32185
+ className: "bh-message-file-divider",
32186
+ "aria-hidden": "true"
32187
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
32188
+ label: t("fileAction.download"),
32189
+ side: "bottom",
32190
+ delayMs: 500,
32191
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32192
+ className: "bh-message-file-download",
32193
+ href: url,
32194
+ download: name,
32195
+ "aria-label": `${t("fileAction.download")}: ${name}`,
32196
+ onClick: (event) => event.stopPropagation(),
32197
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutlineRegular, { size: 16 })
32198
+ })
32199
+ })] }),
32200
+ url === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("audio", {
32201
+ ref: audioRef,
32202
+ className: "bh-message-audio-element",
32203
+ src: url,
32204
+ preload: "metadata",
32205
+ "aria-label": `${t("message.audio.player")}: ${name}`,
32206
+ onLoadedMetadata: (event) => setDuration(event.currentTarget.duration),
32207
+ onTimeUpdate: (event) => setCurrent(event.currentTarget.currentTime),
32208
+ onPlay: () => setPlaying(true),
32209
+ onPause: () => setPlaying(false),
32210
+ onEnded: () => setPlaying(false)
32211
+ })
32212
+ ]
32213
+ });
32214
+ }
32215
+ //#endregion
32007
32216
  //#region packages/client/src/client/message-attachment.tsx
32008
32217
  function MessageAttachment({ attachment: ref, channelId, messageId, actions, t }) {
32009
32218
  const commands = (0, react.useMemo)(() => actions === void 0 ? void 0 : {
@@ -32024,6 +32233,21 @@ My relationship with you, established lore and topics to avoid: [fill in].
32024
32233
  "image/gif",
32025
32234
  "image/webp"
32026
32235
  ].includes(ref.mime);
32236
+ if (ref.mime.startsWith("audio/")) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
32237
+ className: "bh-message-attachment",
32238
+ onContextMenu: id === void 0 ? void 0 : (event) => menu.open(id, event),
32239
+ onKeyDown: id === void 0 ? void 0 : (event) => menu.onKey(id, event),
32240
+ children: [
32241
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageAudio, {
32242
+ name: ref.name,
32243
+ size: ref.size,
32244
+ url,
32245
+ t
32246
+ }),
32247
+ menu.menu,
32248
+ menu.feedback
32249
+ ]
32250
+ });
32027
32251
  const contents = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32028
32252
  className: "bh-message-file-icon",
32029
32253
  "aria-hidden": "true",
@@ -32037,59 +32261,77 @@ My relationship with you, established lore and topics to avoid: [fill in].
32037
32261
  className: "bh-message-file-name",
32038
32262
  title: ref.name,
32039
32263
  children: ref.name
32040
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
32264
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32041
32265
  className: "bh-message-file-size",
32042
- children: [
32043
- "· ",
32044
- Math.max(1, Math.round(ref.size / 1024)),
32045
- " KB"
32046
- ]
32266
+ children: (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(ref.size)
32047
32267
  })]
32048
32268
  })] });
32269
+ const downloadAction = url === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
32270
+ className: "bh-message-file-divider",
32271
+ "aria-hidden": "true"
32272
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
32273
+ label: t("fileAction.download"),
32274
+ side: "bottom",
32275
+ delayMs: 500,
32276
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32277
+ className: "bh-message-file-download",
32278
+ href: url,
32279
+ download: ref.name,
32280
+ "aria-label": `${t("fileAction.download")}: ${ref.name}`,
32281
+ onClick: (event) => event.stopPropagation(),
32282
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutlineRegular, { size: 16 })
32283
+ })
32284
+ })] });
32285
+ const hostMenuButton = id === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
32286
+ label: t("fileAction.menu"),
32287
+ side: "bottom",
32288
+ delayMs: 500,
32289
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
32290
+ type: "button",
32291
+ className: "bh-memory-view-icon-button bh-message-file-more",
32292
+ "aria-label": t("fileAction.menu") + ": " + ref.name,
32293
+ "aria-haspopup": "menu",
32294
+ "aria-expanded": menu.isOpen,
32295
+ disabled: channelId === void 0 || commands === void 0,
32296
+ onClick: (event) => menu.open(id, event),
32297
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutlineRegular, { size: 16 })
32298
+ })
32299
+ });
32049
32300
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
32050
32301
  className: "bh-message-attachment",
32051
32302
  onContextMenu: id === void 0 ? void 0 : (event) => menu.open(id, event),
32052
32303
  onKeyDown: id === void 0 ? void 0 : (event) => menu.onKey(id, event),
32053
32304
  children: [
32054
- image ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32055
- className: "bh-message-image-link",
32056
- href: url,
32057
- target: "_blank",
32058
- rel: "noopener noreferrer",
32059
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
32060
- className: "bh-message-image",
32061
- src: url,
32062
- alt: ref.name,
32063
- loading: "lazy"
32064
- })
32065
- }) : id === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32066
- className: "bh-message-file",
32067
- href: url,
32068
- download: ref.name,
32069
- children: contents
32070
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
32071
- type: "button",
32305
+ image ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
32306
+ className: "bh-message-image-row",
32307
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32308
+ className: "bh-message-image-link",
32309
+ href: url,
32310
+ target: "_blank",
32311
+ rel: "noopener noreferrer",
32312
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
32313
+ className: "bh-message-image",
32314
+ src: url,
32315
+ alt: ref.name,
32316
+ loading: "lazy"
32317
+ })
32318
+ }), hostMenuButton]
32319
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
32072
32320
  className: "bh-message-file",
32073
- "aria-haspopup": "menu",
32074
- "aria-expanded": menu.isOpen,
32075
- disabled: channelId === void 0 || commands === void 0,
32076
- onClick: (event) => menu.open(id, event),
32077
- children: contents
32078
- }),
32079
- id === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
32080
- label: t("fileAction.menu"),
32081
- side: "bottom",
32082
- delayMs: 500,
32083
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
32321
+ children: [id === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
32322
+ className: "bh-message-file-main",
32323
+ href: url,
32324
+ download: ref.name,
32325
+ children: contents
32326
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
32084
32327
  type: "button",
32085
- className: "bh-memory-view-icon-button bh-message-file-more",
32086
- "aria-label": t("fileAction.menu") + ": " + ref.name,
32328
+ className: "bh-message-file-main",
32087
32329
  "aria-haspopup": "menu",
32088
32330
  "aria-expanded": menu.isOpen,
32089
32331
  disabled: channelId === void 0 || commands === void 0,
32090
32332
  onClick: (event) => menu.open(id, event),
32091
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutlineRegular, { size: 16 })
32092
- })
32333
+ children: contents
32334
+ }), downloadAction]
32093
32335
  }),
32094
32336
  menu.menu,
32095
32337
  menu.feedback
@@ -48061,8 +48303,7 @@ My relationship with you, established lore and topics to avoid: [fill in].
48061
48303
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupChannelHeader, {
48062
48304
  channel,
48063
48305
  title,
48064
- members: channelFacepile,
48065
- companion,
48306
+ bots: new Map(state.bots.map((member) => [member.slug, member])),
48066
48307
  expanded: profilePopoverOpen,
48067
48308
  t,
48068
48309
  onOpenActivity: () => setProfilePopoverOpen(true),
@@ -56646,6 +56887,8 @@ html[data-botharness-motion='full'] .bh-skeleton-line {
56646
56887
  }
56647
56888
  .bh-group-channel-name { display: inline-flex; align-items: center; flex: 1; min-width: 0; padding: 0; border: 0; border-radius: 4px; color: inherit; background: transparent; font: inherit; cursor: pointer; }
56648
56889
  .bh-group-channel-name:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: 2px; }
56890
+ .bh-group-channel-avatar { display: inline-flex; flex-shrink: 0; padding: 0; border: 0; border-radius: 7px; color: inherit; background: transparent; cursor: pointer; }
56891
+ .bh-group-channel-avatar:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: 2px; }
56649
56892
  .bh-group-live-activity { container-type: inline-size; min-width: 0; }
56650
56893
  .bh-group-live-activity-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
56651
56894
  .bh-group-activity-chip { display: inline-flex; min-width: 0; max-width: 100%; border-radius: 999px; }
@@ -57791,6 +58034,9 @@ button.bh-profile-heat-cell:focus-visible {
57791
58034
  .bh-message-group-me .bh-bubble-author {
57792
58035
  margin: 0 2px 0 0;
57793
58036
  }
58037
+ .bh-message-group-me .bh-message-identity {
58038
+ flex-direction: row-reverse;
58039
+ }
57794
58040
  .bh-bubble-wrap {
57795
58041
  position: relative;
57796
58042
  display: flex;
@@ -57927,25 +58173,38 @@ button.bh-profile-heat-cell:focus-visible {
57927
58173
 
57928
58174
  /* Files stay part of the same message grouping as their text. */
57929
58175
  .bh-bubble-content { min-width: 0; }
57930
- .bh-message-attachment { position: relative; display: flex; align-items: center; gap: 4px; min-width: 0; }
58176
+ .bh-message-attachment { position: relative; min-width: 0; }
57931
58177
  .bh-message-file-more { flex: none; color: inherit; }
57932
- button.bh-message-file { font: inherit; text-align: left; cursor: pointer; }
57933
- .bh-message-file:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
57934
- .bh-message-attachments { display: grid; gap: 6px; margin-top: 6px; }
58178
+ .bh-message-attachments { display: grid; gap: 8px; margin-top: 8px; }
57935
58179
  .bh-bridge-media-content { white-space: pre-wrap; }
57936
- .bh-bridge-image { max-width: min(100%, 360px); }
58180
+ .bh-bridge-image { max-width: min(100%, 320px); }
57937
58181
  .bh-bridge-image-button { display: block; padding: 0; border: 0; background: transparent; cursor: zoom-in; border-radius: 12px; }
57938
58182
  .bh-bridge-image-button:focus-visible { outline: 2px solid var(--bh-bridge-media-focus); outline-offset: 2px; }
57939
58183
  .bh-bridge-image-state { display: flex; align-items: center; gap: 8px; min-height: 96px; color: var(--bh-bridge-media-muted); }
57940
58184
  .bh-bridge-image-expanded { display: block; max-width: 100%; max-height: 75vh; margin: auto; object-fit: contain; }
57941
- .bh-message-image-link { display: block; max-width: min(100%, 360px); }
58185
+ .bh-message-image-link { display: block; width: fit-content; max-width: 320px; min-width: 0; }
58186
+ .bh-message-image-row { display: flex; align-items: flex-start; gap: 4px; width: fit-content; max-width: 100%; min-width: 0; }
57942
58187
  .bh-message-image { display: block; max-width: 100%; max-height: 320px; border-radius: 12px; object-fit: contain; }
57943
- .bh-message-file { display: inline-flex; align-items: center; gap: 8px; min-width: 0; width: fit-content; max-width: min(100%, 320px); min-height: 42px; padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; background: color-mix(in srgb, currentColor 6%, transparent); color: inherit; text-decoration: none; }
57944
- .bh-bubble-me .bh-message-file { border-color: color-mix(in srgb, currentColor 35%, transparent); }
58188
+ .bh-message-file, .bh-message-audio { display: flex; align-items: center; gap: 10px; min-width: 0; width: fit-content; max-width: min(100%, 320px); min-height: 52px; padding: 8px 8px 8px 12px; border: 0; border-radius: 12px; background: color-mix(in srgb, currentColor 6%, transparent); color: inherit; }
58189
+ .bh-message-file-main { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; text-decoration: none; }
58190
+ button.bh-message-file-main { cursor: pointer; }
58191
+ .bh-message-file-main:focus-visible, .bh-message-file-download:focus-visible, .bh-message-audio-play:focus-visible, .bh-message-audio-spectrum:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
57945
58192
  .bh-message-file-icon { display: grid; place-items: center; flex: none; width: 28px; height: 28px; }
57946
- .bh-message-file-copy { display: flex; align-items: baseline; flex: 1; gap: 4px; min-width: 0; }
57947
- .bh-message-file-name { display: block; overflow: hidden; min-width: 0; white-space: nowrap; text-overflow: ellipsis; }
57948
- .bh-message-file-size { flex: none; color: color-mix(in srgb, currentColor 72%, transparent); font-size: 11px; white-space: nowrap; }
58193
+ .bh-message-file-copy { display: flex; flex-direction: column; flex: 1; gap: 2px; min-width: 0; }
58194
+ .bh-message-file-name { display: block; overflow: hidden; min-width: 0; white-space: nowrap; text-overflow: ellipsis; font-size: 13px; font-weight: 500; line-height: 18px; }
58195
+ .bh-message-file-size { color: color-mix(in srgb, currentColor 72%, transparent); font-size: 11px; line-height: 14px; white-space: nowrap; }
58196
+ .bh-message-file-divider { flex: none; align-self: stretch; width: 1px; margin-block: 2px; background: var(--dsw-alias-border-l2); }
58197
+ .bh-message-file-download { display: grid; place-items: center; flex: none; width: 32px; height: 32px; border-radius: 8px; color: inherit; text-decoration: none; }
58198
+ a.bh-message-file-download:hover, button.bh-message-file-download:hover { background: color-mix(in srgb, currentColor 8%, transparent); }
58199
+ .bh-message-audio-play { display: grid; place-items: center; flex: none; width: 32px; height: 32px; padding: 0; border: 1px solid var(--dsw-alias-border-l2); border-radius: 50%; background: transparent; color: inherit; cursor: pointer; }
58200
+ .bh-message-audio-play:hover:not(:disabled) { background: color-mix(in srgb, currentColor 8%, transparent); }
58201
+ .bh-message-audio-play:disabled { opacity: 0.5; cursor: default; }
58202
+ .bh-message-audio-meta { display: flex; align-items: center; gap: 4px; color: color-mix(in srgb, currentColor 72%, transparent); font-size: 11px; line-height: 14px; white-space: nowrap; }
58203
+ .bh-message-audio-spectrum { display: flex; align-items: center; gap: 2px; width: 100%; min-width: 0; height: 28px; margin-top: 4px; padding: 0; border-radius: 4px; cursor: pointer; touch-action: none; }
58204
+ .bh-message-audio-spectrum[aria-disabled='true'] { cursor: default; }
58205
+ .bh-message-audio-bar { flex: 1 1 0; min-width: 2px; max-width: 4px; border-radius: 2px; background: color-mix(in srgb, currentColor 28%, transparent); }
58206
+ .bh-message-audio-bar[data-played] { background: var(--dsw-alias-state-business-primary); }
58207
+ .bh-message-audio-element { display: none; }
57949
58208
  .bh-bubble-body {
57950
58209
  white-space: pre-wrap;
57951
58210
  word-break: break-word;
@@ -59580,7 +59839,31 @@ html[data-botharness-motion='reduce'] .bh-session-workspace-heading svg {
59580
59839
  border-radius: inherit;
59581
59840
  object-fit: cover;
59582
59841
  }
59583
- .bh-group-avatar-topbar { width: 22px; height: 22px; border-radius: 7px; }
59842
+ .bh-group-avatar-stack {
59843
+ position: relative;
59844
+ display: block;
59845
+ width: 100%;
59846
+ height: 100%;
59847
+ background: var(--dsw-alias-button-elevated-fill);
59848
+ }
59849
+ .bh-group-avatar-stack > .bh-persona-avatar {
59850
+ position: absolute;
59851
+ box-sizing: border-box;
59852
+ overflow: hidden;
59853
+ border-radius: 28%;
59854
+ pointer-events: none;
59855
+ }
59856
+ .bh-group-avatar-stack[data-count='1'] > .bh-persona-avatar { top: 50%; left: 50%; translate: -50% -50%; }
59857
+ .bh-group-avatar-stack[data-count='2'] > .bh-persona-avatar { border: 1.5px solid var(--dsw-alias-button-elevated-fill); }
59858
+ .bh-group-avatar-stack[data-count='2'] > :nth-child(1) { top: 8%; left: 8%; }
59859
+ .bh-group-avatar-stack[data-count='2'] > :nth-child(2) { right: 8%; bottom: 8%; }
59860
+ .bh-group-avatar-stack[data-count='3'] > :nth-child(1),
59861
+ .bh-group-avatar-stack[data-count='4'] > :nth-child(1) { top: 5%; left: 5%; }
59862
+ .bh-group-avatar-stack[data-count='3'] > :nth-child(2),
59863
+ .bh-group-avatar-stack[data-count='4'] > :nth-child(2) { top: 5%; right: 5%; }
59864
+ .bh-group-avatar-stack[data-count='3'] > :nth-child(3) { bottom: 5%; left: 50%; translate: -50% 0; }
59865
+ .bh-group-avatar-stack[data-count='4'] > :nth-child(3) { bottom: 5%; left: 5%; }
59866
+ .bh-group-avatar-stack[data-count='4'] > :nth-child(4) { right: 5%; bottom: 5%; }
59584
59867
  .bh-group-profile-avatar {
59585
59868
  display: inline-flex;
59586
59869
  align-items: center;