@iota-uz/sdk 0.4.23 → 0.4.24

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.
@@ -3303,12 +3303,151 @@ function useBranding() {
3303
3303
  }, [context.extensions?.branding, t]);
3304
3304
  return branding;
3305
3305
  }
3306
- function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
3306
+ function hashString(str) {
3307
+ let hash = 0;
3308
+ for (let i = 0; i < str.length; i++) {
3309
+ const char = str.charCodeAt(i);
3310
+ hash = (hash << 5) - hash + char;
3311
+ hash = hash & hash;
3312
+ }
3313
+ return Math.abs(hash);
3314
+ }
3315
+ var colorPalette = [
3316
+ { bg: "bg-blue-500", text: "text-white" },
3317
+ { bg: "bg-green-500", text: "text-white" },
3318
+ { bg: "bg-purple-500", text: "text-white" },
3319
+ { bg: "bg-pink-500", text: "text-white" },
3320
+ { bg: "bg-indigo-500", text: "text-white" },
3321
+ { bg: "bg-teal-500", text: "text-white" },
3322
+ { bg: "bg-orange-500", text: "text-white" },
3323
+ { bg: "bg-cyan-500", text: "text-white" },
3324
+ { bg: "bg-amber-500", text: "text-white" },
3325
+ { bg: "bg-lime-500", text: "text-white" }
3326
+ ];
3327
+ var sizeClasses = {
3328
+ xs: "w-6 h-6 text-[10px]",
3329
+ sm: "w-8 h-8 text-xs",
3330
+ md: "w-10 h-10 text-sm",
3331
+ lg: "w-12 h-12 text-base"
3332
+ };
3333
+ function UserAvatar({
3334
+ firstName,
3335
+ lastName,
3336
+ initials: providedInitials,
3337
+ size = "md",
3338
+ className = ""
3339
+ }) {
3340
+ const derivedInitials = (() => {
3341
+ const firstChar = firstName?.trim()?.charAt(0) || "";
3342
+ const lastChar = lastName?.trim()?.charAt(0) || "";
3343
+ const combined = `${firstChar}${lastChar}`.trim();
3344
+ return combined || "U";
3345
+ })();
3346
+ const initials = (providedInitials?.trim() || derivedInitials).toUpperCase();
3347
+ const fullName = `${firstName}${lastName}`;
3348
+ const colorIndex = hashString(fullName) % colorPalette.length;
3349
+ const colors = colorPalette[colorIndex];
3350
+ return /* @__PURE__ */ jsxRuntime.jsx(
3351
+ "div",
3352
+ {
3353
+ className: `
3354
+ ${sizeClasses[size]}
3355
+ ${colors.bg}
3356
+ ${colors.text}
3357
+ ${className}
3358
+ rounded-full
3359
+ flex
3360
+ items-center
3361
+ justify-center
3362
+ font-semibold
3363
+ flex-shrink-0
3364
+ select-none
3365
+ `,
3366
+ "aria-label": `${firstName} ${lastName}`,
3367
+ title: `${firstName} ${lastName}`,
3368
+ children: initials
3369
+ }
3370
+ );
3371
+ }
3372
+ var MemoizedUserAvatar = React.memo(UserAvatar);
3373
+ MemoizedUserAvatar.displayName = "UserAvatar";
3374
+ var overlapClasses = {
3375
+ xs: "-ml-1.5",
3376
+ sm: "-ml-2"
3377
+ };
3378
+ var badgeSizeClasses = {
3379
+ xs: "w-6 h-6 text-[10px]",
3380
+ sm: "w-8 h-8 text-xs"
3381
+ };
3382
+ function AvatarStackInner({
3383
+ users,
3384
+ max = 3,
3385
+ size = "sm",
3386
+ onClick,
3387
+ className = ""
3388
+ }) {
3389
+ const visible = users.slice(0, max);
3390
+ const overflow = users.length - max;
3391
+ const interactive = typeof onClick === "function";
3392
+ const overlap = overlapClasses[size];
3393
+ const badgeSize = badgeSizeClasses[size];
3394
+ const handleKeyDown = (e) => {
3395
+ if (interactive && (e.key === "Enter" || e.key === " ")) {
3396
+ e.preventDefault();
3397
+ onClick();
3398
+ }
3399
+ };
3400
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3401
+ "div",
3402
+ {
3403
+ className: `inline-flex items-center ${interactive ? "cursor-pointer transition-opacity hover:opacity-80" : ""} ${className}`,
3404
+ onClick: interactive ? onClick : void 0,
3405
+ onKeyDown: interactive ? handleKeyDown : void 0,
3406
+ role: interactive ? "button" : void 0,
3407
+ tabIndex: interactive ? 0 : void 0,
3408
+ "aria-label": interactive ? `${users.length} members` : void 0,
3409
+ children: [
3410
+ visible.map((user, i) => /* @__PURE__ */ jsxRuntime.jsx(
3411
+ "div",
3412
+ {
3413
+ className: `${i > 0 ? overlap : ""} ring-2 ring-white dark:ring-gray-900 rounded-full`,
3414
+ style: { zIndex: visible.length - i },
3415
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3416
+ MemoizedUserAvatar,
3417
+ {
3418
+ firstName: user.firstName,
3419
+ lastName: user.lastName,
3420
+ initials: user.initials,
3421
+ size
3422
+ }
3423
+ )
3424
+ },
3425
+ `${user.firstName}-${user.lastName}-${i}`
3426
+ )),
3427
+ overflow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
3428
+ "div",
3429
+ {
3430
+ className: `${overlap} ${badgeSize} rounded-full bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 font-medium flex items-center justify-center flex-shrink-0 ring-2 ring-white dark:ring-gray-900`,
3431
+ style: { zIndex: 0 },
3432
+ children: [
3433
+ "+",
3434
+ overflow
3435
+ ]
3436
+ }
3437
+ )
3438
+ ]
3439
+ }
3440
+ );
3441
+ }
3442
+ var AvatarStack = React.memo(AvatarStackInner);
3443
+ AvatarStack.displayName = "AvatarStack";
3444
+ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot, members, onMembersClick }) {
3307
3445
  const { t } = useTranslation();
3308
3446
  const branding = useBranding();
3309
3447
  const BackButton = onBack ? /* @__PURE__ */ jsxRuntime.jsx(
3310
3448
  "button",
3311
3449
  {
3450
+ type: "button",
3312
3451
  onClick: onBack,
3313
3452
  className: "cursor-pointer p-2 hover:bg-gray-100 dark:hover:bg-gray-700 active:bg-gray-200 dark:active:bg-gray-600 rounded-lg transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
3314
3453
  "aria-label": t("BiChat.Chat.GoBack"),
@@ -3327,23 +3466,42 @@ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
3327
3466
  ] }) });
3328
3467
  }
3329
3468
  const resolvedSessionTitle = session.title?.trim() || t("BiChat.Chat.NewChat");
3469
+ const isGroupSession = Boolean(session.isGroup || session.memberCount && session.memberCount > 1);
3470
+ const memberCount = session.memberCount ?? 0;
3471
+ const stackUsers = members && members.length > 0 ? members : [];
3330
3472
  return /* @__PURE__ */ jsxRuntime.jsx("header", { className: "bichat-header border-b border-gray-200 dark:border-gray-700 px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3331
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
3473
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3332
3474
  BackButton,
3333
3475
  Logo,
3334
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-[var(--bichat-text)]", children: resolvedSessionTitle }),
3335
- session.pinned && /* @__PURE__ */ jsxRuntime.jsx(
3336
- "svg",
3337
- {
3338
- className: "w-4 h-4 text-[var(--bichat-primary)]",
3339
- fill: "currentColor",
3340
- viewBox: "0 0 20 20",
3341
- "aria-label": t("BiChat.Chat.Pinned"),
3342
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 2a1 1 0 011 1v1.323l3.954 1.582 1.599-.8a1 1 0 01.894 1.79l-1.233.616 1.738 5.42a1 1 0 01-.285 1.05A3.989 3.989 0 0115 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.715-5.349L11 6.477V16h2a1 1 0 110 2H7a1 1 0 110-2h2V6.477L6.237 7.582l1.715 5.349a1 1 0 01-.285 1.05A3.989 3.989 0 015 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.738-5.42-1.233-.617a1 1 0 01.894-1.788l1.599.799L9 4.323V3a1 1 0 011-1z" })
3343
- }
3344
- )
3476
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
3477
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
3478
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-[var(--bichat-text)] truncate", children: resolvedSessionTitle }),
3479
+ session.pinned && /* @__PURE__ */ jsxRuntime.jsx(
3480
+ "svg",
3481
+ {
3482
+ className: "w-4 h-4 text-[var(--bichat-primary)] flex-shrink-0",
3483
+ fill: "currentColor",
3484
+ viewBox: "0 0 20 20",
3485
+ role: "img",
3486
+ "aria-label": t("BiChat.Chat.Pinned"),
3487
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 2a1 1 0 011 1v1.323l3.954 1.582 1.599-.8a1 1 0 01.894 1.79l-1.233.616 1.738 5.42a1 1 0 01-.285 1.05A3.989 3.989 0 0115 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.715-5.349L11 6.477V16h2a1 1 0 110 2H7a1 1 0 110-2h2V6.477L6.237 7.582l1.715 5.349a1 1 0 01-.285 1.05A3.989 3.989 0 015 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.738-5.42-1.233-.617a1 1 0 01.894-1.788l1.599.799L9 4.323V3a1 1 0 011-1z" })
3488
+ }
3489
+ ),
3490
+ isGroupSession && stackUsers.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3491
+ AvatarStack,
3492
+ {
3493
+ users: stackUsers,
3494
+ max: 3,
3495
+ size: "xs",
3496
+ onClick: onMembersClick,
3497
+ className: "flex-shrink-0"
3498
+ }
3499
+ )
3500
+ ] }),
3501
+ isGroupSession && memberCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-600 dark:text-gray-400 mt-0.5", children: memberCount === 1 ? t("BiChat.Chat.OneMember") : t("BiChat.Chat.MemberCount").replace("{{count}}", String(memberCount)) })
3502
+ ] })
3345
3503
  ] }),
3346
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3504
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
3347
3505
  readOnly && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200 rounded", children: t("BiChat.Chat.ReadOnly") }),
3348
3506
  session.status === "archived" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-1 text-xs bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded", children: t("BiChat.Chat.Archived") }),
3349
3507
  actionsSlot
@@ -4218,6 +4376,7 @@ function UserMessage({
4218
4376
  turn,
4219
4377
  turnId,
4220
4378
  initials = "U",
4379
+ authorName,
4221
4380
  slots,
4222
4381
  classNames: classNameOverrides,
4223
4382
  onCopy,
@@ -4425,6 +4584,16 @@ function UserMessage({
4425
4584
  };
4426
4585
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classes.root, children: [
4427
4586
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classes.wrapper, children: [
4587
+ authorName && /* @__PURE__ */ jsxRuntime.jsx(
4588
+ "span",
4589
+ {
4590
+ id: `${turn.id}-author`,
4591
+ role: "note",
4592
+ "aria-label": authorName,
4593
+ className: "mb-1 px-1 text-[11px] text-right text-gray-500 dark:text-gray-400",
4594
+ children: authorName
4595
+ }
4596
+ ),
4428
4597
  normalizedAttachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.attachments, children: renderSlot(
4429
4598
  slots?.attachments,
4430
4599
  attachmentsSlotProps,
@@ -4436,20 +4605,28 @@ function UserMessage({
4436
4605
  }
4437
4606
  )
4438
4607
  ) }),
4439
- turn.content && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bubbleRef, className: classes.bubble, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
4440
- EditForm,
4608
+ turn.content && /* @__PURE__ */ jsxRuntime.jsx(
4609
+ "div",
4441
4610
  {
4442
- draftContent,
4443
- onDraftChange: handleDraftChange,
4444
- onSave: handleEditSave,
4445
- onCancel: handleEditCancel,
4446
- onKeyDown: handleEditKeyDown,
4447
- textareaRef: editTextareaRef,
4448
- disabled: false,
4449
- originalContent: turn.content,
4450
- t
4611
+ ref: bubbleRef,
4612
+ className: classes.bubble,
4613
+ "aria-describedby": authorName ? `${turn.id}-author` : void 0,
4614
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
4615
+ EditForm,
4616
+ {
4617
+ draftContent,
4618
+ onDraftChange: handleDraftChange,
4619
+ onSave: handleEditSave,
4620
+ onCancel: handleEditCancel,
4621
+ onKeyDown: handleEditKeyDown,
4622
+ textareaRef: editTextareaRef,
4623
+ disabled: false,
4624
+ originalContent: turn.content,
4625
+ t
4626
+ }
4627
+ ) : renderSlot(slots?.content, contentSlotProps, turn.content) })
4451
4628
  }
4452
- ) : renderSlot(slots?.content, contentSlotProps, turn.content) }) }),
4629
+ ),
4453
4630
  !hideActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${classes.actions} ${isCopied ? "opacity-100" : ""}`, children: renderSlot(
4454
4631
  slots?.actions,
4455
4632
  actionsSlotProps,
@@ -4498,19 +4675,25 @@ function UserTurnView({
4498
4675
  turn,
4499
4676
  slots,
4500
4677
  classNames,
4501
- initials = "U",
4678
+ initials,
4502
4679
  hideAvatar,
4503
4680
  hideActions,
4504
4681
  hideTimestamp,
4505
- allowEdit
4682
+ allowEdit,
4683
+ showAuthorName = false
4506
4684
  }) {
4507
4685
  const { handleEdit, handleCopy } = useChatMessaging();
4686
+ const author = turn.userTurn.author;
4687
+ const fullName = [author?.firstName || "", author?.lastName || ""].join(" ").trim();
4688
+ const authorName = showAuthorName && fullName.length > 0 ? fullName : void 0;
4689
+ const resolvedInitials = initials ?? author?.initials ?? "U";
4508
4690
  return /* @__PURE__ */ jsxRuntime.jsx(
4509
4691
  UserMessage,
4510
4692
  {
4511
4693
  turn: turn.userTurn,
4512
4694
  turnId: turn.id,
4513
- initials,
4695
+ initials: resolvedInitials,
4696
+ authorName,
4514
4697
  slots,
4515
4698
  classNames,
4516
4699
  onCopy: handleCopy,
@@ -8505,7 +8688,7 @@ function StreamingBubble({ content, normalizedContent }) {
8505
8688
  }
8506
8689
  function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readOnly }) {
8507
8690
  const { t } = useTranslation();
8508
- const { currentSessionId, fetching } = useChatSession();
8691
+ const { session, currentSessionId, fetching } = useChatSession();
8509
8692
  const {
8510
8693
  turns,
8511
8694
  streamingContent,
@@ -8520,6 +8703,7 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8520
8703
  () => streamingContent ? normalizeStreamingMarkdown(streamingContent) : "",
8521
8704
  [streamingContent]
8522
8705
  );
8706
+ const showAuthorNames = Boolean(session?.isGroup);
8523
8707
  const showEphemeral = showActivityTrace || showTypingIndicator;
8524
8708
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 min-h-0", children: [
8525
8709
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full overflow-y-auto overflow-x-hidden px-4 py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto space-y-6", children: [
@@ -8529,6 +8713,10 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8529
8713
  const prevDate = index > 0 ? new Date(turns[index - 1].createdAt) : null;
8530
8714
  const showDateSeparator = !!prevDate && !dateFns.isSameDay(turnDate, prevDate);
8531
8715
  const isLast = index === turns.length - 1;
8716
+ const userTurnProps = {
8717
+ allowEdit: readOnly ? false : isLast,
8718
+ showAuthorName: showAuthorNames
8719
+ };
8532
8720
  return /* @__PURE__ */ jsxRuntime.jsxs(React.Fragment, { children: [
8533
8721
  showDateSeparator && /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { date: turnDate }),
8534
8722
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -8538,7 +8726,7 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8538
8726
  isLastTurn: isLast,
8539
8727
  renderUserTurn,
8540
8728
  renderAssistantTurn,
8541
- userTurnProps: readOnly ? { allowEdit: false } : { allowEdit: isLast },
8729
+ userTurnProps,
8542
8730
  assistantTurnProps: readOnly ? { allowRegenerate: false } : void 0
8543
8731
  }
8544
8732
  )
@@ -10744,71 +10932,689 @@ function SessionArtifactsPanel({
10744
10932
  }
10745
10933
  );
10746
10934
  }
10935
+ var DialogContext = React.createContext(null);
10936
+ var FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
10937
+ function InlineDialog({ open, onClose, className, children }) {
10938
+ const containerRef = React.useRef(null);
10939
+ const previousFocusRef = React.useRef(null);
10940
+ React.useEffect(() => {
10941
+ if (!open) {
10942
+ return;
10943
+ }
10944
+ previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
10945
+ const container = containerRef.current;
10946
+ if (!container) {
10947
+ return;
10948
+ }
10949
+ const getFocusable = () => Array.from(
10950
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
10951
+ );
10952
+ const handler = (e) => {
10953
+ if (e.key === "Escape") {
10954
+ onClose();
10955
+ return;
10956
+ }
10957
+ if (e.key !== "Tab") {
10958
+ return;
10959
+ }
10960
+ const focusable = getFocusable();
10961
+ if (focusable.length === 0) {
10962
+ e.preventDefault();
10963
+ container.focus();
10964
+ return;
10965
+ }
10966
+ const first = focusable[0];
10967
+ const last = focusable[focusable.length - 1];
10968
+ if (e.shiftKey && document.activeElement === first) {
10969
+ e.preventDefault();
10970
+ last.focus();
10971
+ } else if (!e.shiftKey && document.activeElement === last) {
10972
+ e.preventDefault();
10973
+ first.focus();
10974
+ }
10975
+ };
10976
+ (getFocusable()[0] ?? container)?.focus();
10977
+ container.addEventListener("keydown", handler);
10978
+ return () => {
10979
+ container.removeEventListener("keydown", handler);
10980
+ previousFocusRef.current?.focus();
10981
+ };
10982
+ }, [open, onClose]);
10983
+ if (!open) {
10984
+ return null;
10985
+ }
10986
+ return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
10987
+ "div",
10988
+ {
10989
+ ref: containerRef,
10990
+ className,
10991
+ onClick: onClose,
10992
+ tabIndex: -1,
10993
+ children
10994
+ }
10995
+ ) });
10996
+ }
10997
+ function InlineDialogBackdrop(props) {
10998
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", ...props });
10999
+ }
11000
+ function InlineDialogPanel({
11001
+ children,
11002
+ onClick,
11003
+ ...rest
11004
+ }) {
11005
+ const ref = React.useRef(null);
11006
+ React.useEffect(() => {
11007
+ if (!ref.current) {
11008
+ return;
11009
+ }
11010
+ const target = ref.current.querySelector("[data-autofocus]") ?? ref.current.querySelector(
11011
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
11012
+ );
11013
+ target?.focus();
11014
+ }, []);
11015
+ return /* @__PURE__ */ jsxRuntime.jsx(
11016
+ "div",
11017
+ {
11018
+ ref,
11019
+ role: "dialog",
11020
+ "aria-modal": "true",
11021
+ onClick: (e) => {
11022
+ e.stopPropagation();
11023
+ onClick?.(e);
11024
+ },
11025
+ ...rest,
11026
+ children
11027
+ }
11028
+ );
11029
+ }
11030
+ function InlineDialogTitle(props) {
11031
+ return /* @__PURE__ */ jsxRuntime.jsx("h2", { ...props });
11032
+ }
11033
+ function InlineDialogDescription(props) {
11034
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { ...props });
11035
+ }
10747
11036
 
10748
- // ui/src/bichat/components/StreamError.tsx
11037
+ // ui/src/bichat/components/SessionMembersModal.tsx
10749
11038
  init_useTranslation();
10750
- function StreamError({
10751
- error,
10752
- onRetry,
10753
- onRegenerate,
10754
- onDismiss,
10755
- compact = false
11039
+ init_useTranslation();
11040
+ function ConfirmModalBase({
11041
+ isOpen,
11042
+ title,
11043
+ message,
11044
+ onConfirm,
11045
+ onCancel,
11046
+ confirmText,
11047
+ cancelText,
11048
+ isDanger = false
10756
11049
  }) {
10757
11050
  const { t } = useTranslation();
10758
- return /* @__PURE__ */ jsxRuntime.jsxs(
10759
- framerMotion.motion.div,
10760
- {
10761
- initial: { opacity: 0, y: 10 },
10762
- animate: { opacity: 1, y: 0 },
10763
- exit: { opacity: 0, y: -10 },
10764
- className: `flex items-start gap-3 ${compact ? "px-3 py-2.5" : "px-4 py-3"} bg-red-50 dark:bg-red-950/40 border border-red-200/80 dark:border-red-900/60 rounded-xl shadow-sm`,
10765
- role: "alert",
10766
- children: [
10767
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 mt-0.5 flex items-center justify-center w-7 h-7 rounded-full bg-red-100 dark:bg-red-900/40", children: /* @__PURE__ */ jsxRuntime.jsx(
10768
- react.Warning,
10769
- {
10770
- className: "w-4 h-4 text-red-600 dark:text-red-400",
10771
- weight: "fill"
10772
- }
10773
- ) }),
11051
+ const resolvedConfirmText = confirmText?.trim() ? confirmText : t("BiChat.Common.Confirm");
11052
+ const resolvedCancelText = cancelText?.trim() ? cancelText : t("BiChat.Common.Cancel");
11053
+ return /* @__PURE__ */ jsxRuntime.jsxs(InlineDialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
11054
+ /* @__PURE__ */ jsxRuntime.jsx(InlineDialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11055
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(InlineDialogPanel, { className: "bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/30 max-w-sm w-full overflow-hidden", children: [
11056
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 pt-6 pb-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-4", children: [
11057
+ isDanger && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-xl bg-red-50 dark:bg-red-950/40 border border-red-200/60 dark:border-red-800/40", children: /* @__PURE__ */ jsxRuntime.jsx(react.WarningCircle, { size: 22, weight: "duotone", className: "text-red-600 dark:text-red-400" }) }),
10774
11058
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
10775
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
10776
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
10777
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
10778
- onRetry && /* @__PURE__ */ jsxRuntime.jsxs(
10779
- "button",
10780
- {
10781
- onClick: onRetry,
10782
- className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-white bg-red-600 hover:bg-red-700 active:bg-red-800 dark:bg-red-700 dark:hover:bg-red-600 rounded-lg transition-colors shadow-sm",
10783
- type: "button",
10784
- children: [
10785
- /* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { className: "w-3.5 h-3.5" }),
10786
- t("BiChat.StreamError.Retry")
10787
- ]
10788
- }
10789
- ),
10790
- onRegenerate && /* @__PURE__ */ jsxRuntime.jsxs(
10791
- "button",
10792
- {
10793
- onClick: onRegenerate,
10794
- className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-colors shadow-sm",
10795
- type: "button",
10796
- children: [
10797
- /* @__PURE__ */ jsxRuntime.jsx(react.ArrowsCounterClockwise, { className: "w-3.5 h-3.5" }),
10798
- t("BiChat.StreamError.Regenerate")
10799
- ]
10800
- }
10801
- )
10802
- ] })
10803
- ] }),
10804
- onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
11059
+ /* @__PURE__ */ jsxRuntime.jsx(InlineDialogTitle, { className: "text-base font-semibold text-gray-900 dark:text-gray-100 leading-snug", children: title }),
11060
+ /* @__PURE__ */ jsxRuntime.jsx(InlineDialogDescription, { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 leading-relaxed", children: message })
11061
+ ] })
11062
+ ] }) }),
11063
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-2.5 px-6 pb-5", children: [
11064
+ /* @__PURE__ */ jsxRuntime.jsx(
10805
11065
  "button",
10806
11066
  {
10807
- onClick: onDismiss,
10808
- className: "cursor-pointer flex-shrink-0 mt-0.5 inline-flex items-center justify-center w-6 h-6 text-red-400 dark:text-red-500 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/40 rounded-md transition-colors",
10809
11067
  type: "button",
10810
- "aria-label": t("BiChat.Chat.DismissNotification"),
10811
- children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { className: "w-3.5 h-3.5" })
11068
+ onClick: onCancel,
11069
+ ...isDanger ? { "data-autofocus": true } : {},
11070
+ className: "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700/60 hover:bg-gray-200 dark:hover:bg-gray-700 active:bg-gray-250 dark:active:bg-gray-600 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11071
+ "data-testid": "confirm-modal-cancel",
11072
+ children: resolvedCancelText
11073
+ }
11074
+ ),
11075
+ /* @__PURE__ */ jsxRuntime.jsx(
11076
+ "button",
11077
+ {
11078
+ type: "button",
11079
+ ...!isDanger ? { "data-autofocus": true } : {},
11080
+ onClick: onConfirm,
11081
+ className: [
11082
+ "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-white",
11083
+ "transition-all duration-150 shadow-sm hover:shadow",
11084
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11085
+ isDanger ? "bg-red-600 hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500/50" : "bg-primary-600 hover:bg-primary-700 active:bg-primary-800 focus-visible:ring-primary-500/50"
11086
+ ].join(" "),
11087
+ "data-testid": "confirm-modal-confirm",
11088
+ children: resolvedConfirmText
11089
+ }
11090
+ )
11091
+ ] })
11092
+ ] }) })
11093
+ ] });
11094
+ }
11095
+ var ConfirmModal = React.memo(ConfirmModalBase);
11096
+ ConfirmModal.displayName = "ConfirmModal";
11097
+ var ConfirmModal_default = ConfirmModal;
11098
+ var ROLES = ["editor", "viewer"];
11099
+ function RoleSegmentedControl({
11100
+ value,
11101
+ onChange,
11102
+ disabled,
11103
+ size = "md",
11104
+ t
11105
+ }) {
11106
+ const btnBase = size === "sm" ? "px-2 py-0.5 text-[11px]" : "px-3 py-1 text-xs";
11107
+ const currentIndex = ROLES.indexOf(value);
11108
+ const handleKeyDown = (e) => {
11109
+ if (disabled) {
11110
+ return;
11111
+ }
11112
+ let nextIndex = null;
11113
+ if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
11114
+ e.preventDefault();
11115
+ nextIndex = currentIndex <= 0 ? ROLES.length - 1 : currentIndex - 1;
11116
+ } else if (e.key === "ArrowRight" || e.key === "ArrowDown") {
11117
+ e.preventDefault();
11118
+ nextIndex = currentIndex >= ROLES.length - 1 ? 0 : currentIndex + 1;
11119
+ }
11120
+ if (nextIndex !== null) {
11121
+ const nextRole = ROLES[nextIndex];
11122
+ onChange(nextRole);
11123
+ const target = e.currentTarget.querySelector(`[data-role="${nextRole}"]`);
11124
+ target?.focus();
11125
+ }
11126
+ };
11127
+ return /* @__PURE__ */ jsxRuntime.jsx(
11128
+ "div",
11129
+ {
11130
+ role: "radiogroup",
11131
+ "aria-label": t("BiChat.Share.RoleLabel"),
11132
+ className: "inline-flex rounded-lg border border-gray-200 dark:border-gray-700 p-0.5 bg-gray-50 dark:bg-gray-800/50",
11133
+ onKeyDown: handleKeyDown,
11134
+ children: ROLES.map((role) => /* @__PURE__ */ jsxRuntime.jsx(
11135
+ "button",
11136
+ {
11137
+ type: "button",
11138
+ role: "radio",
11139
+ "aria-checked": value === role,
11140
+ tabIndex: value === role ? 0 : -1,
11141
+ "data-role": role,
11142
+ disabled,
11143
+ onClick: () => onChange(role),
11144
+ className: `${btnBase} cursor-pointer rounded-md font-medium transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 disabled:opacity-50 disabled:cursor-not-allowed ${value === role ? "bg-primary-600 text-white shadow-sm" : "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100"}`,
11145
+ children: role === "editor" ? t("BiChat.Share.RoleEditor") : t("BiChat.Share.RoleViewer")
11146
+ },
11147
+ role
11148
+ ))
11149
+ }
11150
+ );
11151
+ }
11152
+ function MemberSkeleton() {
11153
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 rounded-xl px-3 py-2.5", "aria-hidden": "true", children: [
11154
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 animate-pulse flex-shrink-0" }),
11155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-1.5", children: [
11156
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-28 rounded bg-gray-200 dark:bg-gray-700 animate-pulse" }),
11157
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-16 rounded bg-gray-100 dark:bg-gray-800 animate-pulse" })
11158
+ ] }),
11159
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-16 rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" })
11160
+ ] });
11161
+ }
11162
+ function SessionMembersModal({ isOpen, sessionId, dataSource, onClose }) {
11163
+ const headingId = React.useId();
11164
+ const { t } = useTranslation();
11165
+ const statusTimerRef = React.useRef();
11166
+ const [loading, setLoading] = React.useState(false);
11167
+ const [saving, setSaving] = React.useState(false);
11168
+ const [error, setError] = React.useState(null);
11169
+ const [users, setUsers] = React.useState([]);
11170
+ const [members, setMembers] = React.useState([]);
11171
+ const [selectedUser, setSelectedUser] = React.useState(null);
11172
+ const [selectedRole, setSelectedRole] = React.useState("editor");
11173
+ const [query, setQuery] = React.useState("");
11174
+ const [confirmRemove, setConfirmRemove] = React.useState(null);
11175
+ const [statusMessage, setStatusMessage] = React.useState(null);
11176
+ const [dropdownOpen, setDropdownOpen] = React.useState(false);
11177
+ const [dropdownHighlightIndex, setDropdownHighlightIndex] = React.useState(0);
11178
+ const dropdownOptionRefs = React.useRef([]);
11179
+ const canManageMembers = Boolean(
11180
+ dataSource.listUsers && dataSource.listSessionMembers && dataSource.addSessionMember && dataSource.updateSessionMemberRole && dataSource.removeSessionMember
11181
+ );
11182
+ const refresh = React.useCallback(async () => {
11183
+ if (!sessionId || !canManageMembers) {
11184
+ return;
11185
+ }
11186
+ setLoading(true);
11187
+ setError(null);
11188
+ try {
11189
+ const [usersData, membersData] = await Promise.all([
11190
+ dataSource.listUsers(),
11191
+ dataSource.listSessionMembers(sessionId)
11192
+ ]);
11193
+ setUsers(usersData);
11194
+ setMembers(membersData);
11195
+ } catch {
11196
+ setError(t("BiChat.Share.LoadFailed"));
11197
+ } finally {
11198
+ setLoading(false);
11199
+ }
11200
+ }, [canManageMembers, dataSource, sessionId, t]);
11201
+ React.useEffect(() => {
11202
+ if (!isOpen) {
11203
+ return;
11204
+ }
11205
+ void refresh();
11206
+ }, [isOpen, refresh]);
11207
+ React.useEffect(() => {
11208
+ if (!isOpen) {
11209
+ setQuery("");
11210
+ setSelectedUser(null);
11211
+ setSelectedRole("editor");
11212
+ setError(null);
11213
+ setConfirmRemove(null);
11214
+ setStatusMessage(null);
11215
+ setDropdownOpen(false);
11216
+ setDropdownHighlightIndex(0);
11217
+ }
11218
+ }, [isOpen]);
11219
+ const memberIDs = React.useMemo(() => new Set(members.map((m) => m.user.id)), [members]);
11220
+ const availableUsers = React.useMemo(
11221
+ () => users.filter((user) => !memberIDs.has(user.id)),
11222
+ [users, memberIDs]
11223
+ );
11224
+ const filteredUsers = React.useMemo(() => {
11225
+ if (!query.trim()) {
11226
+ return availableUsers;
11227
+ }
11228
+ const q = query.toLowerCase();
11229
+ return availableUsers.filter(
11230
+ (u) => u.firstName.toLowerCase().includes(q) || u.lastName.toLowerCase().includes(q) || `${u.firstName} ${u.lastName}`.toLowerCase().includes(q)
11231
+ );
11232
+ }, [availableUsers, query]);
11233
+ React.useEffect(() => {
11234
+ setDropdownHighlightIndex(
11235
+ (i) => Math.min(Math.max(0, i), Math.max(0, filteredUsers.length - 1))
11236
+ );
11237
+ }, [filteredUsers.length]);
11238
+ React.useEffect(() => () => clearTimeout(statusTimerRef.current), []);
11239
+ const flashStatus = React.useCallback((msg) => {
11240
+ clearTimeout(statusTimerRef.current);
11241
+ setStatusMessage(msg);
11242
+ statusTimerRef.current = setTimeout(() => setStatusMessage(null), 3e3);
11243
+ }, []);
11244
+ const handleAdd = React.useCallback(async () => {
11245
+ if (!sessionId || !selectedUser || !dataSource.addSessionMember) {
11246
+ return;
11247
+ }
11248
+ setSaving(true);
11249
+ setError(null);
11250
+ try {
11251
+ await dataSource.addSessionMember(sessionId, selectedUser.id, selectedRole);
11252
+ setSelectedUser(null);
11253
+ setQuery("");
11254
+ flashStatus(t("BiChat.Share.MemberAdded"));
11255
+ await refresh();
11256
+ } catch {
11257
+ setError(t("BiChat.Share.AddFailed"));
11258
+ } finally {
11259
+ setSaving(false);
11260
+ }
11261
+ }, [sessionId, selectedUser, selectedRole, dataSource.addSessionMember, refresh, t, flashStatus]);
11262
+ const handleUpdateRole = React.useCallback(async (userId, role) => {
11263
+ if (!sessionId || !dataSource.updateSessionMemberRole) {
11264
+ return;
11265
+ }
11266
+ setSaving(true);
11267
+ setError(null);
11268
+ try {
11269
+ await dataSource.updateSessionMemberRole(sessionId, userId, role);
11270
+ await refresh();
11271
+ } catch {
11272
+ setError(t("BiChat.Share.UpdateFailed"));
11273
+ } finally {
11274
+ setSaving(false);
11275
+ }
11276
+ }, [sessionId, dataSource.updateSessionMemberRole, refresh]);
11277
+ const handleRemove = React.useCallback(async (userId) => {
11278
+ if (!sessionId || !dataSource.removeSessionMember) {
11279
+ return;
11280
+ }
11281
+ setSaving(true);
11282
+ setError(null);
11283
+ try {
11284
+ await dataSource.removeSessionMember(sessionId, userId);
11285
+ flashStatus(t("BiChat.Share.MemberRemoved"));
11286
+ await refresh();
11287
+ } catch {
11288
+ setError(t("BiChat.Share.RemoveFailed"));
11289
+ } finally {
11290
+ setSaving(false);
11291
+ }
11292
+ }, [sessionId, dataSource.removeSessionMember, refresh, flashStatus]);
11293
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11294
+ /* @__PURE__ */ jsxRuntime.jsxs(InlineDialog, { open: isOpen, onClose, className: "relative z-40", children: [
11295
+ /* @__PURE__ */ jsxRuntime.jsx(InlineDialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11296
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
11297
+ InlineDialogPanel,
11298
+ {
11299
+ "aria-labelledby": headingId,
11300
+ className: "bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/30 max-w-md w-full",
11301
+ children: [
11302
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 pt-5 pb-4", children: [
11303
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
11304
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-9 h-9 rounded-xl bg-primary-50 dark:bg-primary-950/40 border border-primary-200/60 dark:border-primary-800/40", children: /* @__PURE__ */ jsxRuntime.jsx(react.UsersThree, { size: 18, weight: "duotone", className: "text-primary-600 dark:text-primary-400" }) }),
11305
+ /* @__PURE__ */ jsxRuntime.jsx(InlineDialogTitle, { id: headingId, className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: t("BiChat.Share.Title") })
11306
+ ] }),
11307
+ /* @__PURE__ */ jsxRuntime.jsx(
11308
+ "button",
11309
+ {
11310
+ type: "button",
11311
+ onClick: onClose,
11312
+ className: "rounded-lg p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 dark:hover:text-gray-300 dark:hover:bg-gray-700 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
11313
+ "aria-label": t("BiChat.Common.Close"),
11314
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 18 })
11315
+ }
11316
+ )
11317
+ ] }),
11318
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 pb-5 space-y-4", children: [
11319
+ !canManageMembers && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-amber-200 bg-amber-50 px-3 py-2.5 text-xs text-amber-700 dark:border-amber-800/60 dark:bg-amber-900/20 dark:text-amber-300", children: t("BiChat.Share.Unsupported") }),
11320
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 px-3 py-2.5 text-xs text-red-700 dark:border-red-800/60 dark:bg-red-900/20 dark:text-red-300", children: error }),
11321
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: statusMessage }),
11322
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
11323
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400", children: [
11324
+ t("BiChat.Share.Members"),
11325
+ !loading && members.length > 0 ? ` (${members.length})` : ""
11326
+ ] }),
11327
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-64 overflow-y-auto -mx-1 px-1 space-y-1", children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11328
+ /* @__PURE__ */ jsxRuntime.jsx(MemberSkeleton, {}),
11329
+ /* @__PURE__ */ jsxRuntime.jsx(MemberSkeleton, {}),
11330
+ /* @__PURE__ */ jsxRuntime.jsx(MemberSkeleton, {})
11331
+ ] }) : members.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-gray-400 dark:text-gray-500", children: [
11332
+ /* @__PURE__ */ jsxRuntime.jsx(react.UsersThree, { size: 32, weight: "thin", className: "mb-2" }),
11333
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: t("BiChat.Share.Empty") })
11334
+ ] }) : members.map((member) => /* @__PURE__ */ jsxRuntime.jsxs(
11335
+ "div",
11336
+ {
11337
+ className: "flex items-center gap-3 rounded-xl px-3 py-2 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700/40",
11338
+ children: [
11339
+ /* @__PURE__ */ jsxRuntime.jsx(
11340
+ MemoizedUserAvatar,
11341
+ {
11342
+ firstName: member.user.firstName,
11343
+ lastName: member.user.lastName,
11344
+ initials: member.user.initials,
11345
+ size: "sm"
11346
+ }
11347
+ ),
11348
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "truncate text-sm font-medium text-gray-900 dark:text-gray-100", children: [
11349
+ member.user.firstName,
11350
+ " ",
11351
+ member.user.lastName
11352
+ ] }) }),
11353
+ member.role === "owner" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-50 dark:bg-amber-900/20 border border-amber-200/60 dark:border-amber-800/40 px-2.5 py-0.5 text-[11px] font-medium text-amber-700 dark:text-amber-300", children: [
11354
+ /* @__PURE__ */ jsxRuntime.jsx(react.Crown, { size: 12, weight: "duotone" }),
11355
+ t("BiChat.Share.RoleOwner")
11356
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
11357
+ /* @__PURE__ */ jsxRuntime.jsx(
11358
+ RoleSegmentedControl,
11359
+ {
11360
+ value: member.role,
11361
+ onChange: (role) => handleUpdateRole(member.user.id, role),
11362
+ disabled: saving,
11363
+ size: "sm",
11364
+ t
11365
+ }
11366
+ ),
11367
+ /* @__PURE__ */ jsxRuntime.jsx(
11368
+ "button",
11369
+ {
11370
+ type: "button",
11371
+ disabled: saving,
11372
+ onClick: () => setConfirmRemove(member),
11373
+ className: "cursor-pointer rounded-lg p-1.5 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500/50",
11374
+ "aria-label": `${t("BiChat.Share.Remove")} ${member.user.firstName} ${member.user.lastName}`,
11375
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.Trash, { size: 14 })
11376
+ }
11377
+ )
11378
+ ] })
11379
+ ]
11380
+ },
11381
+ member.user.id
11382
+ )) })
11383
+ ] }),
11384
+ canManageMembers && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-gray-200 dark:border-gray-700 p-3 space-y-3", children: [
11385
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("BiChat.Share.AddMember") }),
11386
+ /* @__PURE__ */ jsxRuntime.jsxs(
11387
+ "div",
11388
+ {
11389
+ className: "relative",
11390
+ onBlur: (e) => {
11391
+ if (!e.currentTarget.contains(e.relatedTarget)) {
11392
+ setDropdownOpen(false);
11393
+ }
11394
+ },
11395
+ children: [
11396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
11397
+ /* @__PURE__ */ jsxRuntime.jsx(
11398
+ react.MagnifyingGlass,
11399
+ {
11400
+ size: 14,
11401
+ className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500"
11402
+ }
11403
+ ),
11404
+ /* @__PURE__ */ jsxRuntime.jsx(
11405
+ "input",
11406
+ {
11407
+ type: "text",
11408
+ className: "w-full rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800/60 pl-8 pr-3 py-2 text-sm text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 transition-colors focus:border-primary-400 dark:focus:border-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500/20",
11409
+ placeholder: t("BiChat.Share.SearchUsers"),
11410
+ value: selectedUser ? `${selectedUser.firstName} ${selectedUser.lastName}` : query,
11411
+ onFocus: () => {
11412
+ setDropdownOpen(true);
11413
+ setDropdownHighlightIndex(0);
11414
+ if (selectedUser) {
11415
+ setSelectedUser(null);
11416
+ setQuery("");
11417
+ }
11418
+ },
11419
+ onChange: (e) => {
11420
+ setQuery(e.target.value);
11421
+ setSelectedUser(null);
11422
+ setDropdownOpen(true);
11423
+ setDropdownHighlightIndex(0);
11424
+ },
11425
+ onKeyDown: (e) => {
11426
+ if (!dropdownOpen || filteredUsers.length === 0) {
11427
+ if (e.key === "Escape") {
11428
+ setDropdownOpen(false);
11429
+ }
11430
+ return;
11431
+ }
11432
+ if (e.key === "Escape") {
11433
+ e.preventDefault();
11434
+ setDropdownOpen(false);
11435
+ setDropdownHighlightIndex(0);
11436
+ return;
11437
+ }
11438
+ if (e.key === "ArrowDown") {
11439
+ e.preventDefault();
11440
+ const next = (dropdownHighlightIndex + 1) % filteredUsers.length;
11441
+ setDropdownHighlightIndex(next);
11442
+ setTimeout(() => dropdownOptionRefs.current[next]?.focus(), 0);
11443
+ return;
11444
+ }
11445
+ if (e.key === "ArrowUp") {
11446
+ e.preventDefault();
11447
+ const next = dropdownHighlightIndex <= 0 ? filteredUsers.length - 1 : dropdownHighlightIndex - 1;
11448
+ setDropdownHighlightIndex(next);
11449
+ setTimeout(() => dropdownOptionRefs.current[next]?.focus(), 0);
11450
+ return;
11451
+ }
11452
+ if (e.key === "Enter") {
11453
+ e.preventDefault();
11454
+ const user = filteredUsers[dropdownHighlightIndex];
11455
+ if (user) {
11456
+ setSelectedUser(user);
11457
+ setQuery("");
11458
+ setDropdownOpen(false);
11459
+ }
11460
+ }
11461
+ }
11462
+ }
11463
+ )
11464
+ ] }),
11465
+ dropdownOpen && !selectedUser && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-10 mt-1 max-h-48 w-full overflow-auto rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-lg py-1", children: filteredUsers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-gray-500 dark:text-gray-400", children: availableUsers.length === 0 ? t("BiChat.Share.NoUsersAvailable") : t("BiChat.Share.NoSearchResults") }) : filteredUsers.map((user, index) => /* @__PURE__ */ jsxRuntime.jsxs(
11466
+ "button",
11467
+ {
11468
+ type: "button",
11469
+ ref: (el) => {
11470
+ dropdownOptionRefs.current[index] = el;
11471
+ },
11472
+ className: `flex w-full items-center gap-2.5 px-3 py-2 cursor-pointer transition-colors hover:bg-primary-50 dark:hover:bg-primary-900/20 ${index === dropdownHighlightIndex ? "bg-primary-50 dark:bg-primary-900/20" : ""}`,
11473
+ onMouseDown: (e) => e.preventDefault(),
11474
+ onClick: () => {
11475
+ setSelectedUser(user);
11476
+ setQuery("");
11477
+ setDropdownOpen(false);
11478
+ },
11479
+ children: [
11480
+ /* @__PURE__ */ jsxRuntime.jsx(
11481
+ MemoizedUserAvatar,
11482
+ {
11483
+ firstName: user.firstName,
11484
+ lastName: user.lastName,
11485
+ initials: user.initials,
11486
+ size: "xs"
11487
+ }
11488
+ ),
11489
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-900 dark:text-gray-100", children: [
11490
+ user.firstName,
11491
+ " ",
11492
+ user.lastName
11493
+ ] })
11494
+ ]
11495
+ },
11496
+ user.id
11497
+ )) })
11498
+ ]
11499
+ }
11500
+ ),
11501
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
11502
+ /* @__PURE__ */ jsxRuntime.jsx(
11503
+ RoleSegmentedControl,
11504
+ {
11505
+ value: selectedRole,
11506
+ onChange: setSelectedRole,
11507
+ disabled: saving,
11508
+ t
11509
+ }
11510
+ ),
11511
+ /* @__PURE__ */ jsxRuntime.jsxs(
11512
+ "button",
11513
+ {
11514
+ type: "button",
11515
+ onClick: handleAdd,
11516
+ disabled: saving || !selectedUser,
11517
+ className: "inline-flex cursor-pointer items-center gap-1.5 rounded-xl bg-primary-600 px-3.5 py-2 text-sm font-medium text-white shadow-sm transition-all duration-150 hover:bg-primary-700 hover:shadow active:bg-primary-800 disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11518
+ children: [
11519
+ /* @__PURE__ */ jsxRuntime.jsx(react.UserPlus, { size: 14 }),
11520
+ t("BiChat.Share.Add")
11521
+ ]
11522
+ }
11523
+ )
11524
+ ] })
11525
+ ] })
11526
+ ] })
11527
+ ]
11528
+ }
11529
+ ) })
11530
+ ] }),
11531
+ /* @__PURE__ */ jsxRuntime.jsx(
11532
+ ConfirmModal,
11533
+ {
11534
+ isOpen: !!confirmRemove,
11535
+ isDanger: true,
11536
+ title: t("BiChat.Share.RemoveConfirmTitle"),
11537
+ message: confirmRemove ? t("BiChat.Share.RemoveConfirmMessage").replace(
11538
+ "{{name}}",
11539
+ `${confirmRemove.user.firstName} ${confirmRemove.user.lastName}`
11540
+ ) : "",
11541
+ confirmText: t("BiChat.Share.Remove"),
11542
+ onConfirm: () => {
11543
+ if (confirmRemove) {
11544
+ void handleRemove(confirmRemove.user.id);
11545
+ }
11546
+ setConfirmRemove(null);
11547
+ },
11548
+ onCancel: () => setConfirmRemove(null)
11549
+ }
11550
+ )
11551
+ ] });
11552
+ }
11553
+
11554
+ // ui/src/bichat/components/StreamError.tsx
11555
+ init_useTranslation();
11556
+ function StreamError({
11557
+ error,
11558
+ onRetry,
11559
+ onRegenerate,
11560
+ onDismiss,
11561
+ compact = false
11562
+ }) {
11563
+ const { t } = useTranslation();
11564
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11565
+ framerMotion.motion.div,
11566
+ {
11567
+ initial: { opacity: 0, y: 10 },
11568
+ animate: { opacity: 1, y: 0 },
11569
+ exit: { opacity: 0, y: -10 },
11570
+ className: `flex items-start gap-3 ${compact ? "px-3 py-2.5" : "px-4 py-3"} bg-red-50 dark:bg-red-950/40 border border-red-200/80 dark:border-red-900/60 rounded-xl shadow-sm`,
11571
+ role: "alert",
11572
+ children: [
11573
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 mt-0.5 flex items-center justify-center w-7 h-7 rounded-full bg-red-100 dark:bg-red-900/40", children: /* @__PURE__ */ jsxRuntime.jsx(
11574
+ react.Warning,
11575
+ {
11576
+ className: "w-4 h-4 text-red-600 dark:text-red-400",
11577
+ weight: "fill"
11578
+ }
11579
+ ) }),
11580
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
11581
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
11582
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
11583
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
11584
+ onRetry && /* @__PURE__ */ jsxRuntime.jsxs(
11585
+ "button",
11586
+ {
11587
+ onClick: onRetry,
11588
+ className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-white bg-red-600 hover:bg-red-700 active:bg-red-800 dark:bg-red-700 dark:hover:bg-red-600 rounded-lg transition-colors shadow-sm",
11589
+ type: "button",
11590
+ children: [
11591
+ /* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { className: "w-3.5 h-3.5" }),
11592
+ t("BiChat.StreamError.Retry")
11593
+ ]
11594
+ }
11595
+ ),
11596
+ onRegenerate && /* @__PURE__ */ jsxRuntime.jsxs(
11597
+ "button",
11598
+ {
11599
+ onClick: onRegenerate,
11600
+ className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-colors shadow-sm",
11601
+ type: "button",
11602
+ children: [
11603
+ /* @__PURE__ */ jsxRuntime.jsx(react.ArrowsCounterClockwise, { className: "w-3.5 h-3.5" }),
11604
+ t("BiChat.StreamError.Regenerate")
11605
+ ]
11606
+ }
11607
+ )
11608
+ ] })
11609
+ ] }),
11610
+ onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
11611
+ "button",
11612
+ {
11613
+ onClick: onDismiss,
11614
+ className: "cursor-pointer flex-shrink-0 mt-0.5 inline-flex items-center justify-center w-6 h-6 text-red-400 dark:text-red-500 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/40 rounded-md transition-colors",
11615
+ type: "button",
11616
+ "aria-label": t("BiChat.Chat.DismissNotification"),
11617
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { className: "w-3.5 h-3.5" })
10812
11618
  }
10813
11619
  )
10814
11620
  ]
@@ -10872,7 +11678,8 @@ function ChatSessionCore({
10872
11678
  updateQueueItem
10873
11679
  } = useChatInput();
10874
11680
  const isArchived = session?.status === "archived";
10875
- const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived;
11681
+ const accessReadOnly = session?.access ? !session.access.canWrite : false;
11682
+ const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived || accessReadOnly;
10876
11683
  const [restoring, setRestoring] = React.useState(false);
10877
11684
  const handleRestore = React.useCallback(async () => {
10878
11685
  if (!session?.id) {
@@ -10893,6 +11700,8 @@ function ChatSessionCore({
10893
11700
  const [artifactsPanelExpanded, setArtifactsPanelExpanded] = React.useState(
10894
11701
  artifactsPanelDefaultExpanded
10895
11702
  );
11703
+ const [membersModalOpen, setMembersModalOpen] = React.useState(false);
11704
+ const [headerMembers, setHeaderMembers] = React.useState(null);
10896
11705
  const [artifactsPanelWidth, setArtifactsPanelWidth] = React.useState(ARTIFACTS_PANEL_WIDTH_DEFAULT);
10897
11706
  const [isResizingArtifactsPanel, setIsResizingArtifactsPanel] = React.useState(false);
10898
11707
  const layoutContainerRef = React.useRef(null);
@@ -10927,6 +11736,25 @@ function ChatSessionCore({
10927
11736
  } catch {
10928
11737
  }
10929
11738
  }, [artifactsPanelStorageKey, showArtifactsPanel]);
11739
+ React.useEffect(() => {
11740
+ if (!session?.id || !dataSource.listSessionMembers) {
11741
+ setHeaderMembers(null);
11742
+ return;
11743
+ }
11744
+ let cancelled = false;
11745
+ dataSource.listSessionMembers(session.id).then((members) => {
11746
+ if (!cancelled) {
11747
+ setHeaderMembers(members.map((m) => m.user));
11748
+ }
11749
+ }).catch(() => {
11750
+ if (!cancelled) {
11751
+ setHeaderMembers(null);
11752
+ }
11753
+ });
11754
+ return () => {
11755
+ cancelled = true;
11756
+ };
11757
+ }, [session?.id, dataSource.listSessionMembers]);
10930
11758
  const handleArtifactsResizeStart = React.useCallback(() => {
10931
11759
  setIsResizingArtifactsPanel(true);
10932
11760
  }, []);
@@ -11016,8 +11844,26 @@ function ChatSessionCore({
11016
11844
  }
11017
11845
  }
11018
11846
  };
11019
- const headerActions = showArtifactsControls ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11020
- /* @__PURE__ */ jsxRuntime.jsxs(
11847
+ const canShowShareButton = Boolean(
11848
+ session?.access?.canManageMembers && dataSource.listUsers && dataSource.listSessionMembers && dataSource.addSessionMember && dataSource.updateSessionMemberRole && dataSource.removeSessionMember
11849
+ );
11850
+ const shareButton = canShowShareButton ? /* @__PURE__ */ jsxRuntime.jsxs(
11851
+ "button",
11852
+ {
11853
+ type: "button",
11854
+ onClick: () => setMembersModalOpen(true),
11855
+ className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium text-gray-500 transition-all duration-150 hover:bg-gray-100 hover:text-gray-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200",
11856
+ "aria-label": t("BiChat.Share.Title"),
11857
+ title: t("BiChat.Share.Title"),
11858
+ children: [
11859
+ /* @__PURE__ */ jsxRuntime.jsx(react.ShareNetwork, { className: "h-4 w-4" }),
11860
+ t("BiChat.Share.Button")
11861
+ ]
11862
+ }
11863
+ ) : null;
11864
+ const headerActions = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11865
+ shareButton,
11866
+ showArtifactsControls && /* @__PURE__ */ jsxRuntime.jsxs(
11021
11867
  "button",
11022
11868
  {
11023
11869
  type: "button",
@@ -11037,7 +11883,7 @@ function ChatSessionCore({
11037
11883
  }
11038
11884
  ),
11039
11885
  actionsSlot
11040
- ] }) : actionsSlot;
11886
+ ] });
11041
11887
  return /* @__PURE__ */ jsxRuntime.jsxs(
11042
11888
  "main",
11043
11889
  {
@@ -11050,7 +11896,9 @@ function ChatSessionCore({
11050
11896
  onBack,
11051
11897
  readOnly: effectiveReadOnly,
11052
11898
  logoSlot,
11053
- actionsSlot: headerActions
11899
+ actionsSlot: headerActions,
11900
+ members: headerMembers ?? (session?.owner ? [session.owner] : void 0),
11901
+ onMembersClick: canShowShareButton ? () => setMembersModalOpen(true) : void 0
11054
11902
  }
11055
11903
  ),
11056
11904
  error && /* @__PURE__ */ jsxRuntime.jsx(
@@ -11247,6 +12095,15 @@ function ChatSessionCore({
11247
12095
  ) })
11248
12096
  ]
11249
12097
  }
12098
+ ),
12099
+ canShowShareButton && /* @__PURE__ */ jsxRuntime.jsx(
12100
+ SessionMembersModal,
12101
+ {
12102
+ isOpen: membersModalOpen,
12103
+ sessionId: session?.id,
12104
+ dataSource,
12105
+ onClose: () => setMembersModalOpen(false)
12106
+ }
11250
12107
  )
11251
12108
  ]
11252
12109
  }
@@ -11269,7 +12126,7 @@ function ChatSession(props) {
11269
12126
  // ui/src/bichat/index.ts
11270
12127
  init_MarkdownRenderer();
11271
12128
  init_ChartCard();
11272
- var sizeClasses = {
12129
+ var sizeClasses2 = {
11273
12130
  sm: {
11274
12131
  container: "py-6 px-3",
11275
12132
  title: "text-sm",
@@ -11294,7 +12151,7 @@ function EmptyState({
11294
12151
  className = "",
11295
12152
  size = "md"
11296
12153
  }) {
11297
- const sizes = sizeClasses[size];
12154
+ const sizes = sizeClasses2[size];
11298
12155
  const prefersReducedMotion2 = framerMotion.useReducedMotion();
11299
12156
  const duration = prefersReducedMotion2 ? 0 : 0.4;
11300
12157
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -11354,7 +12211,7 @@ MemoizedEmptyState.displayName = "EmptyState";
11354
12211
 
11355
12212
  // ui/src/bichat/components/EditableText.tsx
11356
12213
  init_useTranslation();
11357
- var sizeClasses2 = {
12214
+ var sizeClasses3 = {
11358
12215
  sm: "text-sm",
11359
12216
  md: "text-base",
11360
12217
  lg: "text-lg"
@@ -11425,7 +12282,7 @@ var EditableText = React.forwardRef(
11425
12282
  const handleBlur = () => {
11426
12283
  handleSave();
11427
12284
  };
11428
- const sizeClass = sizeClasses2[size];
12285
+ const sizeClass = sizeClasses3[size];
11429
12286
  if (isEditing) {
11430
12287
  return /* @__PURE__ */ jsxRuntime.jsx(
11431
12288
  "div",
@@ -11485,7 +12342,7 @@ var MemoizedEditableText = React.memo(EditableText);
11485
12342
 
11486
12343
  // ui/src/bichat/components/SearchInput.tsx
11487
12344
  init_useTranslation();
11488
- var sizeClasses3 = {
12345
+ var sizeClasses4 = {
11489
12346
  sm: {
11490
12347
  container: "py-1.5 pl-8 pr-8 text-xs",
11491
12348
  icon: 14,
@@ -11518,7 +12375,7 @@ function SearchInput({
11518
12375
  const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Search");
11519
12376
  const resolvedAriaLabel = ariaLabel ?? t("BiChat.Common.Search");
11520
12377
  const inputRef = React.useRef(null);
11521
- const sizes = sizeClasses3[size];
12378
+ const sizes = sizeClasses4[size];
11522
12379
  React.useEffect(() => {
11523
12380
  if (autoFocus && inputRef.current) {
11524
12381
  inputRef.current.focus();
@@ -11945,134 +12802,6 @@ function ToastContainer({ toasts, onDismiss, dismissLabel }) {
11945
12802
  }
11946
12803
  );
11947
12804
  }
11948
-
11949
- // ui/src/bichat/components/ConfirmModal.tsx
11950
- init_useTranslation();
11951
- function ConfirmModalBase({
11952
- isOpen,
11953
- title,
11954
- message,
11955
- onConfirm,
11956
- onCancel,
11957
- confirmText,
11958
- cancelText,
11959
- isDanger = false
11960
- }) {
11961
- const { t } = useTranslation();
11962
- const resolvedConfirmText = confirmText?.trim() ? confirmText : t("BiChat.Common.Confirm");
11963
- const resolvedCancelText = cancelText?.trim() ? cancelText : t("BiChat.Common.Cancel");
11964
- return /* @__PURE__ */ jsxRuntime.jsxs(react$1.Dialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
11965
- /* @__PURE__ */ jsxRuntime.jsx(react$1.DialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11966
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(react$1.DialogPanel, { className: "bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/30 max-w-sm w-full overflow-hidden", children: [
11967
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 pt-6 pb-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3.5", children: [
11968
- isDanger && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-xl bg-red-50 dark:bg-red-950/40 border border-red-200/60 dark:border-red-800/40", children: /* @__PURE__ */ jsxRuntime.jsx(react.WarningCircle, { size: 22, weight: "duotone", className: "text-red-600 dark:text-red-400" }) }),
11969
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
11970
- /* @__PURE__ */ jsxRuntime.jsx(react$1.DialogTitle, { className: "text-base font-semibold text-gray-900 dark:text-gray-100 leading-snug", children: title }),
11971
- /* @__PURE__ */ jsxRuntime.jsx(react$1.Description, { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 leading-relaxed", children: message })
11972
- ] })
11973
- ] }) }),
11974
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-2.5 px-6 pb-5", children: [
11975
- /* @__PURE__ */ jsxRuntime.jsx(
11976
- "button",
11977
- {
11978
- type: "button",
11979
- onClick: onCancel,
11980
- ...isDanger ? { "data-autofocus": true } : {},
11981
- className: "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700/60 hover:bg-gray-200 dark:hover:bg-gray-700 active:bg-gray-250 dark:active:bg-gray-600 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11982
- "data-testid": "confirm-modal-cancel",
11983
- children: resolvedCancelText
11984
- }
11985
- ),
11986
- /* @__PURE__ */ jsxRuntime.jsx(
11987
- "button",
11988
- {
11989
- type: "button",
11990
- ...!isDanger ? { "data-autofocus": true } : {},
11991
- onClick: onConfirm,
11992
- className: [
11993
- "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-white",
11994
- "transition-all duration-150 shadow-sm hover:shadow",
11995
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11996
- isDanger ? "bg-red-600 hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500/50" : "bg-primary-600 hover:bg-primary-700 active:bg-primary-800 focus-visible:ring-primary-500/50"
11997
- ].join(" "),
11998
- "data-testid": "confirm-modal-confirm",
11999
- children: resolvedConfirmText
12000
- }
12001
- )
12002
- ] })
12003
- ] }) })
12004
- ] });
12005
- }
12006
- var ConfirmModal = React.memo(ConfirmModalBase);
12007
- ConfirmModal.displayName = "ConfirmModal";
12008
- var ConfirmModal_default = ConfirmModal;
12009
- function hashString(str) {
12010
- let hash = 0;
12011
- for (let i = 0; i < str.length; i++) {
12012
- const char = str.charCodeAt(i);
12013
- hash = (hash << 5) - hash + char;
12014
- hash = hash & hash;
12015
- }
12016
- return Math.abs(hash);
12017
- }
12018
- var colorPalette = [
12019
- { bg: "bg-blue-500", text: "text-white" },
12020
- { bg: "bg-green-500", text: "text-white" },
12021
- { bg: "bg-purple-500", text: "text-white" },
12022
- { bg: "bg-pink-500", text: "text-white" },
12023
- { bg: "bg-indigo-500", text: "text-white" },
12024
- { bg: "bg-teal-500", text: "text-white" },
12025
- { bg: "bg-orange-500", text: "text-white" },
12026
- { bg: "bg-cyan-500", text: "text-white" },
12027
- { bg: "bg-amber-500", text: "text-white" },
12028
- { bg: "bg-lime-500", text: "text-white" }
12029
- ];
12030
- var sizeClasses4 = {
12031
- sm: "w-8 h-8 text-xs",
12032
- md: "w-10 h-10 text-sm",
12033
- lg: "w-12 h-12 text-base"
12034
- };
12035
- function UserAvatar({
12036
- firstName,
12037
- lastName,
12038
- initials: providedInitials,
12039
- size = "md",
12040
- className = ""
12041
- }) {
12042
- const derivedInitials = (() => {
12043
- const firstChar = firstName?.trim()?.charAt(0) || "";
12044
- const lastChar = lastName?.trim()?.charAt(0) || "";
12045
- const combined = `${firstChar}${lastChar}`.trim();
12046
- return combined || "U";
12047
- })();
12048
- const initials = (providedInitials?.trim() || derivedInitials).toUpperCase();
12049
- const fullName = `${firstName}${lastName}`;
12050
- const colorIndex = hashString(fullName) % colorPalette.length;
12051
- const colors = colorPalette[colorIndex];
12052
- return /* @__PURE__ */ jsxRuntime.jsx(
12053
- "div",
12054
- {
12055
- className: `
12056
- ${sizeClasses4[size]}
12057
- ${colors.bg}
12058
- ${colors.text}
12059
- ${className}
12060
- rounded-full
12061
- flex
12062
- items-center
12063
- justify-center
12064
- font-semibold
12065
- flex-shrink-0
12066
- select-none
12067
- `,
12068
- "aria-label": `${firstName} ${lastName}`,
12069
- title: `${firstName} ${lastName}`,
12070
- children: initials
12071
- }
12072
- );
12073
- }
12074
- var MemoizedUserAvatar = React.memo(UserAvatar);
12075
- MemoizedUserAvatar.displayName = "UserAvatar";
12076
12805
  function PermissionGuard({
12077
12806
  permissions,
12078
12807
  mode = "all",
@@ -12514,6 +13243,11 @@ var SessionItem = React.memo(
12514
13243
  const isTitleGenerating = !session.title?.trim();
12515
13244
  const displayTitle = isTitleGenerating ? t("BiChat.Common.Generating") : session.title ?? t("BiChat.Common.Untitled");
12516
13245
  const lastActivity = formatRelativeTime(session.updatedAt, t);
13246
+ const accessRole = session.access?.role ?? "owner";
13247
+ const roleLabel = accessRole === "editor" ? t("BiChat.Share.RoleEditor") : accessRole === "viewer" ? t("BiChat.Share.RoleViewer") : accessRole === "read_all" ? t("BiChat.Share.RoleReadOnly") : "";
13248
+ const visibilityLabel = session.isGroup ? t("BiChat.Sidebar.GroupChat") : accessRole === "editor" || accessRole === "viewer" ? t("BiChat.Sidebar.SharedWithYou") : "";
13249
+ const isGroupOrShared = Boolean(session.isGroup || session.memberCount && session.memberCount > 1);
13250
+ const metaParts = [lastActivity, visibilityLabel, roleLabel].filter(Boolean);
12517
13251
  const { handlers: longPressHandlers } = useLongPress({
12518
13252
  delay: 500,
12519
13253
  onLongPress: (e) => {
@@ -12655,17 +13389,23 @@ var SessionItem = React.memo(
12655
13389
  "data-testid": `${testIdPrefix}-session-${session.id}`,
12656
13390
  ...longPressHandlers,
12657
13391
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
12658
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
12659
- /* @__PURE__ */ jsxRuntime.jsx(
12660
- MemoizedEditableText,
12661
- {
12662
- ref: editableTitleRef,
12663
- value: displayTitle,
12664
- onSave: (newTitle) => onRename?.(newTitle),
12665
- isLoading: isTitleGenerating
12666
- }
12667
- ),
12668
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate mt-0.5", children: lastActivity })
13392
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 min-w-0 flex-1", children: [
13393
+ isGroupOrShared && /* @__PURE__ */ jsxRuntime.jsx(react.UsersThree, { size: 14, weight: "duotone", className: "text-primary-500 dark:text-primary-400 mt-1 flex-shrink-0" }),
13394
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
13395
+ /* @__PURE__ */ jsxRuntime.jsx(
13396
+ MemoizedEditableText,
13397
+ {
13398
+ ref: editableTitleRef,
13399
+ value: displayTitle,
13400
+ onSave: (newTitle) => onRename?.(newTitle),
13401
+ isLoading: isTitleGenerating
13402
+ }
13403
+ ),
13404
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate mt-0.5", children: [
13405
+ metaParts.join(" \u2022 "),
13406
+ isGroupOrShared && session.memberCount && session.memberCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center ml-1 rounded-full bg-primary-50 dark:bg-primary-900/30 px-1.5 text-[10px] font-medium text-primary-600 dark:text-primary-400", children: session.memberCount })
13407
+ ] })
13408
+ ] })
12669
13409
  ] }),
12670
13410
  !isTouch && hasContextMenu && /* @__PURE__ */ jsxRuntime.jsxs(react$1.Menu, { children: [
12671
13411
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -13019,24 +13759,23 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
13019
13759
  setOffset((prev) => prev + limit);
13020
13760
  }
13021
13761
  }, [fetching, hasMore]);
13022
- const loadMoreRef = React.useCallback(
13023
- (node) => {
13024
- if (!node || fetching || !hasMore) {
13025
- return;
13762
+ const loadMoreNodeRef = React.useRef(null);
13763
+ const loadMoreRef = React.useCallback((node) => {
13764
+ loadMoreNodeRef.current = node;
13765
+ }, []);
13766
+ React.useEffect(() => {
13767
+ const node = loadMoreNodeRef.current;
13768
+ if (!node || fetching || !hasMore) {
13769
+ return;
13770
+ }
13771
+ const observer = new IntersectionObserver((entries) => {
13772
+ if (entries[0].isIntersecting) {
13773
+ handleLoadMore();
13026
13774
  }
13027
- const observer = new IntersectionObserver(
13028
- (entries) => {
13029
- if (entries[0].isIntersecting) {
13030
- handleLoadMore();
13031
- }
13032
- },
13033
- { threshold: 0.1 }
13034
- );
13035
- observer.observe(node);
13036
- return () => observer.disconnect();
13037
- },
13038
- [fetching, hasMore, handleLoadMore]
13039
- );
13775
+ }, { threshold: 0.1 });
13776
+ observer.observe(node);
13777
+ return () => observer.disconnect();
13778
+ }, [fetching, hasMore, handleLoadMore]);
13040
13779
  const derivedUsers = React.useMemo(() => {
13041
13780
  if (dataSource.listUsers) {
13042
13781
  return users;
@@ -13094,57 +13833,61 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
13094
13833
  role: "list",
13095
13834
  "aria-label": t("BiChat.AllChats.OrganizationChatSessions"),
13096
13835
  children: [
13097
- chats.map((chat) => /* @__PURE__ */ jsxRuntime.jsx(
13098
- framerMotion.motion.div,
13099
- {
13100
- initial: { opacity: 0, y: -10 },
13101
- animate: { opacity: 1, y: 0 },
13102
- exit: { opacity: 0, y: -10 },
13103
- children: /* @__PURE__ */ jsxRuntime.jsx(
13104
- "div",
13105
- {
13106
- role: "link",
13107
- tabIndex: 0,
13108
- onClick: () => onSessionSelect(chat.id),
13109
- onKeyDown: (e) => {
13110
- if (e.key === "Enter" || e.key === " ") {
13111
- e.preventDefault();
13112
- onSessionSelect(chat.id);
13113
- }
13114
- },
13115
- className: `
13836
+ chats.map((chat) => {
13837
+ const owner = chat.owner ?? {
13838
+ firstName: "",
13839
+ lastName: "",
13840
+ initials: "U"
13841
+ };
13842
+ const ownerName = [owner.firstName, owner.lastName].filter(Boolean).join(" ");
13843
+ return /* @__PURE__ */ jsxRuntime.jsx(
13844
+ framerMotion.motion.div,
13845
+ {
13846
+ initial: { opacity: 0, y: -10 },
13847
+ animate: { opacity: 1, y: 0 },
13848
+ exit: { opacity: 0, y: -10 },
13849
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13850
+ "div",
13851
+ {
13852
+ role: "link",
13853
+ tabIndex: 0,
13854
+ onClick: () => onSessionSelect(chat.id),
13855
+ onKeyDown: (e) => {
13856
+ if (e.key === "Enter" || e.key === " ") {
13857
+ e.preventDefault();
13858
+ onSessionSelect(chat.id);
13859
+ }
13860
+ },
13861
+ className: `
13116
13862
  block px-3 py-2 rounded-lg transition-smooth group cursor-pointer
13117
13863
  ${chat.id === activeSessionId ? "bg-primary-50/50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400 border-l-4 border-primary-400 dark:border-primary-600" : "text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 border-l-4 border-transparent"}
13118
13864
  `,
13119
- "aria-current": chat.id === activeSessionId ? "page" : void 0,
13120
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
13121
- /* @__PURE__ */ jsxRuntime.jsx(
13122
- MemoizedUserAvatar,
13123
- {
13124
- firstName: chat.owner.firstName,
13125
- lastName: chat.owner.lastName,
13126
- initials: chat.owner.initials,
13127
- size: "sm"
13128
- }
13129
- ),
13130
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
13131
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
13132
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: [
13133
- chat.owner.firstName,
13134
- " ",
13135
- chat.owner.lastName
13136
- ] }),
13137
- chat.status === "archived" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 mt-1 px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-full text-xs", children: [
13138
- /* @__PURE__ */ jsxRuntime.jsx(react.Archive, { size: 12, className: "w-3 h-3" }),
13139
- t("BiChat.Chat.Archived")
13865
+ "aria-current": chat.id === activeSessionId ? "page" : void 0,
13866
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
13867
+ /* @__PURE__ */ jsxRuntime.jsx(
13868
+ MemoizedUserAvatar,
13869
+ {
13870
+ firstName: owner.firstName,
13871
+ lastName: owner.lastName,
13872
+ initials: owner.initials,
13873
+ size: "sm"
13874
+ }
13875
+ ),
13876
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
13877
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
13878
+ ownerName && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: ownerName }),
13879
+ chat.status === "archived" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 mt-1 px-2 py-0.5 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-full text-xs", children: [
13880
+ /* @__PURE__ */ jsxRuntime.jsx(react.Archive, { size: 12, className: "w-3 h-3" }),
13881
+ t("BiChat.Chat.Archived")
13882
+ ] })
13140
13883
  ] })
13141
13884
  ] })
13142
- ] })
13143
- }
13144
- )
13145
- },
13146
- chat.id
13147
- )),
13885
+ }
13886
+ )
13887
+ },
13888
+ chat.id
13889
+ );
13890
+ }),
13148
13891
  hasMore && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "py-4 text-center", children: fetching ? /* @__PURE__ */ jsxRuntime.jsx(SessionSkeleton, { count: 2 }) : /* @__PURE__ */ jsxRuntime.jsx(
13149
13892
  "button",
13150
13893
  {
@@ -13914,19 +14657,22 @@ function Sidebar2({
13914
14657
  animate: "visible",
13915
14658
  role: "list",
13916
14659
  "aria-label": t("BiChat.Sidebar.PinnedChats"),
13917
- children: pinnedSessions.map((session) => /* @__PURE__ */ jsxRuntime.jsx(
13918
- SessionItem_default,
13919
- {
13920
- session,
13921
- isActive: session.id === activeSessionId,
13922
- onSelect: () => handleSessionSelect(session.id),
13923
- onArchive: () => handleSessionArchive(session.id),
13924
- onPin: () => handleSessionPin(session.id, session.pinned),
13925
- onRename: (newTitle) => handleSessionRename(session.id, newTitle),
13926
- onRegenerateTitle: () => handleSessionRegenerateTitle(session.id)
13927
- },
13928
- session.id
13929
- ))
14660
+ children: pinnedSessions.map((session) => {
14661
+ const canWrite = session.access?.canWrite ?? true;
14662
+ return /* @__PURE__ */ jsxRuntime.jsx(
14663
+ SessionItem_default,
14664
+ {
14665
+ session,
14666
+ isActive: session.id === activeSessionId,
14667
+ onSelect: () => handleSessionSelect(session.id),
14668
+ onArchive: canWrite ? () => handleSessionArchive(session.id) : void 0,
14669
+ onPin: canWrite ? () => handleSessionPin(session.id, session.pinned) : void 0,
14670
+ onRename: canWrite ? (newTitle) => handleSessionRename(session.id, newTitle) : void 0,
14671
+ onRegenerateTitle: canWrite ? () => handleSessionRegenerateTitle(session.id) : void 0
14672
+ },
14673
+ session.id
14674
+ );
14675
+ })
13930
14676
  }
13931
14677
  ),
13932
14678
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-gray-200 dark:border-gray-700 my-3" })
@@ -13948,19 +14694,22 @@ function Sidebar2({
13948
14694
  animate: "visible",
13949
14695
  role: "list",
13950
14696
  "aria-label": `${group.name} chats`,
13951
- children: group.sessions.map((session) => /* @__PURE__ */ jsxRuntime.jsx(
13952
- SessionItem_default,
13953
- {
13954
- session,
13955
- isActive: session.id === activeSessionId,
13956
- onSelect: () => handleSessionSelect(session.id),
13957
- onArchive: () => handleSessionArchive(session.id),
13958
- onPin: () => handleSessionPin(session.id, session.pinned),
13959
- onRename: (newTitle) => handleSessionRename(session.id, newTitle),
13960
- onRegenerateTitle: () => handleSessionRegenerateTitle(session.id)
13961
- },
13962
- session.id
13963
- ))
14697
+ children: group.sessions.map((session) => {
14698
+ const canWrite = session.access?.canWrite ?? true;
14699
+ return /* @__PURE__ */ jsxRuntime.jsx(
14700
+ SessionItem_default,
14701
+ {
14702
+ session,
14703
+ isActive: session.id === activeSessionId,
14704
+ onSelect: () => handleSessionSelect(session.id),
14705
+ onArchive: canWrite ? () => handleSessionArchive(session.id) : void 0,
14706
+ onPin: canWrite ? () => handleSessionPin(session.id, session.pinned) : void 0,
14707
+ onRename: canWrite ? (newTitle) => handleSessionRename(session.id, newTitle) : void 0,
14708
+ onRegenerateTitle: canWrite ? () => handleSessionRegenerateTitle(session.id) : void 0
14709
+ },
14710
+ session.id
14711
+ );
14712
+ })
13964
14713
  }
13965
14714
  )
13966
14715
  ] }, group.name)),
@@ -16121,6 +16870,76 @@ function useScrollToBottom(items) {
16121
16870
  scrollToBottom
16122
16871
  };
16123
16872
  }
16873
+ function useHttpDataSourceConfigFromApplet(options) {
16874
+ return React.useMemo(() => {
16875
+ const ctx = typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0;
16876
+ if (!ctx) {
16877
+ throw new Error(
16878
+ "Applet context not found. Ensure window.__APPLET_CONTEXT__ is injected by the backend."
16879
+ );
16880
+ }
16881
+ const rpcEndpoint = ctx.config?.rpcUIEndpoint ?? "/rpc";
16882
+ const streamEndpoint = ctx.config?.streamEndpoint ?? "/stream";
16883
+ const csrfToken = ctx.session?.csrfToken ?? (typeof window !== "undefined" ? window.__CSRF_TOKEN__ : void 0) ?? "";
16884
+ const isDev = typeof undefined?.DEV === "boolean" && undefined?.DEV;
16885
+ if (!csrfToken && isDev) {
16886
+ console.warn(
16887
+ "[useHttpDataSourceConfigFromApplet] CSRF token is empty \u2014 requests may be rejected by the server."
16888
+ );
16889
+ }
16890
+ return {
16891
+ baseUrl: "",
16892
+ rpcEndpoint,
16893
+ streamEndpoint,
16894
+ csrfToken,
16895
+ timeout: options?.timeout ?? 12e4
16896
+ };
16897
+ }, [options?.timeout]);
16898
+ }
16899
+ var SESSION_PATH_REGEX = /\/session\/([^/]+)/;
16900
+ function useBichatRouter({
16901
+ navigate,
16902
+ pathname,
16903
+ onNavigate
16904
+ }) {
16905
+ const activeSessionId = React.useMemo(
16906
+ () => pathname.match(SESSION_PATH_REGEX)?.[1],
16907
+ [pathname]
16908
+ );
16909
+ const maybeClose = React.useCallback(() => {
16910
+ onNavigate?.();
16911
+ }, [onNavigate]);
16912
+ const onSessionSelect = React.useCallback(
16913
+ (sessionId) => {
16914
+ if (sessionId) {
16915
+ navigate(`/session/${sessionId}`);
16916
+ } else {
16917
+ navigate("/");
16918
+ }
16919
+ maybeClose();
16920
+ },
16921
+ [navigate, maybeClose]
16922
+ );
16923
+ const onNewChat = React.useCallback(() => {
16924
+ navigate("/");
16925
+ maybeClose();
16926
+ }, [navigate, maybeClose]);
16927
+ const onArchivedView = React.useCallback(() => {
16928
+ navigate("/archived");
16929
+ maybeClose();
16930
+ }, [navigate, maybeClose]);
16931
+ const onBack = React.useCallback(() => {
16932
+ navigate("/");
16933
+ maybeClose();
16934
+ }, [navigate, maybeClose]);
16935
+ return {
16936
+ activeSessionId,
16937
+ onSessionSelect,
16938
+ onNewChat,
16939
+ onArchivedView,
16940
+ onBack
16941
+ };
16942
+ }
16124
16943
 
16125
16944
  // ui/src/bichat/index.ts
16126
16945
  init_IotaContext();
@@ -16424,10 +17243,59 @@ function resolveArtifactName(artifact) {
16424
17243
  }
16425
17244
  return `${label} artifact`;
16426
17245
  }
17246
+ function mapSessionUser(rawUser) {
17247
+ if (!isRecord2(rawUser)) {
17248
+ return void 0;
17249
+ }
17250
+ const rawId = rawUser.id;
17251
+ const id = readNonEmptyString(rawId) ?? (typeof rawId === "number" && Number.isFinite(rawId) ? String(rawId) : null);
17252
+ if (!id) {
17253
+ return void 0;
17254
+ }
17255
+ const firstName = readString2(rawUser.firstName);
17256
+ const lastName = readString2(rawUser.lastName);
17257
+ const initials = readNonEmptyString(rawUser.initials) || `${firstName.charAt(0)}${lastName.charAt(0)}`.trim().toUpperCase() || "U";
17258
+ return {
17259
+ id,
17260
+ firstName,
17261
+ lastName,
17262
+ initials
17263
+ };
17264
+ }
17265
+ function mapSessionAccess(rawAccess) {
17266
+ if (!isRecord2(rawAccess)) {
17267
+ return void 0;
17268
+ }
17269
+ const role = readString2(rawAccess.role).toLowerCase();
17270
+ const source = readString2(rawAccess.source).toLowerCase();
17271
+ const normalizedRole = role === "owner" || role === "editor" || role === "viewer" || role === "read_all" ? role : "none";
17272
+ const normalizedSource = source === "owner" || source === "member" || source === "permission" ? source : "none";
17273
+ const canRead = rawAccess.canRead === true || rawAccess.canRead === "true";
17274
+ const canWrite = rawAccess.canWrite === true || rawAccess.canWrite === "true";
17275
+ const canManageMembers = rawAccess.canManageMembers === true || rawAccess.canManageMembers === "true";
17276
+ if (normalizedRole === "none" && normalizedSource === "none" && !canRead && !canWrite && !canManageMembers) {
17277
+ return void 0;
17278
+ }
17279
+ return {
17280
+ role: normalizedRole,
17281
+ source: normalizedSource,
17282
+ canRead,
17283
+ canWrite,
17284
+ canManageMembers
17285
+ };
17286
+ }
16427
17287
  function toSession(session) {
16428
17288
  return {
16429
- ...session,
16430
- status: session.status === "archived" ? "archived" : "active"
17289
+ id: readString2(session.id),
17290
+ title: readString2(session.title),
17291
+ status: session.status === "archived" ? "archived" : "active",
17292
+ pinned: Boolean(session.pinned),
17293
+ createdAt: readString2(session.createdAt),
17294
+ updatedAt: readString2(session.updatedAt),
17295
+ owner: mapSessionUser(session.owner),
17296
+ isGroup: Boolean(session.isGroup),
17297
+ memberCount: typeof session.memberCount === "number" ? session.memberCount : void 0,
17298
+ access: mapSessionAccess(session.access)
16431
17299
  };
16432
17300
  }
16433
17301
  function toSessionArtifact(artifact) {
@@ -16701,6 +17569,7 @@ function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
16701
17569
  id: userTurnID,
16702
17570
  content: readString2(rawTurn.userTurn.content),
16703
17571
  attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
17572
+ author: mapSessionUser(rawTurn.userTurn.author),
16704
17573
  createdAt: readString2(rawTurn.userTurn.createdAt, createdAt)
16705
17574
  },
16706
17575
  assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
@@ -17193,6 +18062,68 @@ async function clearSessionHistory(callRPC, sessionId) {
17193
18062
  async function compactSessionHistory(callRPC, sessionId) {
17194
18063
  return callRPC("bichat.session.compact", { id: sessionId });
17195
18064
  }
18065
+ async function listUsers(callRPC) {
18066
+ const data = await callRPC("bichat.user.list", {});
18067
+ return data.users.map((user) => ({
18068
+ id: String(user.id),
18069
+ firstName: user.firstName || "",
18070
+ lastName: user.lastName || "",
18071
+ initials: user.initials || ""
18072
+ }));
18073
+ }
18074
+ async function listAllSessions(callRPC, options) {
18075
+ const data = await callRPC("bichat.session.listAll", {
18076
+ limit: options?.limit ?? 50,
18077
+ offset: options?.offset ?? 0,
18078
+ includeArchived: options?.includeArchived ?? false,
18079
+ userId: options?.userId ?? null
18080
+ });
18081
+ return {
18082
+ sessions: data.sessions.map(toSession),
18083
+ total: typeof data.total === "number" ? data.total : data.sessions.length,
18084
+ hasMore: Boolean(data.hasMore)
18085
+ };
18086
+ }
18087
+ async function listSessionMembers(callRPC, sessionId) {
18088
+ const data = await callRPC("bichat.session.members.list", { sessionId });
18089
+ return data.members.map((member) => ({
18090
+ user: {
18091
+ id: String(member.user.id),
18092
+ firstName: member.user.firstName,
18093
+ lastName: member.user.lastName,
18094
+ initials: member.user.initials
18095
+ },
18096
+ role: (() => {
18097
+ const normalizedRole = (member.role || "").toLowerCase();
18098
+ if (normalizedRole === "owner") {
18099
+ return "owner";
18100
+ }
18101
+ if (normalizedRole === "editor") {
18102
+ return "editor";
18103
+ }
18104
+ return "viewer";
18105
+ })(),
18106
+ createdAt: member.createdAt,
18107
+ updatedAt: member.updatedAt
18108
+ }));
18109
+ }
18110
+ async function addSessionMember(callRPC, sessionId, userId, role) {
18111
+ await callRPC("bichat.session.members.add", {
18112
+ sessionId,
18113
+ userId,
18114
+ role: role.toUpperCase()
18115
+ });
18116
+ }
18117
+ async function updateSessionMemberRole(callRPC, sessionId, userId, role) {
18118
+ await callRPC("bichat.session.members.updateRole", {
18119
+ sessionId,
18120
+ userId,
18121
+ role: role.toUpperCase()
18122
+ });
18123
+ }
18124
+ async function removeSessionMember(callRPC, sessionId, userId) {
18125
+ await callRPC("bichat.session.members.remove", { sessionId, userId });
18126
+ }
17196
18127
 
17197
18128
  // ui/src/bichat/utils/sseParser.ts
17198
18129
  function* processDataLines(lines) {
@@ -17805,7 +18736,12 @@ async function uploadSessionArtifacts(callRPC, sessionId, files, uploadFileFn) {
17805
18736
  const data = await callRPC("bichat.session.uploadArtifacts", {
17806
18737
  sessionId,
17807
18738
  attachments: uploads.map((upload) => ({
17808
- uploadId: upload.id
18739
+ id: String(upload.id),
18740
+ filename: upload.name,
18741
+ uploadId: upload.id,
18742
+ mimeType: upload.mimetype || "application/octet-stream",
18743
+ sizeBytes: upload.size,
18744
+ url: upload.url
17809
18745
  }))
17810
18746
  });
17811
18747
  return {
@@ -17932,6 +18868,24 @@ var HttpDataSource = class {
17932
18868
  async compactSessionHistory(sessionId) {
17933
18869
  return compactSessionHistory(this.boundCallRPC, sessionId);
17934
18870
  }
18871
+ async listUsers() {
18872
+ return listUsers(this.boundCallRPC);
18873
+ }
18874
+ async listAllSessions(options) {
18875
+ return listAllSessions(this.boundCallRPC, options);
18876
+ }
18877
+ async listSessionMembers(sessionId) {
18878
+ return listSessionMembers(this.boundCallRPC, sessionId);
18879
+ }
18880
+ async addSessionMember(sessionId, userId, role) {
18881
+ return addSessionMember(this.boundCallRPC, sessionId, userId, role);
18882
+ }
18883
+ async updateSessionMemberRole(sessionId, userId, role) {
18884
+ return updateSessionMemberRole(this.boundCallRPC, sessionId, userId, role);
18885
+ }
18886
+ async removeSessionMember(sessionId, userId) {
18887
+ return removeSessionMember(this.boundCallRPC, sessionId, userId);
18888
+ }
17935
18889
  // -------------------------------------------------------------------------
17936
18890
  // Message transport (delegates to MessageTransport)
17937
18891
  // -------------------------------------------------------------------------
@@ -18067,6 +19021,7 @@ exports.AttachmentGrid = MemoizedAttachmentGrid;
18067
19021
  exports.AttachmentPreview = AttachmentPreview_default;
18068
19022
  exports.AttachmentUpload = AttachmentUpload_default;
18069
19023
  exports.Avatar = Avatar;
19024
+ exports.AvatarStack = AvatarStack;
18070
19025
  exports.BiChatLayout = BiChatLayout;
18071
19026
  exports.Bubble = Bubble;
18072
19027
  exports.CHART_VISUAL = CHART_VISUAL;
@@ -18110,6 +19065,7 @@ exports.SessionArtifactList = SessionArtifactList;
18110
19065
  exports.SessionArtifactPreview = SessionArtifactPreview;
18111
19066
  exports.SessionArtifactsPanel = SessionArtifactsPanel;
18112
19067
  exports.SessionItem = SessionItem_default;
19068
+ exports.SessionMembersModal = SessionMembersModal;
18113
19069
  exports.SessionSkeleton = SessionSkeleton;
18114
19070
  exports.Sidebar = Sidebar2;
18115
19071
  exports.Skeleton = MemoizedSkeleton;
@@ -18176,6 +19132,7 @@ exports.useActionButtonContext = useActionButtonContext;
18176
19132
  exports.useAttachments = useAttachments;
18177
19133
  exports.useAutoScroll = useAutoScroll;
18178
19134
  exports.useAvatarContext = useAvatarContext;
19135
+ exports.useBichatRouter = useBichatRouter;
18179
19136
  exports.useBubbleContext = useBubbleContext;
18180
19137
  exports.useChatInput = useChatInput;
18181
19138
  exports.useChatMessaging = useChatMessaging;
@@ -18183,6 +19140,7 @@ exports.useChatSession = useChatSession;
18183
19140
  exports.useConfig = useConfig;
18184
19141
  exports.useDataTable = useDataTable;
18185
19142
  exports.useFocusTrap = useFocusTrap;
19143
+ exports.useHttpDataSourceConfigFromApplet = useHttpDataSourceConfigFromApplet;
18186
19144
  exports.useImageGallery = useImageGallery;
18187
19145
  exports.useIotaContext = useIotaContext;
18188
19146
  exports.useKeyboardShortcuts = useKeyboardShortcuts;