@iota-uz/sdk 0.4.22 → 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.
@@ -2,7 +2,7 @@ import React, { createContext, memo, useRef, useEffect, useCallback, useState, u
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import ReactApexChart from 'react-apexcharts';
4
4
  import ApexCharts from 'apexcharts';
5
- import { CaretUp, CaretDown, DotsThreeVertical, Check, Copy, X, Columns, ArrowsIn, ArrowsOut, Warning, ArrowClockwise, Paperclip, PaperPlaneRight, ChartBar, FileText, Lightbulb, CircleNotch, ArrowUUpLeft, PencilSimple, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Image, MagnifyingGlass, DownloadSimple, ArrowCounterClockwise, Bug, ArrowUp, ArrowDown, Stack, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, WarningCircle, FilePdf, FileXls, FileCsv, FileDoc, FileCode, File as File$1, MagnifyingGlassMinus, MagnifyingGlassPlus, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, ArrowSquareOut, Wrench, ClockCounterClockwise, Package, Plus, ArrowsCounterClockwise, ChatCircle, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, Table, SpinnerGap, FloppyDisk, Sidebar } from '@phosphor-icons/react';
5
+ import { CaretUp, CaretDown, DotsThreeVertical, Check, Copy, X, Columns, ArrowsIn, ArrowsOut, Warning, ArrowClockwise, Paperclip, PaperPlaneRight, ChartBar, FileText, Lightbulb, CircleNotch, ArrowUUpLeft, PencilSimple, Bookmark, ArrowsClockwise, Archive, Trash, UsersThree, DotsThree, Image, MagnifyingGlass, DownloadSimple, ArrowCounterClockwise, Bug, ArrowUp, ArrowDown, Stack, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, WarningCircle, FilePdf, FileXls, FileCsv, FileDoc, FileCode, File as File$1, MagnifyingGlassMinus, MagnifyingGlassPlus, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, ArrowSquareOut, Wrench, ClockCounterClockwise, Package, Plus, Crown, UserPlus, ArrowsCounterClockwise, ChatCircle, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, Table, SpinnerGap, FloppyDisk, ShareNetwork, Sidebar } from '@phosphor-icons/react';
6
6
  import { Prism } from 'react-syntax-highlighter';
7
7
  import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
8
8
  import ReactMarkdown from 'react-markdown';
@@ -12,7 +12,7 @@ import rehypeKatex from 'rehype-katex';
12
12
  import { motion, useMotionValue, useTransform, AnimatePresence, MotionConfig, useReducedMotion } from 'framer-motion';
13
13
  import 'react-dom/client';
14
14
  import { differenceInMinutes, differenceInHours, differenceInDays, format, isSameDay, startOfDay, isToday, isYesterday } from 'date-fns';
15
- import { Menu, MenuButton, MenuItems, MenuItem, Popover, PopoverButton, PopoverPanel, Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Transition } from '@headlessui/react';
15
+ import { Menu, MenuButton, MenuItems, MenuItem, Popover, PopoverButton, PopoverPanel, Dialog, DialogBackdrop, DialogPanel, Transition } from '@headlessui/react';
16
16
  import { createPortal } from 'react-dom';
17
17
 
18
18
  var __defProp = Object.defineProperty;
@@ -1868,7 +1868,7 @@ function pendingQuestionFromInterrupt(interrupt, fallbackTurnId) {
1868
1868
  options: Array.isArray(question.options) ? question.options.filter((option) => !!option && typeof option.id === "string").map((option) => ({
1869
1869
  id: option.id,
1870
1870
  label: typeof option.label === "string" ? option.label : "",
1871
- value: typeof option.label === "string" ? option.label : ""
1871
+ value: option.id
1872
1872
  })) : []
1873
1873
  })) : [];
1874
1874
  return {
@@ -3012,14 +3012,9 @@ var ChatMachine = class {
3012
3012
  if (!curSessionId || !curPendingQuestion) {
3013
3013
  return;
3014
3014
  }
3015
- const previousTurns = this.state.messaging.turns;
3016
3015
  this._updateMessaging({ loading: true });
3017
3016
  this._updateSession({ error: null, errorRetryable: false });
3018
3017
  const previousPendingQuestion = curPendingQuestion;
3019
- this._updateMessaging({
3020
- pendingQuestion: null,
3021
- turns: applyTurnLifecycleForPendingQuestion(previousTurns, null)
3022
- });
3023
3018
  try {
3024
3019
  const result = await this.dataSource.submitQuestionAnswers(
3025
3020
  curSessionId,
@@ -3030,60 +3025,28 @@ var ChatMachine = class {
3030
3025
  return;
3031
3026
  }
3032
3027
  if (result.success) {
3033
- if (curSessionId !== "new") {
3034
- try {
3035
- const fetchResult = await this.dataSource.fetchSession(curSessionId);
3036
- if (this.disposed) {
3037
- return;
3038
- }
3039
- if (fetchResult) {
3040
- this._updateSession({ session: fetchResult.session });
3041
- const hasMalformedRefresh = previousTurns.length > 0 && Array.isArray(fetchResult.turns) && fetchResult.turns.length === 0;
3042
- if (hasMalformedRefresh) {
3043
- console.warn("[ChatMachine] Preserving previous turns due to empty post-HITL refetch payload", {
3044
- sessionId: curSessionId,
3045
- previousTurnCount: previousTurns.length
3046
- });
3047
- this._updateSession({
3048
- error: "Failed to fully refresh session. Showing last known messages.",
3049
- errorRetryable: true
3050
- });
3051
- this._updateMessaging({
3052
- pendingQuestion: fetchResult.pendingQuestion || null,
3053
- turns: applyTurnLifecycleForPendingQuestion(
3054
- previousTurns,
3055
- fetchResult.pendingQuestion || null
3056
- )
3057
- });
3058
- } else {
3059
- this._setTurnsFromFetch(fetchResult.turns, fetchResult.pendingQuestion || null);
3060
- }
3061
- } else {
3062
- this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
3063
- }
3064
- } catch (fetchErr) {
3065
- if (this.disposed) {
3066
- return;
3067
- }
3068
- const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
3069
- this._updateSession({ error: normalized.userMessage, errorRetryable: true });
3028
+ if (result.data) {
3029
+ this._updateSession({ session: result.data.session });
3030
+ this._setTurnsFromFetch(result.data.turns, result.data.pendingQuestion || null);
3031
+ } else if (curSessionId !== "new") {
3032
+ const fetchResult = await this.dataSource.fetchSession(curSessionId);
3033
+ if (this.disposed) {
3034
+ return;
3035
+ }
3036
+ if (fetchResult) {
3037
+ this._updateSession({ session: fetchResult.session });
3038
+ this._setTurnsFromFetch(fetchResult.turns, fetchResult.pendingQuestion || null);
3039
+ } else {
3040
+ this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
3070
3041
  }
3071
3042
  }
3072
3043
  } else {
3073
- this._updateMessaging({
3074
- pendingQuestion: previousPendingQuestion,
3075
- turns: applyTurnLifecycleForPendingQuestion(previousTurns, previousPendingQuestion)
3076
- });
3077
3044
  this._updateSession({ error: result.error || "Failed to submit answers", errorRetryable: false });
3078
3045
  }
3079
3046
  } catch (err) {
3080
3047
  if (this.disposed) {
3081
3048
  return;
3082
3049
  }
3083
- this._updateMessaging({
3084
- pendingQuestion: previousPendingQuestion,
3085
- turns: applyTurnLifecycleForPendingQuestion(previousTurns, previousPendingQuestion)
3086
- });
3087
3050
  const normalized = normalizeRPCError(err, "Failed to submit answers");
3088
3051
  this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
3089
3052
  } finally {
@@ -3328,12 +3291,151 @@ function useBranding() {
3328
3291
  }, [context.extensions?.branding, t]);
3329
3292
  return branding;
3330
3293
  }
3331
- function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
3294
+ function hashString(str) {
3295
+ let hash = 0;
3296
+ for (let i = 0; i < str.length; i++) {
3297
+ const char = str.charCodeAt(i);
3298
+ hash = (hash << 5) - hash + char;
3299
+ hash = hash & hash;
3300
+ }
3301
+ return Math.abs(hash);
3302
+ }
3303
+ var colorPalette = [
3304
+ { bg: "bg-blue-500", text: "text-white" },
3305
+ { bg: "bg-green-500", text: "text-white" },
3306
+ { bg: "bg-purple-500", text: "text-white" },
3307
+ { bg: "bg-pink-500", text: "text-white" },
3308
+ { bg: "bg-indigo-500", text: "text-white" },
3309
+ { bg: "bg-teal-500", text: "text-white" },
3310
+ { bg: "bg-orange-500", text: "text-white" },
3311
+ { bg: "bg-cyan-500", text: "text-white" },
3312
+ { bg: "bg-amber-500", text: "text-white" },
3313
+ { bg: "bg-lime-500", text: "text-white" }
3314
+ ];
3315
+ var sizeClasses = {
3316
+ xs: "w-6 h-6 text-[10px]",
3317
+ sm: "w-8 h-8 text-xs",
3318
+ md: "w-10 h-10 text-sm",
3319
+ lg: "w-12 h-12 text-base"
3320
+ };
3321
+ function UserAvatar({
3322
+ firstName,
3323
+ lastName,
3324
+ initials: providedInitials,
3325
+ size = "md",
3326
+ className = ""
3327
+ }) {
3328
+ const derivedInitials = (() => {
3329
+ const firstChar = firstName?.trim()?.charAt(0) || "";
3330
+ const lastChar = lastName?.trim()?.charAt(0) || "";
3331
+ const combined = `${firstChar}${lastChar}`.trim();
3332
+ return combined || "U";
3333
+ })();
3334
+ const initials = (providedInitials?.trim() || derivedInitials).toUpperCase();
3335
+ const fullName = `${firstName}${lastName}`;
3336
+ const colorIndex = hashString(fullName) % colorPalette.length;
3337
+ const colors = colorPalette[colorIndex];
3338
+ return /* @__PURE__ */ jsx(
3339
+ "div",
3340
+ {
3341
+ className: `
3342
+ ${sizeClasses[size]}
3343
+ ${colors.bg}
3344
+ ${colors.text}
3345
+ ${className}
3346
+ rounded-full
3347
+ flex
3348
+ items-center
3349
+ justify-center
3350
+ font-semibold
3351
+ flex-shrink-0
3352
+ select-none
3353
+ `,
3354
+ "aria-label": `${firstName} ${lastName}`,
3355
+ title: `${firstName} ${lastName}`,
3356
+ children: initials
3357
+ }
3358
+ );
3359
+ }
3360
+ var MemoizedUserAvatar = memo(UserAvatar);
3361
+ MemoizedUserAvatar.displayName = "UserAvatar";
3362
+ var overlapClasses = {
3363
+ xs: "-ml-1.5",
3364
+ sm: "-ml-2"
3365
+ };
3366
+ var badgeSizeClasses = {
3367
+ xs: "w-6 h-6 text-[10px]",
3368
+ sm: "w-8 h-8 text-xs"
3369
+ };
3370
+ function AvatarStackInner({
3371
+ users,
3372
+ max = 3,
3373
+ size = "sm",
3374
+ onClick,
3375
+ className = ""
3376
+ }) {
3377
+ const visible = users.slice(0, max);
3378
+ const overflow = users.length - max;
3379
+ const interactive = typeof onClick === "function";
3380
+ const overlap = overlapClasses[size];
3381
+ const badgeSize = badgeSizeClasses[size];
3382
+ const handleKeyDown = (e) => {
3383
+ if (interactive && (e.key === "Enter" || e.key === " ")) {
3384
+ e.preventDefault();
3385
+ onClick();
3386
+ }
3387
+ };
3388
+ return /* @__PURE__ */ jsxs(
3389
+ "div",
3390
+ {
3391
+ className: `inline-flex items-center ${interactive ? "cursor-pointer transition-opacity hover:opacity-80" : ""} ${className}`,
3392
+ onClick: interactive ? onClick : void 0,
3393
+ onKeyDown: interactive ? handleKeyDown : void 0,
3394
+ role: interactive ? "button" : void 0,
3395
+ tabIndex: interactive ? 0 : void 0,
3396
+ "aria-label": interactive ? `${users.length} members` : void 0,
3397
+ children: [
3398
+ visible.map((user, i) => /* @__PURE__ */ jsx(
3399
+ "div",
3400
+ {
3401
+ className: `${i > 0 ? overlap : ""} ring-2 ring-white dark:ring-gray-900 rounded-full`,
3402
+ style: { zIndex: visible.length - i },
3403
+ children: /* @__PURE__ */ jsx(
3404
+ MemoizedUserAvatar,
3405
+ {
3406
+ firstName: user.firstName,
3407
+ lastName: user.lastName,
3408
+ initials: user.initials,
3409
+ size
3410
+ }
3411
+ )
3412
+ },
3413
+ `${user.firstName}-${user.lastName}-${i}`
3414
+ )),
3415
+ overflow > 0 && /* @__PURE__ */ jsxs(
3416
+ "div",
3417
+ {
3418
+ 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`,
3419
+ style: { zIndex: 0 },
3420
+ children: [
3421
+ "+",
3422
+ overflow
3423
+ ]
3424
+ }
3425
+ )
3426
+ ]
3427
+ }
3428
+ );
3429
+ }
3430
+ var AvatarStack = memo(AvatarStackInner);
3431
+ AvatarStack.displayName = "AvatarStack";
3432
+ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot, members, onMembersClick }) {
3332
3433
  const { t } = useTranslation();
3333
3434
  const branding = useBranding();
3334
3435
  const BackButton = onBack ? /* @__PURE__ */ jsx(
3335
3436
  "button",
3336
3437
  {
3438
+ type: "button",
3337
3439
  onClick: onBack,
3338
3440
  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",
3339
3441
  "aria-label": t("BiChat.Chat.GoBack"),
@@ -3352,23 +3454,42 @@ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
3352
3454
  ] }) });
3353
3455
  }
3354
3456
  const resolvedSessionTitle = session.title?.trim() || t("BiChat.Chat.NewChat");
3457
+ const isGroupSession = Boolean(session.isGroup || session.memberCount && session.memberCount > 1);
3458
+ const memberCount = session.memberCount ?? 0;
3459
+ const stackUsers = members && members.length > 0 ? members : [];
3355
3460
  return /* @__PURE__ */ jsx("header", { className: "bichat-header border-b border-gray-200 dark:border-gray-700 px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3356
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3461
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3357
3462
  BackButton,
3358
3463
  Logo,
3359
- /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold text-[var(--bichat-text)]", children: resolvedSessionTitle }),
3360
- session.pinned && /* @__PURE__ */ jsx(
3361
- "svg",
3362
- {
3363
- className: "w-4 h-4 text-[var(--bichat-primary)]",
3364
- fill: "currentColor",
3365
- viewBox: "0 0 20 20",
3366
- "aria-label": t("BiChat.Chat.Pinned"),
3367
- children: /* @__PURE__ */ 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" })
3368
- }
3369
- )
3464
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3465
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
3466
+ /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold text-[var(--bichat-text)] truncate", children: resolvedSessionTitle }),
3467
+ session.pinned && /* @__PURE__ */ jsx(
3468
+ "svg",
3469
+ {
3470
+ className: "w-4 h-4 text-[var(--bichat-primary)] flex-shrink-0",
3471
+ fill: "currentColor",
3472
+ viewBox: "0 0 20 20",
3473
+ role: "img",
3474
+ "aria-label": t("BiChat.Chat.Pinned"),
3475
+ children: /* @__PURE__ */ 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" })
3476
+ }
3477
+ ),
3478
+ isGroupSession && stackUsers.length > 0 && /* @__PURE__ */ jsx(
3479
+ AvatarStack,
3480
+ {
3481
+ users: stackUsers,
3482
+ max: 3,
3483
+ size: "xs",
3484
+ onClick: onMembersClick,
3485
+ className: "flex-shrink-0"
3486
+ }
3487
+ )
3488
+ ] }),
3489
+ isGroupSession && memberCount > 0 && /* @__PURE__ */ 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)) })
3490
+ ] })
3370
3491
  ] }),
3371
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3492
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
3372
3493
  readOnly && /* @__PURE__ */ 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") }),
3373
3494
  session.status === "archived" && /* @__PURE__ */ 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") }),
3374
3495
  actionsSlot
@@ -4243,6 +4364,7 @@ function UserMessage({
4243
4364
  turn,
4244
4365
  turnId,
4245
4366
  initials = "U",
4367
+ authorName,
4246
4368
  slots,
4247
4369
  classNames: classNameOverrides,
4248
4370
  onCopy,
@@ -4450,6 +4572,16 @@ function UserMessage({
4450
4572
  };
4451
4573
  return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
4452
4574
  /* @__PURE__ */ jsxs("div", { className: classes.wrapper, children: [
4575
+ authorName && /* @__PURE__ */ jsx(
4576
+ "span",
4577
+ {
4578
+ id: `${turn.id}-author`,
4579
+ role: "note",
4580
+ "aria-label": authorName,
4581
+ className: "mb-1 px-1 text-[11px] text-right text-gray-500 dark:text-gray-400",
4582
+ children: authorName
4583
+ }
4584
+ ),
4453
4585
  normalizedAttachments.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.attachments, children: renderSlot(
4454
4586
  slots?.attachments,
4455
4587
  attachmentsSlotProps,
@@ -4461,20 +4593,28 @@ function UserMessage({
4461
4593
  }
4462
4594
  )
4463
4595
  ) }),
4464
- turn.content && /* @__PURE__ */ jsx("div", { ref: bubbleRef, className: classes.bubble, children: /* @__PURE__ */ jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsx(
4465
- EditForm,
4596
+ turn.content && /* @__PURE__ */ jsx(
4597
+ "div",
4466
4598
  {
4467
- draftContent,
4468
- onDraftChange: handleDraftChange,
4469
- onSave: handleEditSave,
4470
- onCancel: handleEditCancel,
4471
- onKeyDown: handleEditKeyDown,
4472
- textareaRef: editTextareaRef,
4473
- disabled: false,
4474
- originalContent: turn.content,
4475
- t
4599
+ ref: bubbleRef,
4600
+ className: classes.bubble,
4601
+ "aria-describedby": authorName ? `${turn.id}-author` : void 0,
4602
+ children: /* @__PURE__ */ jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsx(
4603
+ EditForm,
4604
+ {
4605
+ draftContent,
4606
+ onDraftChange: handleDraftChange,
4607
+ onSave: handleEditSave,
4608
+ onCancel: handleEditCancel,
4609
+ onKeyDown: handleEditKeyDown,
4610
+ textareaRef: editTextareaRef,
4611
+ disabled: false,
4612
+ originalContent: turn.content,
4613
+ t
4614
+ }
4615
+ ) : renderSlot(slots?.content, contentSlotProps, turn.content) })
4476
4616
  }
4477
- ) : renderSlot(slots?.content, contentSlotProps, turn.content) }) }),
4617
+ ),
4478
4618
  !hideActions && /* @__PURE__ */ jsx("div", { className: `${classes.actions} ${isCopied ? "opacity-100" : ""}`, children: renderSlot(
4479
4619
  slots?.actions,
4480
4620
  actionsSlotProps,
@@ -4523,19 +4663,25 @@ function UserTurnView({
4523
4663
  turn,
4524
4664
  slots,
4525
4665
  classNames,
4526
- initials = "U",
4666
+ initials,
4527
4667
  hideAvatar,
4528
4668
  hideActions,
4529
4669
  hideTimestamp,
4530
- allowEdit
4670
+ allowEdit,
4671
+ showAuthorName = false
4531
4672
  }) {
4532
4673
  const { handleEdit, handleCopy } = useChatMessaging();
4674
+ const author = turn.userTurn.author;
4675
+ const fullName = [author?.firstName || "", author?.lastName || ""].join(" ").trim();
4676
+ const authorName = showAuthorName && fullName.length > 0 ? fullName : void 0;
4677
+ const resolvedInitials = initials ?? author?.initials ?? "U";
4533
4678
  return /* @__PURE__ */ jsx(
4534
4679
  UserMessage,
4535
4680
  {
4536
4681
  turn: turn.userTurn,
4537
4682
  turnId: turn.id,
4538
- initials,
4683
+ initials: resolvedInitials,
4684
+ authorName,
4539
4685
  slots,
4540
4686
  classNames,
4541
4687
  onCopy: handleCopy,
@@ -6517,13 +6663,13 @@ function InlineQuestionForm({ pendingQuestion }) {
6517
6663
  const currentAnswer = answers[currentQuestion?.id];
6518
6664
  const currentOtherText = otherTexts[currentQuestion?.id] || "";
6519
6665
  const handleOptionChange = useCallback(
6520
- (optionLabel, checked) => {
6666
+ (optionID, checked) => {
6521
6667
  if (!currentQuestion) {
6522
6668
  return;
6523
6669
  }
6524
6670
  const questionId = currentQuestion.id;
6525
6671
  const existingAnswer = answers[questionId] || { options: [] };
6526
- const isOtherOption = optionLabel === "__other__";
6672
+ const isOtherOption = optionID === "__other__";
6527
6673
  const isMultiSelect2 = currentQuestion.type === "MULTIPLE_CHOICE";
6528
6674
  if (isOtherOption) {
6529
6675
  setAnswers({
@@ -6538,14 +6684,14 @@ function InlineQuestionForm({ pendingQuestion }) {
6538
6684
  let newOptions;
6539
6685
  if (isMultiSelect2) {
6540
6686
  if (!checked) {
6541
- newOptions = existingAnswer.options.filter((o) => o !== optionLabel);
6542
- } else if (existingAnswer.options.includes(optionLabel)) {
6687
+ newOptions = existingAnswer.options.filter((o) => o !== optionID);
6688
+ } else if (existingAnswer.options.includes(optionID)) {
6543
6689
  newOptions = existingAnswer.options;
6544
6690
  } else {
6545
- newOptions = [...existingAnswer.options, optionLabel];
6691
+ newOptions = [...existingAnswer.options, optionID];
6546
6692
  }
6547
6693
  } else {
6548
- newOptions = checked ? [optionLabel] : [];
6694
+ newOptions = checked ? [optionID] : [];
6549
6695
  }
6550
6696
  setAnswers({
6551
6697
  ...answers,
@@ -6636,7 +6782,7 @@ function InlineQuestionForm({ pendingQuestion }) {
6636
6782
  const options = (currentQuestion.options || []).filter((option) => Boolean(option && typeof option.label === "string")).map((option, index) => ({
6637
6783
  id: option.id || `${currentQuestion.id}-option-${index}`,
6638
6784
  label: option.label,
6639
- value: option.value || option.label
6785
+ value: option.value || option.id || `${currentQuestion.id}-option-${index}`
6640
6786
  }));
6641
6787
  const isOtherSelected = currentAnswer?.customText !== void 0;
6642
6788
  const canProceed = isCurrentAnswerValid();
@@ -6684,7 +6830,7 @@ function InlineQuestionForm({ pendingQuestion }) {
6684
6830
  ] }),
6685
6831
  /* @__PURE__ */ jsxs("div", { className: "px-4 pb-2 space-y-1.5", children: [
6686
6832
  options.map((option) => {
6687
- const isSelected = currentAnswer?.options.includes(option.label) || false;
6833
+ const isSelected = currentAnswer?.options.includes(option.id) || false;
6688
6834
  return /* @__PURE__ */ jsxs(
6689
6835
  "label",
6690
6836
  {
@@ -6712,7 +6858,7 @@ function InlineQuestionForm({ pendingQuestion }) {
6712
6858
  name: `question-${currentQuestion.id}`,
6713
6859
  value: option.value,
6714
6860
  checked: isSelected,
6715
- onChange: (e) => handleOptionChange(option.label, e.target.checked),
6861
+ onChange: (e) => handleOptionChange(option.id, e.target.checked),
6716
6862
  className: "sr-only"
6717
6863
  }
6718
6864
  ),
@@ -8530,7 +8676,7 @@ function StreamingBubble({ content, normalizedContent }) {
8530
8676
  }
8531
8677
  function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readOnly }) {
8532
8678
  const { t } = useTranslation();
8533
- const { currentSessionId, fetching } = useChatSession();
8679
+ const { session, currentSessionId, fetching } = useChatSession();
8534
8680
  const {
8535
8681
  turns,
8536
8682
  streamingContent,
@@ -8545,6 +8691,7 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8545
8691
  () => streamingContent ? normalizeStreamingMarkdown(streamingContent) : "",
8546
8692
  [streamingContent]
8547
8693
  );
8694
+ const showAuthorNames = Boolean(session?.isGroup);
8548
8695
  const showEphemeral = showActivityTrace || showTypingIndicator;
8549
8696
  return /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0", children: [
8550
8697
  /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full overflow-y-auto overflow-x-hidden px-4 py-6", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto space-y-6", children: [
@@ -8554,6 +8701,10 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8554
8701
  const prevDate = index > 0 ? new Date(turns[index - 1].createdAt) : null;
8555
8702
  const showDateSeparator = !!prevDate && !isSameDay(turnDate, prevDate);
8556
8703
  const isLast = index === turns.length - 1;
8704
+ const userTurnProps = {
8705
+ allowEdit: readOnly ? false : isLast,
8706
+ showAuthorName: showAuthorNames
8707
+ };
8557
8708
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
8558
8709
  showDateSeparator && /* @__PURE__ */ jsx(DateSeparator, { date: turnDate }),
8559
8710
  /* @__PURE__ */ jsx(
@@ -8563,7 +8714,7 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
8563
8714
  isLastTurn: isLast,
8564
8715
  renderUserTurn,
8565
8716
  renderAssistantTurn,
8566
- userTurnProps: readOnly ? { allowEdit: false } : { allowEdit: isLast },
8717
+ userTurnProps,
8567
8718
  assistantTurnProps: readOnly ? { allowRegenerate: false } : void 0
8568
8719
  }
8569
8720
  )
@@ -10769,47 +10920,665 @@ function SessionArtifactsPanel({
10769
10920
  }
10770
10921
  );
10771
10922
  }
10923
+ var DialogContext = createContext(null);
10924
+ var FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
10925
+ function InlineDialog({ open, onClose, className, children }) {
10926
+ const containerRef = useRef(null);
10927
+ const previousFocusRef = useRef(null);
10928
+ useEffect(() => {
10929
+ if (!open) {
10930
+ return;
10931
+ }
10932
+ previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
10933
+ const container = containerRef.current;
10934
+ if (!container) {
10935
+ return;
10936
+ }
10937
+ const getFocusable = () => Array.from(
10938
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
10939
+ );
10940
+ const handler = (e) => {
10941
+ if (e.key === "Escape") {
10942
+ onClose();
10943
+ return;
10944
+ }
10945
+ if (e.key !== "Tab") {
10946
+ return;
10947
+ }
10948
+ const focusable = getFocusable();
10949
+ if (focusable.length === 0) {
10950
+ e.preventDefault();
10951
+ container.focus();
10952
+ return;
10953
+ }
10954
+ const first = focusable[0];
10955
+ const last = focusable[focusable.length - 1];
10956
+ if (e.shiftKey && document.activeElement === first) {
10957
+ e.preventDefault();
10958
+ last.focus();
10959
+ } else if (!e.shiftKey && document.activeElement === last) {
10960
+ e.preventDefault();
10961
+ first.focus();
10962
+ }
10963
+ };
10964
+ (getFocusable()[0] ?? container)?.focus();
10965
+ container.addEventListener("keydown", handler);
10966
+ return () => {
10967
+ container.removeEventListener("keydown", handler);
10968
+ previousFocusRef.current?.focus();
10969
+ };
10970
+ }, [open, onClose]);
10971
+ if (!open) {
10972
+ return null;
10973
+ }
10974
+ return /* @__PURE__ */ jsx(DialogContext.Provider, { value: onClose, children: /* @__PURE__ */ jsx(
10975
+ "div",
10976
+ {
10977
+ ref: containerRef,
10978
+ className,
10979
+ onClick: onClose,
10980
+ tabIndex: -1,
10981
+ children
10982
+ }
10983
+ ) });
10984
+ }
10985
+ function InlineDialogBackdrop(props) {
10986
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", ...props });
10987
+ }
10988
+ function InlineDialogPanel({
10989
+ children,
10990
+ onClick,
10991
+ ...rest
10992
+ }) {
10993
+ const ref = useRef(null);
10994
+ useEffect(() => {
10995
+ if (!ref.current) {
10996
+ return;
10997
+ }
10998
+ const target = ref.current.querySelector("[data-autofocus]") ?? ref.current.querySelector(
10999
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
11000
+ );
11001
+ target?.focus();
11002
+ }, []);
11003
+ return /* @__PURE__ */ jsx(
11004
+ "div",
11005
+ {
11006
+ ref,
11007
+ role: "dialog",
11008
+ "aria-modal": "true",
11009
+ onClick: (e) => {
11010
+ e.stopPropagation();
11011
+ onClick?.(e);
11012
+ },
11013
+ ...rest,
11014
+ children
11015
+ }
11016
+ );
11017
+ }
11018
+ function InlineDialogTitle(props) {
11019
+ return /* @__PURE__ */ jsx("h2", { ...props });
11020
+ }
11021
+ function InlineDialogDescription(props) {
11022
+ return /* @__PURE__ */ jsx("p", { ...props });
11023
+ }
10772
11024
 
10773
- // ui/src/bichat/components/StreamError.tsx
11025
+ // ui/src/bichat/components/SessionMembersModal.tsx
10774
11026
  init_useTranslation();
10775
- function StreamError({
10776
- error,
10777
- onRetry,
10778
- onRegenerate,
10779
- onDismiss,
10780
- compact = false
11027
+ init_useTranslation();
11028
+ function ConfirmModalBase({
11029
+ isOpen,
11030
+ title,
11031
+ message,
11032
+ onConfirm,
11033
+ onCancel,
11034
+ confirmText,
11035
+ cancelText,
11036
+ isDanger = false
10781
11037
  }) {
10782
11038
  const { t } = useTranslation();
10783
- return /* @__PURE__ */ jsxs(
10784
- motion.div,
10785
- {
10786
- initial: { opacity: 0, y: 10 },
10787
- animate: { opacity: 1, y: 0 },
10788
- exit: { opacity: 0, y: -10 },
10789
- 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`,
10790
- role: "alert",
10791
- children: [
10792
- /* @__PURE__ */ 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__ */ jsx(
10793
- Warning,
11039
+ const resolvedConfirmText = confirmText?.trim() ? confirmText : t("BiChat.Common.Confirm");
11040
+ const resolvedCancelText = cancelText?.trim() ? cancelText : t("BiChat.Common.Cancel");
11041
+ return /* @__PURE__ */ jsxs(InlineDialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
11042
+ /* @__PURE__ */ jsx(InlineDialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11043
+ /* @__PURE__ */ jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ 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: [
11044
+ /* @__PURE__ */ jsx("div", { className: "px-6 pt-6 pb-5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
11045
+ isDanger && /* @__PURE__ */ 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__ */ jsx(WarningCircle, { size: 22, weight: "duotone", className: "text-red-600 dark:text-red-400" }) }),
11046
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
11047
+ /* @__PURE__ */ jsx(InlineDialogTitle, { className: "text-base font-semibold text-gray-900 dark:text-gray-100 leading-snug", children: title }),
11048
+ /* @__PURE__ */ jsx(InlineDialogDescription, { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 leading-relaxed", children: message })
11049
+ ] })
11050
+ ] }) }),
11051
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2.5 px-6 pb-5", children: [
11052
+ /* @__PURE__ */ jsx(
11053
+ "button",
10794
11054
  {
10795
- className: "w-4 h-4 text-red-600 dark:text-red-400",
10796
- weight: "fill"
11055
+ type: "button",
11056
+ onClick: onCancel,
11057
+ ...isDanger ? { "data-autofocus": true } : {},
11058
+ 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",
11059
+ "data-testid": "confirm-modal-cancel",
11060
+ children: resolvedCancelText
10797
11061
  }
10798
- ) }),
10799
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
10800
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
10801
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
10802
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
10803
- onRetry && /* @__PURE__ */ jsxs(
10804
- "button",
10805
- {
10806
- onClick: onRetry,
10807
- 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",
10808
- type: "button",
10809
- children: [
10810
- /* @__PURE__ */ jsx(ArrowClockwise, { className: "w-3.5 h-3.5" }),
10811
- t("BiChat.StreamError.Retry")
10812
- ]
11062
+ ),
11063
+ /* @__PURE__ */ jsx(
11064
+ "button",
11065
+ {
11066
+ type: "button",
11067
+ ...!isDanger ? { "data-autofocus": true } : {},
11068
+ onClick: onConfirm,
11069
+ className: [
11070
+ "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-white",
11071
+ "transition-all duration-150 shadow-sm hover:shadow",
11072
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
11073
+ 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"
11074
+ ].join(" "),
11075
+ "data-testid": "confirm-modal-confirm",
11076
+ children: resolvedConfirmText
11077
+ }
11078
+ )
11079
+ ] })
11080
+ ] }) })
11081
+ ] });
11082
+ }
11083
+ var ConfirmModal = memo(ConfirmModalBase);
11084
+ ConfirmModal.displayName = "ConfirmModal";
11085
+ var ConfirmModal_default = ConfirmModal;
11086
+ var ROLES = ["editor", "viewer"];
11087
+ function RoleSegmentedControl({
11088
+ value,
11089
+ onChange,
11090
+ disabled,
11091
+ size = "md",
11092
+ t
11093
+ }) {
11094
+ const btnBase = size === "sm" ? "px-2 py-0.5 text-[11px]" : "px-3 py-1 text-xs";
11095
+ const currentIndex = ROLES.indexOf(value);
11096
+ const handleKeyDown = (e) => {
11097
+ if (disabled) {
11098
+ return;
11099
+ }
11100
+ let nextIndex = null;
11101
+ if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
11102
+ e.preventDefault();
11103
+ nextIndex = currentIndex <= 0 ? ROLES.length - 1 : currentIndex - 1;
11104
+ } else if (e.key === "ArrowRight" || e.key === "ArrowDown") {
11105
+ e.preventDefault();
11106
+ nextIndex = currentIndex >= ROLES.length - 1 ? 0 : currentIndex + 1;
11107
+ }
11108
+ if (nextIndex !== null) {
11109
+ const nextRole = ROLES[nextIndex];
11110
+ onChange(nextRole);
11111
+ const target = e.currentTarget.querySelector(`[data-role="${nextRole}"]`);
11112
+ target?.focus();
11113
+ }
11114
+ };
11115
+ return /* @__PURE__ */ jsx(
11116
+ "div",
11117
+ {
11118
+ role: "radiogroup",
11119
+ "aria-label": t("BiChat.Share.RoleLabel"),
11120
+ className: "inline-flex rounded-lg border border-gray-200 dark:border-gray-700 p-0.5 bg-gray-50 dark:bg-gray-800/50",
11121
+ onKeyDown: handleKeyDown,
11122
+ children: ROLES.map((role) => /* @__PURE__ */ jsx(
11123
+ "button",
11124
+ {
11125
+ type: "button",
11126
+ role: "radio",
11127
+ "aria-checked": value === role,
11128
+ tabIndex: value === role ? 0 : -1,
11129
+ "data-role": role,
11130
+ disabled,
11131
+ onClick: () => onChange(role),
11132
+ 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"}`,
11133
+ children: role === "editor" ? t("BiChat.Share.RoleEditor") : t("BiChat.Share.RoleViewer")
11134
+ },
11135
+ role
11136
+ ))
11137
+ }
11138
+ );
11139
+ }
11140
+ function MemberSkeleton() {
11141
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-xl px-3 py-2.5", "aria-hidden": "true", children: [
11142
+ /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-700 animate-pulse flex-shrink-0" }),
11143
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
11144
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-28 rounded bg-gray-200 dark:bg-gray-700 animate-pulse" }),
11145
+ /* @__PURE__ */ jsx("div", { className: "h-2.5 w-16 rounded bg-gray-100 dark:bg-gray-800 animate-pulse" })
11146
+ ] }),
11147
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-16 rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" })
11148
+ ] });
11149
+ }
11150
+ function SessionMembersModal({ isOpen, sessionId, dataSource, onClose }) {
11151
+ const headingId = useId();
11152
+ const { t } = useTranslation();
11153
+ const statusTimerRef = useRef();
11154
+ const [loading, setLoading] = useState(false);
11155
+ const [saving, setSaving] = useState(false);
11156
+ const [error, setError] = useState(null);
11157
+ const [users, setUsers] = useState([]);
11158
+ const [members, setMembers] = useState([]);
11159
+ const [selectedUser, setSelectedUser] = useState(null);
11160
+ const [selectedRole, setSelectedRole] = useState("editor");
11161
+ const [query, setQuery] = useState("");
11162
+ const [confirmRemove, setConfirmRemove] = useState(null);
11163
+ const [statusMessage, setStatusMessage] = useState(null);
11164
+ const [dropdownOpen, setDropdownOpen] = useState(false);
11165
+ const [dropdownHighlightIndex, setDropdownHighlightIndex] = useState(0);
11166
+ const dropdownOptionRefs = useRef([]);
11167
+ const canManageMembers = Boolean(
11168
+ dataSource.listUsers && dataSource.listSessionMembers && dataSource.addSessionMember && dataSource.updateSessionMemberRole && dataSource.removeSessionMember
11169
+ );
11170
+ const refresh = useCallback(async () => {
11171
+ if (!sessionId || !canManageMembers) {
11172
+ return;
11173
+ }
11174
+ setLoading(true);
11175
+ setError(null);
11176
+ try {
11177
+ const [usersData, membersData] = await Promise.all([
11178
+ dataSource.listUsers(),
11179
+ dataSource.listSessionMembers(sessionId)
11180
+ ]);
11181
+ setUsers(usersData);
11182
+ setMembers(membersData);
11183
+ } catch {
11184
+ setError(t("BiChat.Share.LoadFailed"));
11185
+ } finally {
11186
+ setLoading(false);
11187
+ }
11188
+ }, [canManageMembers, dataSource, sessionId, t]);
11189
+ useEffect(() => {
11190
+ if (!isOpen) {
11191
+ return;
11192
+ }
11193
+ void refresh();
11194
+ }, [isOpen, refresh]);
11195
+ useEffect(() => {
11196
+ if (!isOpen) {
11197
+ setQuery("");
11198
+ setSelectedUser(null);
11199
+ setSelectedRole("editor");
11200
+ setError(null);
11201
+ setConfirmRemove(null);
11202
+ setStatusMessage(null);
11203
+ setDropdownOpen(false);
11204
+ setDropdownHighlightIndex(0);
11205
+ }
11206
+ }, [isOpen]);
11207
+ const memberIDs = useMemo(() => new Set(members.map((m) => m.user.id)), [members]);
11208
+ const availableUsers = useMemo(
11209
+ () => users.filter((user) => !memberIDs.has(user.id)),
11210
+ [users, memberIDs]
11211
+ );
11212
+ const filteredUsers = useMemo(() => {
11213
+ if (!query.trim()) {
11214
+ return availableUsers;
11215
+ }
11216
+ const q = query.toLowerCase();
11217
+ return availableUsers.filter(
11218
+ (u) => u.firstName.toLowerCase().includes(q) || u.lastName.toLowerCase().includes(q) || `${u.firstName} ${u.lastName}`.toLowerCase().includes(q)
11219
+ );
11220
+ }, [availableUsers, query]);
11221
+ useEffect(() => {
11222
+ setDropdownHighlightIndex(
11223
+ (i) => Math.min(Math.max(0, i), Math.max(0, filteredUsers.length - 1))
11224
+ );
11225
+ }, [filteredUsers.length]);
11226
+ useEffect(() => () => clearTimeout(statusTimerRef.current), []);
11227
+ const flashStatus = useCallback((msg) => {
11228
+ clearTimeout(statusTimerRef.current);
11229
+ setStatusMessage(msg);
11230
+ statusTimerRef.current = setTimeout(() => setStatusMessage(null), 3e3);
11231
+ }, []);
11232
+ const handleAdd = useCallback(async () => {
11233
+ if (!sessionId || !selectedUser || !dataSource.addSessionMember) {
11234
+ return;
11235
+ }
11236
+ setSaving(true);
11237
+ setError(null);
11238
+ try {
11239
+ await dataSource.addSessionMember(sessionId, selectedUser.id, selectedRole);
11240
+ setSelectedUser(null);
11241
+ setQuery("");
11242
+ flashStatus(t("BiChat.Share.MemberAdded"));
11243
+ await refresh();
11244
+ } catch {
11245
+ setError(t("BiChat.Share.AddFailed"));
11246
+ } finally {
11247
+ setSaving(false);
11248
+ }
11249
+ }, [sessionId, selectedUser, selectedRole, dataSource.addSessionMember, refresh, t, flashStatus]);
11250
+ const handleUpdateRole = useCallback(async (userId, role) => {
11251
+ if (!sessionId || !dataSource.updateSessionMemberRole) {
11252
+ return;
11253
+ }
11254
+ setSaving(true);
11255
+ setError(null);
11256
+ try {
11257
+ await dataSource.updateSessionMemberRole(sessionId, userId, role);
11258
+ await refresh();
11259
+ } catch {
11260
+ setError(t("BiChat.Share.UpdateFailed"));
11261
+ } finally {
11262
+ setSaving(false);
11263
+ }
11264
+ }, [sessionId, dataSource.updateSessionMemberRole, refresh]);
11265
+ const handleRemove = useCallback(async (userId) => {
11266
+ if (!sessionId || !dataSource.removeSessionMember) {
11267
+ return;
11268
+ }
11269
+ setSaving(true);
11270
+ setError(null);
11271
+ try {
11272
+ await dataSource.removeSessionMember(sessionId, userId);
11273
+ flashStatus(t("BiChat.Share.MemberRemoved"));
11274
+ await refresh();
11275
+ } catch {
11276
+ setError(t("BiChat.Share.RemoveFailed"));
11277
+ } finally {
11278
+ setSaving(false);
11279
+ }
11280
+ }, [sessionId, dataSource.removeSessionMember, refresh, flashStatus]);
11281
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
11282
+ /* @__PURE__ */ jsxs(InlineDialog, { open: isOpen, onClose, className: "relative z-40", children: [
11283
+ /* @__PURE__ */ jsx(InlineDialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11284
+ /* @__PURE__ */ jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs(
11285
+ InlineDialogPanel,
11286
+ {
11287
+ "aria-labelledby": headingId,
11288
+ className: "bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/30 max-w-md w-full",
11289
+ children: [
11290
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 pt-5 pb-4", children: [
11291
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
11292
+ /* @__PURE__ */ 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__ */ jsx(UsersThree, { size: 18, weight: "duotone", className: "text-primary-600 dark:text-primary-400" }) }),
11293
+ /* @__PURE__ */ jsx(InlineDialogTitle, { id: headingId, className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: t("BiChat.Share.Title") })
11294
+ ] }),
11295
+ /* @__PURE__ */ jsx(
11296
+ "button",
11297
+ {
11298
+ type: "button",
11299
+ onClick: onClose,
11300
+ 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",
11301
+ "aria-label": t("BiChat.Common.Close"),
11302
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
11303
+ }
11304
+ )
11305
+ ] }),
11306
+ /* @__PURE__ */ jsxs("div", { className: "px-6 pb-5 space-y-4", children: [
11307
+ !canManageMembers && /* @__PURE__ */ 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") }),
11308
+ error && /* @__PURE__ */ 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 }),
11309
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: statusMessage }),
11310
+ /* @__PURE__ */ jsxs("div", { children: [
11311
+ /* @__PURE__ */ jsxs("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400", children: [
11312
+ t("BiChat.Share.Members"),
11313
+ !loading && members.length > 0 ? ` (${members.length})` : ""
11314
+ ] }),
11315
+ /* @__PURE__ */ jsx("div", { className: "max-h-64 overflow-y-auto -mx-1 px-1 space-y-1", children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
11316
+ /* @__PURE__ */ jsx(MemberSkeleton, {}),
11317
+ /* @__PURE__ */ jsx(MemberSkeleton, {}),
11318
+ /* @__PURE__ */ jsx(MemberSkeleton, {})
11319
+ ] }) : members.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-gray-400 dark:text-gray-500", children: [
11320
+ /* @__PURE__ */ jsx(UsersThree, { size: 32, weight: "thin", className: "mb-2" }),
11321
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: t("BiChat.Share.Empty") })
11322
+ ] }) : members.map((member) => /* @__PURE__ */ jsxs(
11323
+ "div",
11324
+ {
11325
+ className: "flex items-center gap-3 rounded-xl px-3 py-2 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700/40",
11326
+ children: [
11327
+ /* @__PURE__ */ jsx(
11328
+ MemoizedUserAvatar,
11329
+ {
11330
+ firstName: member.user.firstName,
11331
+ lastName: member.user.lastName,
11332
+ initials: member.user.initials,
11333
+ size: "sm"
11334
+ }
11335
+ ),
11336
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "truncate text-sm font-medium text-gray-900 dark:text-gray-100", children: [
11337
+ member.user.firstName,
11338
+ " ",
11339
+ member.user.lastName
11340
+ ] }) }),
11341
+ member.role === "owner" ? /* @__PURE__ */ 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: [
11342
+ /* @__PURE__ */ jsx(Crown, { size: 12, weight: "duotone" }),
11343
+ t("BiChat.Share.RoleOwner")
11344
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
11345
+ /* @__PURE__ */ jsx(
11346
+ RoleSegmentedControl,
11347
+ {
11348
+ value: member.role,
11349
+ onChange: (role) => handleUpdateRole(member.user.id, role),
11350
+ disabled: saving,
11351
+ size: "sm",
11352
+ t
11353
+ }
11354
+ ),
11355
+ /* @__PURE__ */ jsx(
11356
+ "button",
11357
+ {
11358
+ type: "button",
11359
+ disabled: saving,
11360
+ onClick: () => setConfirmRemove(member),
11361
+ 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",
11362
+ "aria-label": `${t("BiChat.Share.Remove")} ${member.user.firstName} ${member.user.lastName}`,
11363
+ children: /* @__PURE__ */ jsx(Trash, { size: 14 })
11364
+ }
11365
+ )
11366
+ ] })
11367
+ ]
11368
+ },
11369
+ member.user.id
11370
+ )) })
11371
+ ] }),
11372
+ canManageMembers && /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-gray-200 dark:border-gray-700 p-3 space-y-3", children: [
11373
+ /* @__PURE__ */ jsx("h3", { className: "text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("BiChat.Share.AddMember") }),
11374
+ /* @__PURE__ */ jsxs(
11375
+ "div",
11376
+ {
11377
+ className: "relative",
11378
+ onBlur: (e) => {
11379
+ if (!e.currentTarget.contains(e.relatedTarget)) {
11380
+ setDropdownOpen(false);
11381
+ }
11382
+ },
11383
+ children: [
11384
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
11385
+ /* @__PURE__ */ jsx(
11386
+ MagnifyingGlass,
11387
+ {
11388
+ size: 14,
11389
+ className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500"
11390
+ }
11391
+ ),
11392
+ /* @__PURE__ */ jsx(
11393
+ "input",
11394
+ {
11395
+ type: "text",
11396
+ 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",
11397
+ placeholder: t("BiChat.Share.SearchUsers"),
11398
+ value: selectedUser ? `${selectedUser.firstName} ${selectedUser.lastName}` : query,
11399
+ onFocus: () => {
11400
+ setDropdownOpen(true);
11401
+ setDropdownHighlightIndex(0);
11402
+ if (selectedUser) {
11403
+ setSelectedUser(null);
11404
+ setQuery("");
11405
+ }
11406
+ },
11407
+ onChange: (e) => {
11408
+ setQuery(e.target.value);
11409
+ setSelectedUser(null);
11410
+ setDropdownOpen(true);
11411
+ setDropdownHighlightIndex(0);
11412
+ },
11413
+ onKeyDown: (e) => {
11414
+ if (!dropdownOpen || filteredUsers.length === 0) {
11415
+ if (e.key === "Escape") {
11416
+ setDropdownOpen(false);
11417
+ }
11418
+ return;
11419
+ }
11420
+ if (e.key === "Escape") {
11421
+ e.preventDefault();
11422
+ setDropdownOpen(false);
11423
+ setDropdownHighlightIndex(0);
11424
+ return;
11425
+ }
11426
+ if (e.key === "ArrowDown") {
11427
+ e.preventDefault();
11428
+ const next = (dropdownHighlightIndex + 1) % filteredUsers.length;
11429
+ setDropdownHighlightIndex(next);
11430
+ setTimeout(() => dropdownOptionRefs.current[next]?.focus(), 0);
11431
+ return;
11432
+ }
11433
+ if (e.key === "ArrowUp") {
11434
+ e.preventDefault();
11435
+ const next = dropdownHighlightIndex <= 0 ? filteredUsers.length - 1 : dropdownHighlightIndex - 1;
11436
+ setDropdownHighlightIndex(next);
11437
+ setTimeout(() => dropdownOptionRefs.current[next]?.focus(), 0);
11438
+ return;
11439
+ }
11440
+ if (e.key === "Enter") {
11441
+ e.preventDefault();
11442
+ const user = filteredUsers[dropdownHighlightIndex];
11443
+ if (user) {
11444
+ setSelectedUser(user);
11445
+ setQuery("");
11446
+ setDropdownOpen(false);
11447
+ }
11448
+ }
11449
+ }
11450
+ }
11451
+ )
11452
+ ] }),
11453
+ dropdownOpen && !selectedUser && /* @__PURE__ */ 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__ */ 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__ */ jsxs(
11454
+ "button",
11455
+ {
11456
+ type: "button",
11457
+ ref: (el) => {
11458
+ dropdownOptionRefs.current[index] = el;
11459
+ },
11460
+ 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" : ""}`,
11461
+ onMouseDown: (e) => e.preventDefault(),
11462
+ onClick: () => {
11463
+ setSelectedUser(user);
11464
+ setQuery("");
11465
+ setDropdownOpen(false);
11466
+ },
11467
+ children: [
11468
+ /* @__PURE__ */ jsx(
11469
+ MemoizedUserAvatar,
11470
+ {
11471
+ firstName: user.firstName,
11472
+ lastName: user.lastName,
11473
+ initials: user.initials,
11474
+ size: "xs"
11475
+ }
11476
+ ),
11477
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-900 dark:text-gray-100", children: [
11478
+ user.firstName,
11479
+ " ",
11480
+ user.lastName
11481
+ ] })
11482
+ ]
11483
+ },
11484
+ user.id
11485
+ )) })
11486
+ ]
11487
+ }
11488
+ ),
11489
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
11490
+ /* @__PURE__ */ jsx(
11491
+ RoleSegmentedControl,
11492
+ {
11493
+ value: selectedRole,
11494
+ onChange: setSelectedRole,
11495
+ disabled: saving,
11496
+ t
11497
+ }
11498
+ ),
11499
+ /* @__PURE__ */ jsxs(
11500
+ "button",
11501
+ {
11502
+ type: "button",
11503
+ onClick: handleAdd,
11504
+ disabled: saving || !selectedUser,
11505
+ 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",
11506
+ children: [
11507
+ /* @__PURE__ */ jsx(UserPlus, { size: 14 }),
11508
+ t("BiChat.Share.Add")
11509
+ ]
11510
+ }
11511
+ )
11512
+ ] })
11513
+ ] })
11514
+ ] })
11515
+ ]
11516
+ }
11517
+ ) })
11518
+ ] }),
11519
+ /* @__PURE__ */ jsx(
11520
+ ConfirmModal,
11521
+ {
11522
+ isOpen: !!confirmRemove,
11523
+ isDanger: true,
11524
+ title: t("BiChat.Share.RemoveConfirmTitle"),
11525
+ message: confirmRemove ? t("BiChat.Share.RemoveConfirmMessage").replace(
11526
+ "{{name}}",
11527
+ `${confirmRemove.user.firstName} ${confirmRemove.user.lastName}`
11528
+ ) : "",
11529
+ confirmText: t("BiChat.Share.Remove"),
11530
+ onConfirm: () => {
11531
+ if (confirmRemove) {
11532
+ void handleRemove(confirmRemove.user.id);
11533
+ }
11534
+ setConfirmRemove(null);
11535
+ },
11536
+ onCancel: () => setConfirmRemove(null)
11537
+ }
11538
+ )
11539
+ ] });
11540
+ }
11541
+
11542
+ // ui/src/bichat/components/StreamError.tsx
11543
+ init_useTranslation();
11544
+ function StreamError({
11545
+ error,
11546
+ onRetry,
11547
+ onRegenerate,
11548
+ onDismiss,
11549
+ compact = false
11550
+ }) {
11551
+ const { t } = useTranslation();
11552
+ return /* @__PURE__ */ jsxs(
11553
+ motion.div,
11554
+ {
11555
+ initial: { opacity: 0, y: 10 },
11556
+ animate: { opacity: 1, y: 0 },
11557
+ exit: { opacity: 0, y: -10 },
11558
+ 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`,
11559
+ role: "alert",
11560
+ children: [
11561
+ /* @__PURE__ */ 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__ */ jsx(
11562
+ Warning,
11563
+ {
11564
+ className: "w-4 h-4 text-red-600 dark:text-red-400",
11565
+ weight: "fill"
11566
+ }
11567
+ ) }),
11568
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
11569
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
11570
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
11571
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
11572
+ onRetry && /* @__PURE__ */ jsxs(
11573
+ "button",
11574
+ {
11575
+ onClick: onRetry,
11576
+ 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",
11577
+ type: "button",
11578
+ children: [
11579
+ /* @__PURE__ */ jsx(ArrowClockwise, { className: "w-3.5 h-3.5" }),
11580
+ t("BiChat.StreamError.Retry")
11581
+ ]
10813
11582
  }
10814
11583
  ),
10815
11584
  onRegenerate && /* @__PURE__ */ jsxs(
@@ -10897,7 +11666,8 @@ function ChatSessionCore({
10897
11666
  updateQueueItem
10898
11667
  } = useChatInput();
10899
11668
  const isArchived = session?.status === "archived";
10900
- const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived;
11669
+ const accessReadOnly = session?.access ? !session.access.canWrite : false;
11670
+ const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived || accessReadOnly;
10901
11671
  const [restoring, setRestoring] = useState(false);
10902
11672
  const handleRestore = useCallback(async () => {
10903
11673
  if (!session?.id) {
@@ -10918,6 +11688,8 @@ function ChatSessionCore({
10918
11688
  const [artifactsPanelExpanded, setArtifactsPanelExpanded] = useState(
10919
11689
  artifactsPanelDefaultExpanded
10920
11690
  );
11691
+ const [membersModalOpen, setMembersModalOpen] = useState(false);
11692
+ const [headerMembers, setHeaderMembers] = useState(null);
10921
11693
  const [artifactsPanelWidth, setArtifactsPanelWidth] = useState(ARTIFACTS_PANEL_WIDTH_DEFAULT);
10922
11694
  const [isResizingArtifactsPanel, setIsResizingArtifactsPanel] = useState(false);
10923
11695
  const layoutContainerRef = useRef(null);
@@ -10952,6 +11724,25 @@ function ChatSessionCore({
10952
11724
  } catch {
10953
11725
  }
10954
11726
  }, [artifactsPanelStorageKey, showArtifactsPanel]);
11727
+ useEffect(() => {
11728
+ if (!session?.id || !dataSource.listSessionMembers) {
11729
+ setHeaderMembers(null);
11730
+ return;
11731
+ }
11732
+ let cancelled = false;
11733
+ dataSource.listSessionMembers(session.id).then((members) => {
11734
+ if (!cancelled) {
11735
+ setHeaderMembers(members.map((m) => m.user));
11736
+ }
11737
+ }).catch(() => {
11738
+ if (!cancelled) {
11739
+ setHeaderMembers(null);
11740
+ }
11741
+ });
11742
+ return () => {
11743
+ cancelled = true;
11744
+ };
11745
+ }, [session?.id, dataSource.listSessionMembers]);
10955
11746
  const handleArtifactsResizeStart = useCallback(() => {
10956
11747
  setIsResizingArtifactsPanel(true);
10957
11748
  }, []);
@@ -11040,9 +11831,27 @@ function ChatSessionCore({
11040
11831
  window.dispatchEvent(new CustomEvent("bichat:artifacts-panel-expanded", { detail: { expanded: true } }));
11041
11832
  }
11042
11833
  }
11043
- };
11044
- const headerActions = showArtifactsControls ? /* @__PURE__ */ jsxs(Fragment, { children: [
11045
- /* @__PURE__ */ jsxs(
11834
+ };
11835
+ const canShowShareButton = Boolean(
11836
+ session?.access?.canManageMembers && dataSource.listUsers && dataSource.listSessionMembers && dataSource.addSessionMember && dataSource.updateSessionMemberRole && dataSource.removeSessionMember
11837
+ );
11838
+ const shareButton = canShowShareButton ? /* @__PURE__ */ jsxs(
11839
+ "button",
11840
+ {
11841
+ type: "button",
11842
+ onClick: () => setMembersModalOpen(true),
11843
+ 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",
11844
+ "aria-label": t("BiChat.Share.Title"),
11845
+ title: t("BiChat.Share.Title"),
11846
+ children: [
11847
+ /* @__PURE__ */ jsx(ShareNetwork, { className: "h-4 w-4" }),
11848
+ t("BiChat.Share.Button")
11849
+ ]
11850
+ }
11851
+ ) : null;
11852
+ const headerActions = /* @__PURE__ */ jsxs(Fragment, { children: [
11853
+ shareButton,
11854
+ showArtifactsControls && /* @__PURE__ */ jsxs(
11046
11855
  "button",
11047
11856
  {
11048
11857
  type: "button",
@@ -11062,7 +11871,7 @@ function ChatSessionCore({
11062
11871
  }
11063
11872
  ),
11064
11873
  actionsSlot
11065
- ] }) : actionsSlot;
11874
+ ] });
11066
11875
  return /* @__PURE__ */ jsxs(
11067
11876
  "main",
11068
11877
  {
@@ -11075,7 +11884,9 @@ function ChatSessionCore({
11075
11884
  onBack,
11076
11885
  readOnly: effectiveReadOnly,
11077
11886
  logoSlot,
11078
- actionsSlot: headerActions
11887
+ actionsSlot: headerActions,
11888
+ members: headerMembers ?? (session?.owner ? [session.owner] : void 0),
11889
+ onMembersClick: canShowShareButton ? () => setMembersModalOpen(true) : void 0
11079
11890
  }
11080
11891
  ),
11081
11892
  error && /* @__PURE__ */ jsx(
@@ -11272,6 +12083,15 @@ function ChatSessionCore({
11272
12083
  ) })
11273
12084
  ]
11274
12085
  }
12086
+ ),
12087
+ canShowShareButton && /* @__PURE__ */ jsx(
12088
+ SessionMembersModal,
12089
+ {
12090
+ isOpen: membersModalOpen,
12091
+ sessionId: session?.id,
12092
+ dataSource,
12093
+ onClose: () => setMembersModalOpen(false)
12094
+ }
11275
12095
  )
11276
12096
  ]
11277
12097
  }
@@ -11294,7 +12114,7 @@ function ChatSession(props) {
11294
12114
  // ui/src/bichat/index.ts
11295
12115
  init_MarkdownRenderer();
11296
12116
  init_ChartCard();
11297
- var sizeClasses = {
12117
+ var sizeClasses2 = {
11298
12118
  sm: {
11299
12119
  container: "py-6 px-3",
11300
12120
  title: "text-sm",
@@ -11319,7 +12139,7 @@ function EmptyState({
11319
12139
  className = "",
11320
12140
  size = "md"
11321
12141
  }) {
11322
- const sizes = sizeClasses[size];
12142
+ const sizes = sizeClasses2[size];
11323
12143
  const prefersReducedMotion2 = useReducedMotion();
11324
12144
  const duration = prefersReducedMotion2 ? 0 : 0.4;
11325
12145
  return /* @__PURE__ */ jsx(
@@ -11379,7 +12199,7 @@ MemoizedEmptyState.displayName = "EmptyState";
11379
12199
 
11380
12200
  // ui/src/bichat/components/EditableText.tsx
11381
12201
  init_useTranslation();
11382
- var sizeClasses2 = {
12202
+ var sizeClasses3 = {
11383
12203
  sm: "text-sm",
11384
12204
  md: "text-base",
11385
12205
  lg: "text-lg"
@@ -11450,7 +12270,7 @@ var EditableText = forwardRef(
11450
12270
  const handleBlur = () => {
11451
12271
  handleSave();
11452
12272
  };
11453
- const sizeClass = sizeClasses2[size];
12273
+ const sizeClass = sizeClasses3[size];
11454
12274
  if (isEditing) {
11455
12275
  return /* @__PURE__ */ jsx(
11456
12276
  "div",
@@ -11510,7 +12330,7 @@ var MemoizedEditableText = memo(EditableText);
11510
12330
 
11511
12331
  // ui/src/bichat/components/SearchInput.tsx
11512
12332
  init_useTranslation();
11513
- var sizeClasses3 = {
12333
+ var sizeClasses4 = {
11514
12334
  sm: {
11515
12335
  container: "py-1.5 pl-8 pr-8 text-xs",
11516
12336
  icon: 14,
@@ -11543,7 +12363,7 @@ function SearchInput({
11543
12363
  const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Search");
11544
12364
  const resolvedAriaLabel = ariaLabel ?? t("BiChat.Common.Search");
11545
12365
  const inputRef = useRef(null);
11546
- const sizes = sizeClasses3[size];
12366
+ const sizes = sizeClasses4[size];
11547
12367
  useEffect(() => {
11548
12368
  if (autoFocus && inputRef.current) {
11549
12369
  inputRef.current.focus();
@@ -11970,134 +12790,6 @@ function ToastContainer({ toasts, onDismiss, dismissLabel }) {
11970
12790
  }
11971
12791
  );
11972
12792
  }
11973
-
11974
- // ui/src/bichat/components/ConfirmModal.tsx
11975
- init_useTranslation();
11976
- function ConfirmModalBase({
11977
- isOpen,
11978
- title,
11979
- message,
11980
- onConfirm,
11981
- onCancel,
11982
- confirmText,
11983
- cancelText,
11984
- isDanger = false
11985
- }) {
11986
- const { t } = useTranslation();
11987
- const resolvedConfirmText = confirmText?.trim() ? confirmText : t("BiChat.Common.Confirm");
11988
- const resolvedCancelText = cancelText?.trim() ? cancelText : t("BiChat.Common.Cancel");
11989
- return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
11990
- /* @__PURE__ */ jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
11991
- /* @__PURE__ */ jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs(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: [
11992
- /* @__PURE__ */ jsx("div", { className: "px-6 pt-6 pb-5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3.5", children: [
11993
- isDanger && /* @__PURE__ */ 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__ */ jsx(WarningCircle, { size: 22, weight: "duotone", className: "text-red-600 dark:text-red-400" }) }),
11994
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
11995
- /* @__PURE__ */ jsx(DialogTitle, { className: "text-base font-semibold text-gray-900 dark:text-gray-100 leading-snug", children: title }),
11996
- /* @__PURE__ */ jsx(Description, { className: "mt-2 text-sm text-gray-600 dark:text-gray-400 leading-relaxed", children: message })
11997
- ] })
11998
- ] }) }),
11999
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2.5 px-6 pb-5", children: [
12000
- /* @__PURE__ */ jsx(
12001
- "button",
12002
- {
12003
- type: "button",
12004
- onClick: onCancel,
12005
- ...isDanger ? { "data-autofocus": true } : {},
12006
- 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",
12007
- "data-testid": "confirm-modal-cancel",
12008
- children: resolvedCancelText
12009
- }
12010
- ),
12011
- /* @__PURE__ */ jsx(
12012
- "button",
12013
- {
12014
- type: "button",
12015
- ...!isDanger ? { "data-autofocus": true } : {},
12016
- onClick: onConfirm,
12017
- className: [
12018
- "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-white",
12019
- "transition-all duration-150 shadow-sm hover:shadow",
12020
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
12021
- 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"
12022
- ].join(" "),
12023
- "data-testid": "confirm-modal-confirm",
12024
- children: resolvedConfirmText
12025
- }
12026
- )
12027
- ] })
12028
- ] }) })
12029
- ] });
12030
- }
12031
- var ConfirmModal = memo(ConfirmModalBase);
12032
- ConfirmModal.displayName = "ConfirmModal";
12033
- var ConfirmModal_default = ConfirmModal;
12034
- function hashString(str) {
12035
- let hash = 0;
12036
- for (let i = 0; i < str.length; i++) {
12037
- const char = str.charCodeAt(i);
12038
- hash = (hash << 5) - hash + char;
12039
- hash = hash & hash;
12040
- }
12041
- return Math.abs(hash);
12042
- }
12043
- var colorPalette = [
12044
- { bg: "bg-blue-500", text: "text-white" },
12045
- { bg: "bg-green-500", text: "text-white" },
12046
- { bg: "bg-purple-500", text: "text-white" },
12047
- { bg: "bg-pink-500", text: "text-white" },
12048
- { bg: "bg-indigo-500", text: "text-white" },
12049
- { bg: "bg-teal-500", text: "text-white" },
12050
- { bg: "bg-orange-500", text: "text-white" },
12051
- { bg: "bg-cyan-500", text: "text-white" },
12052
- { bg: "bg-amber-500", text: "text-white" },
12053
- { bg: "bg-lime-500", text: "text-white" }
12054
- ];
12055
- var sizeClasses4 = {
12056
- sm: "w-8 h-8 text-xs",
12057
- md: "w-10 h-10 text-sm",
12058
- lg: "w-12 h-12 text-base"
12059
- };
12060
- function UserAvatar({
12061
- firstName,
12062
- lastName,
12063
- initials: providedInitials,
12064
- size = "md",
12065
- className = ""
12066
- }) {
12067
- const derivedInitials = (() => {
12068
- const firstChar = firstName?.trim()?.charAt(0) || "";
12069
- const lastChar = lastName?.trim()?.charAt(0) || "";
12070
- const combined = `${firstChar}${lastChar}`.trim();
12071
- return combined || "U";
12072
- })();
12073
- const initials = (providedInitials?.trim() || derivedInitials).toUpperCase();
12074
- const fullName = `${firstName}${lastName}`;
12075
- const colorIndex = hashString(fullName) % colorPalette.length;
12076
- const colors = colorPalette[colorIndex];
12077
- return /* @__PURE__ */ jsx(
12078
- "div",
12079
- {
12080
- className: `
12081
- ${sizeClasses4[size]}
12082
- ${colors.bg}
12083
- ${colors.text}
12084
- ${className}
12085
- rounded-full
12086
- flex
12087
- items-center
12088
- justify-center
12089
- font-semibold
12090
- flex-shrink-0
12091
- select-none
12092
- `,
12093
- "aria-label": `${firstName} ${lastName}`,
12094
- title: `${firstName} ${lastName}`,
12095
- children: initials
12096
- }
12097
- );
12098
- }
12099
- var MemoizedUserAvatar = memo(UserAvatar);
12100
- MemoizedUserAvatar.displayName = "UserAvatar";
12101
12793
  function PermissionGuard({
12102
12794
  permissions,
12103
12795
  mode = "all",
@@ -12539,6 +13231,11 @@ var SessionItem = memo(
12539
13231
  const isTitleGenerating = !session.title?.trim();
12540
13232
  const displayTitle = isTitleGenerating ? t("BiChat.Common.Generating") : session.title ?? t("BiChat.Common.Untitled");
12541
13233
  const lastActivity = formatRelativeTime(session.updatedAt, t);
13234
+ const accessRole = session.access?.role ?? "owner";
13235
+ const roleLabel = accessRole === "editor" ? t("BiChat.Share.RoleEditor") : accessRole === "viewer" ? t("BiChat.Share.RoleViewer") : accessRole === "read_all" ? t("BiChat.Share.RoleReadOnly") : "";
13236
+ const visibilityLabel = session.isGroup ? t("BiChat.Sidebar.GroupChat") : accessRole === "editor" || accessRole === "viewer" ? t("BiChat.Sidebar.SharedWithYou") : "";
13237
+ const isGroupOrShared = Boolean(session.isGroup || session.memberCount && session.memberCount > 1);
13238
+ const metaParts = [lastActivity, visibilityLabel, roleLabel].filter(Boolean);
12542
13239
  const { handlers: longPressHandlers } = useLongPress({
12543
13240
  delay: 500,
12544
13241
  onLongPress: (e) => {
@@ -12680,17 +13377,23 @@ var SessionItem = memo(
12680
13377
  "data-testid": `${testIdPrefix}-session-${session.id}`,
12681
13378
  ...longPressHandlers,
12682
13379
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
12683
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
12684
- /* @__PURE__ */ jsx(
12685
- MemoizedEditableText,
12686
- {
12687
- ref: editableTitleRef,
12688
- value: displayTitle,
12689
- onSave: (newTitle) => onRename?.(newTitle),
12690
- isLoading: isTitleGenerating
12691
- }
12692
- ),
12693
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate mt-0.5", children: lastActivity })
13380
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 min-w-0 flex-1", children: [
13381
+ isGroupOrShared && /* @__PURE__ */ jsx(UsersThree, { size: 14, weight: "duotone", className: "text-primary-500 dark:text-primary-400 mt-1 flex-shrink-0" }),
13382
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
13383
+ /* @__PURE__ */ jsx(
13384
+ MemoizedEditableText,
13385
+ {
13386
+ ref: editableTitleRef,
13387
+ value: displayTitle,
13388
+ onSave: (newTitle) => onRename?.(newTitle),
13389
+ isLoading: isTitleGenerating
13390
+ }
13391
+ ),
13392
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate mt-0.5", children: [
13393
+ metaParts.join(" \u2022 "),
13394
+ isGroupOrShared && session.memberCount && session.memberCount > 0 && /* @__PURE__ */ 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 })
13395
+ ] })
13396
+ ] })
12694
13397
  ] }),
12695
13398
  !isTouch && hasContextMenu && /* @__PURE__ */ jsxs(Menu, { children: [
12696
13399
  /* @__PURE__ */ jsx(
@@ -13044,24 +13747,23 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
13044
13747
  setOffset((prev) => prev + limit);
13045
13748
  }
13046
13749
  }, [fetching, hasMore]);
13047
- const loadMoreRef = useCallback(
13048
- (node) => {
13049
- if (!node || fetching || !hasMore) {
13050
- return;
13750
+ const loadMoreNodeRef = useRef(null);
13751
+ const loadMoreRef = useCallback((node) => {
13752
+ loadMoreNodeRef.current = node;
13753
+ }, []);
13754
+ useEffect(() => {
13755
+ const node = loadMoreNodeRef.current;
13756
+ if (!node || fetching || !hasMore) {
13757
+ return;
13758
+ }
13759
+ const observer = new IntersectionObserver((entries) => {
13760
+ if (entries[0].isIntersecting) {
13761
+ handleLoadMore();
13051
13762
  }
13052
- const observer = new IntersectionObserver(
13053
- (entries) => {
13054
- if (entries[0].isIntersecting) {
13055
- handleLoadMore();
13056
- }
13057
- },
13058
- { threshold: 0.1 }
13059
- );
13060
- observer.observe(node);
13061
- return () => observer.disconnect();
13062
- },
13063
- [fetching, hasMore, handleLoadMore]
13064
- );
13763
+ }, { threshold: 0.1 });
13764
+ observer.observe(node);
13765
+ return () => observer.disconnect();
13766
+ }, [fetching, hasMore, handleLoadMore]);
13065
13767
  const derivedUsers = useMemo(() => {
13066
13768
  if (dataSource.listUsers) {
13067
13769
  return users;
@@ -13119,57 +13821,61 @@ function AllChatsList({ dataSource, onSessionSelect, activeSessionId }) {
13119
13821
  role: "list",
13120
13822
  "aria-label": t("BiChat.AllChats.OrganizationChatSessions"),
13121
13823
  children: [
13122
- chats.map((chat) => /* @__PURE__ */ jsx(
13123
- motion.div,
13124
- {
13125
- initial: { opacity: 0, y: -10 },
13126
- animate: { opacity: 1, y: 0 },
13127
- exit: { opacity: 0, y: -10 },
13128
- children: /* @__PURE__ */ jsx(
13129
- "div",
13130
- {
13131
- role: "link",
13132
- tabIndex: 0,
13133
- onClick: () => onSessionSelect(chat.id),
13134
- onKeyDown: (e) => {
13135
- if (e.key === "Enter" || e.key === " ") {
13136
- e.preventDefault();
13137
- onSessionSelect(chat.id);
13138
- }
13139
- },
13140
- className: `
13824
+ chats.map((chat) => {
13825
+ const owner = chat.owner ?? {
13826
+ firstName: "",
13827
+ lastName: "",
13828
+ initials: "U"
13829
+ };
13830
+ const ownerName = [owner.firstName, owner.lastName].filter(Boolean).join(" ");
13831
+ return /* @__PURE__ */ jsx(
13832
+ motion.div,
13833
+ {
13834
+ initial: { opacity: 0, y: -10 },
13835
+ animate: { opacity: 1, y: 0 },
13836
+ exit: { opacity: 0, y: -10 },
13837
+ children: /* @__PURE__ */ jsx(
13838
+ "div",
13839
+ {
13840
+ role: "link",
13841
+ tabIndex: 0,
13842
+ onClick: () => onSessionSelect(chat.id),
13843
+ onKeyDown: (e) => {
13844
+ if (e.key === "Enter" || e.key === " ") {
13845
+ e.preventDefault();
13846
+ onSessionSelect(chat.id);
13847
+ }
13848
+ },
13849
+ className: `
13141
13850
  block px-3 py-2 rounded-lg transition-smooth group cursor-pointer
13142
13851
  ${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"}
13143
13852
  `,
13144
- "aria-current": chat.id === activeSessionId ? "page" : void 0,
13145
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
13146
- /* @__PURE__ */ jsx(
13147
- MemoizedUserAvatar,
13148
- {
13149
- firstName: chat.owner.firstName,
13150
- lastName: chat.owner.lastName,
13151
- initials: chat.owner.initials,
13152
- size: "sm"
13153
- }
13154
- ),
13155
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
13156
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
13157
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: [
13158
- chat.owner.firstName,
13159
- " ",
13160
- chat.owner.lastName
13161
- ] }),
13162
- chat.status === "archived" && /* @__PURE__ */ 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: [
13163
- /* @__PURE__ */ jsx(Archive, { size: 12, className: "w-3 h-3" }),
13164
- t("BiChat.Chat.Archived")
13853
+ "aria-current": chat.id === activeSessionId ? "page" : void 0,
13854
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
13855
+ /* @__PURE__ */ jsx(
13856
+ MemoizedUserAvatar,
13857
+ {
13858
+ firstName: owner.firstName,
13859
+ lastName: owner.lastName,
13860
+ initials: owner.initials,
13861
+ size: "sm"
13862
+ }
13863
+ ),
13864
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
13865
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: chat.title || t("BiChat.Common.Untitled") }),
13866
+ ownerName && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 truncate", children: ownerName }),
13867
+ chat.status === "archived" && /* @__PURE__ */ 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: [
13868
+ /* @__PURE__ */ jsx(Archive, { size: 12, className: "w-3 h-3" }),
13869
+ t("BiChat.Chat.Archived")
13870
+ ] })
13165
13871
  ] })
13166
13872
  ] })
13167
- ] })
13168
- }
13169
- )
13170
- },
13171
- chat.id
13172
- )),
13873
+ }
13874
+ )
13875
+ },
13876
+ chat.id
13877
+ );
13878
+ }),
13173
13879
  hasMore && /* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "py-4 text-center", children: fetching ? /* @__PURE__ */ jsx(SessionSkeleton, { count: 2 }) : /* @__PURE__ */ jsx(
13174
13880
  "button",
13175
13881
  {
@@ -13939,19 +14645,22 @@ function Sidebar2({
13939
14645
  animate: "visible",
13940
14646
  role: "list",
13941
14647
  "aria-label": t("BiChat.Sidebar.PinnedChats"),
13942
- children: pinnedSessions.map((session) => /* @__PURE__ */ jsx(
13943
- SessionItem_default,
13944
- {
13945
- session,
13946
- isActive: session.id === activeSessionId,
13947
- onSelect: () => handleSessionSelect(session.id),
13948
- onArchive: () => handleSessionArchive(session.id),
13949
- onPin: () => handleSessionPin(session.id, session.pinned),
13950
- onRename: (newTitle) => handleSessionRename(session.id, newTitle),
13951
- onRegenerateTitle: () => handleSessionRegenerateTitle(session.id)
13952
- },
13953
- session.id
13954
- ))
14648
+ children: pinnedSessions.map((session) => {
14649
+ const canWrite = session.access?.canWrite ?? true;
14650
+ return /* @__PURE__ */ jsx(
14651
+ SessionItem_default,
14652
+ {
14653
+ session,
14654
+ isActive: session.id === activeSessionId,
14655
+ onSelect: () => handleSessionSelect(session.id),
14656
+ onArchive: canWrite ? () => handleSessionArchive(session.id) : void 0,
14657
+ onPin: canWrite ? () => handleSessionPin(session.id, session.pinned) : void 0,
14658
+ onRename: canWrite ? (newTitle) => handleSessionRename(session.id, newTitle) : void 0,
14659
+ onRegenerateTitle: canWrite ? () => handleSessionRegenerateTitle(session.id) : void 0
14660
+ },
14661
+ session.id
14662
+ );
14663
+ })
13955
14664
  }
13956
14665
  ),
13957
14666
  /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-gray-700 my-3" })
@@ -13973,19 +14682,22 @@ function Sidebar2({
13973
14682
  animate: "visible",
13974
14683
  role: "list",
13975
14684
  "aria-label": `${group.name} chats`,
13976
- children: group.sessions.map((session) => /* @__PURE__ */ jsx(
13977
- SessionItem_default,
13978
- {
13979
- session,
13980
- isActive: session.id === activeSessionId,
13981
- onSelect: () => handleSessionSelect(session.id),
13982
- onArchive: () => handleSessionArchive(session.id),
13983
- onPin: () => handleSessionPin(session.id, session.pinned),
13984
- onRename: (newTitle) => handleSessionRename(session.id, newTitle),
13985
- onRegenerateTitle: () => handleSessionRegenerateTitle(session.id)
13986
- },
13987
- session.id
13988
- ))
14685
+ children: group.sessions.map((session) => {
14686
+ const canWrite = session.access?.canWrite ?? true;
14687
+ return /* @__PURE__ */ jsx(
14688
+ SessionItem_default,
14689
+ {
14690
+ session,
14691
+ isActive: session.id === activeSessionId,
14692
+ onSelect: () => handleSessionSelect(session.id),
14693
+ onArchive: canWrite ? () => handleSessionArchive(session.id) : void 0,
14694
+ onPin: canWrite ? () => handleSessionPin(session.id, session.pinned) : void 0,
14695
+ onRename: canWrite ? (newTitle) => handleSessionRename(session.id, newTitle) : void 0,
14696
+ onRegenerateTitle: canWrite ? () => handleSessionRegenerateTitle(session.id) : void 0
14697
+ },
14698
+ session.id
14699
+ );
14700
+ })
13989
14701
  }
13990
14702
  )
13991
14703
  ] }, group.name)),
@@ -14861,12 +15573,12 @@ function QuestionStep({
14861
15573
  const data = selectedAnswers[question.id] || { };
14862
15574
  setOtherText(data.customText || "");
14863
15575
  }, [question.id]);
14864
- const handleOptionClick = (optionLabel) => {
15576
+ const handleOptionClick = (optionID) => {
14865
15577
  if (isMultiSelect) {
14866
- const newOptions = selectedOptions.includes(optionLabel) ? selectedOptions.filter((a) => a !== optionLabel) : [...selectedOptions, optionLabel];
15578
+ const newOptions = selectedOptions.includes(optionID) ? selectedOptions.filter((a) => a !== optionID) : [...selectedOptions, optionID];
14867
15579
  onAnswer({ options: newOptions, customText: otherText || void 0 });
14868
15580
  } else {
14869
- onAnswer({ options: [optionLabel], customText: otherText || void 0 });
15581
+ onAnswer({ options: [optionID], customText: otherText || void 0 });
14870
15582
  }
14871
15583
  };
14872
15584
  const handleOtherTextChange = (text) => {
@@ -14880,11 +15592,11 @@ function QuestionStep({
14880
15592
  /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: question.text }) }),
14881
15593
  isMultiSelect && /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-500 italic", children: t("BiChat.Question.SelectMulti") }),
14882
15594
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: (question.options || []).map((option) => {
14883
- const isSelected = selectedOptions.includes(option.label);
15595
+ const isSelected = selectedOptions.includes(option.id);
14884
15596
  return /* @__PURE__ */ jsx(
14885
15597
  "button",
14886
15598
  {
14887
- onClick: () => handleOptionClick(option.label),
15599
+ onClick: () => handleOptionClick(option.id),
14888
15600
  className: `
14889
15601
  cursor-pointer relative p-4 text-left border-2 rounded-lg transition-all
14890
15602
  ${isSelected ? "border-primary-500 bg-white dark:bg-gray-800" : "border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-600"}
@@ -14952,6 +15664,7 @@ function ConfirmationStep({
14952
15664
  const answerData = answers[question.id] || { options: [] };
14953
15665
  const selectedOptions = answerData.options || [];
14954
15666
  const customText = answerData.customText;
15667
+ const optionLabelByID = new Map((question.options || []).map((option) => [option.id, option.label]));
14955
15668
  const hasAnswer = selectedOptions.length > 0 || !!customText;
14956
15669
  return /* @__PURE__ */ jsxs(
14957
15670
  "div",
@@ -14964,7 +15677,7 @@ function ConfirmationStep({
14964
15677
  "span",
14965
15678
  {
14966
15679
  className: "inline-flex items-center px-3 py-1 rounded-lg text-sm font-medium border border-primary-500 bg-primary-500/10 text-primary-600 dark:border-primary-400 dark:bg-primary-400/10 dark:text-primary-400",
14967
- children: option
15680
+ children: optionLabelByID.get(option) || option
14968
15681
  },
14969
15682
  option
14970
15683
  )),
@@ -16145,6 +16858,76 @@ function useScrollToBottom(items) {
16145
16858
  scrollToBottom
16146
16859
  };
16147
16860
  }
16861
+ function useHttpDataSourceConfigFromApplet(options) {
16862
+ return useMemo(() => {
16863
+ const ctx = typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0;
16864
+ if (!ctx) {
16865
+ throw new Error(
16866
+ "Applet context not found. Ensure window.__APPLET_CONTEXT__ is injected by the backend."
16867
+ );
16868
+ }
16869
+ const rpcEndpoint = ctx.config?.rpcUIEndpoint ?? "/rpc";
16870
+ const streamEndpoint = ctx.config?.streamEndpoint ?? "/stream";
16871
+ const csrfToken = ctx.session?.csrfToken ?? (typeof window !== "undefined" ? window.__CSRF_TOKEN__ : void 0) ?? "";
16872
+ const isDev = typeof import.meta.env?.DEV === "boolean" && import.meta.env?.DEV;
16873
+ if (!csrfToken && isDev) {
16874
+ console.warn(
16875
+ "[useHttpDataSourceConfigFromApplet] CSRF token is empty \u2014 requests may be rejected by the server."
16876
+ );
16877
+ }
16878
+ return {
16879
+ baseUrl: "",
16880
+ rpcEndpoint,
16881
+ streamEndpoint,
16882
+ csrfToken,
16883
+ timeout: options?.timeout ?? 12e4
16884
+ };
16885
+ }, [options?.timeout]);
16886
+ }
16887
+ var SESSION_PATH_REGEX = /\/session\/([^/]+)/;
16888
+ function useBichatRouter({
16889
+ navigate,
16890
+ pathname,
16891
+ onNavigate
16892
+ }) {
16893
+ const activeSessionId = useMemo(
16894
+ () => pathname.match(SESSION_PATH_REGEX)?.[1],
16895
+ [pathname]
16896
+ );
16897
+ const maybeClose = useCallback(() => {
16898
+ onNavigate?.();
16899
+ }, [onNavigate]);
16900
+ const onSessionSelect = useCallback(
16901
+ (sessionId) => {
16902
+ if (sessionId) {
16903
+ navigate(`/session/${sessionId}`);
16904
+ } else {
16905
+ navigate("/");
16906
+ }
16907
+ maybeClose();
16908
+ },
16909
+ [navigate, maybeClose]
16910
+ );
16911
+ const onNewChat = useCallback(() => {
16912
+ navigate("/");
16913
+ maybeClose();
16914
+ }, [navigate, maybeClose]);
16915
+ const onArchivedView = useCallback(() => {
16916
+ navigate("/archived");
16917
+ maybeClose();
16918
+ }, [navigate, maybeClose]);
16919
+ const onBack = useCallback(() => {
16920
+ navigate("/");
16921
+ maybeClose();
16922
+ }, [navigate, maybeClose]);
16923
+ return {
16924
+ activeSessionId,
16925
+ onSessionSelect,
16926
+ onNewChat,
16927
+ onArchivedView,
16928
+ onBack
16929
+ };
16930
+ }
16148
16931
 
16149
16932
  // ui/src/bichat/index.ts
16150
16933
  init_IotaContext();
@@ -16448,10 +17231,59 @@ function resolveArtifactName(artifact) {
16448
17231
  }
16449
17232
  return `${label} artifact`;
16450
17233
  }
17234
+ function mapSessionUser(rawUser) {
17235
+ if (!isRecord2(rawUser)) {
17236
+ return void 0;
17237
+ }
17238
+ const rawId = rawUser.id;
17239
+ const id = readNonEmptyString(rawId) ?? (typeof rawId === "number" && Number.isFinite(rawId) ? String(rawId) : null);
17240
+ if (!id) {
17241
+ return void 0;
17242
+ }
17243
+ const firstName = readString2(rawUser.firstName);
17244
+ const lastName = readString2(rawUser.lastName);
17245
+ const initials = readNonEmptyString(rawUser.initials) || `${firstName.charAt(0)}${lastName.charAt(0)}`.trim().toUpperCase() || "U";
17246
+ return {
17247
+ id,
17248
+ firstName,
17249
+ lastName,
17250
+ initials
17251
+ };
17252
+ }
17253
+ function mapSessionAccess(rawAccess) {
17254
+ if (!isRecord2(rawAccess)) {
17255
+ return void 0;
17256
+ }
17257
+ const role = readString2(rawAccess.role).toLowerCase();
17258
+ const source = readString2(rawAccess.source).toLowerCase();
17259
+ const normalizedRole = role === "owner" || role === "editor" || role === "viewer" || role === "read_all" ? role : "none";
17260
+ const normalizedSource = source === "owner" || source === "member" || source === "permission" ? source : "none";
17261
+ const canRead = rawAccess.canRead === true || rawAccess.canRead === "true";
17262
+ const canWrite = rawAccess.canWrite === true || rawAccess.canWrite === "true";
17263
+ const canManageMembers = rawAccess.canManageMembers === true || rawAccess.canManageMembers === "true";
17264
+ if (normalizedRole === "none" && normalizedSource === "none" && !canRead && !canWrite && !canManageMembers) {
17265
+ return void 0;
17266
+ }
17267
+ return {
17268
+ role: normalizedRole,
17269
+ source: normalizedSource,
17270
+ canRead,
17271
+ canWrite,
17272
+ canManageMembers
17273
+ };
17274
+ }
16451
17275
  function toSession(session) {
16452
17276
  return {
16453
- ...session,
16454
- status: session.status === "archived" ? "archived" : "active"
17277
+ id: readString2(session.id),
17278
+ title: readString2(session.title),
17279
+ status: session.status === "archived" ? "archived" : "active",
17280
+ pinned: Boolean(session.pinned),
17281
+ createdAt: readString2(session.createdAt),
17282
+ updatedAt: readString2(session.updatedAt),
17283
+ owner: mapSessionUser(session.owner),
17284
+ isGroup: Boolean(session.isGroup),
17285
+ memberCount: typeof session.memberCount === "number" ? session.memberCount : void 0,
17286
+ access: mapSessionAccess(session.access)
16455
17287
  };
16456
17288
  }
16457
17289
  function toSessionArtifact(artifact) {
@@ -16725,6 +17557,7 @@ function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
16725
17557
  id: userTurnID,
16726
17558
  content: readString2(rawTurn.userTurn.content),
16727
17559
  attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
17560
+ author: mapSessionUser(rawTurn.userTurn.author),
16728
17561
  createdAt: readString2(rawTurn.userTurn.createdAt, createdAt)
16729
17562
  },
16730
17563
  assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
@@ -16793,10 +17626,11 @@ function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
16793
17626
  return true;
16794
17627
  }).map((option, optionIndex) => {
16795
17628
  const label = readString2(option.label);
17629
+ const id = readString2(option.id, `${questionID}-opt-${optionIndex}`);
16796
17630
  return {
16797
- id: readString2(option.id, `${questionID}-opt-${optionIndex}`),
17631
+ id,
16798
17632
  label,
16799
- value: label
17633
+ value: id
16800
17634
  };
16801
17635
  }) : [];
16802
17636
  return {
@@ -17216,6 +18050,68 @@ async function clearSessionHistory(callRPC, sessionId) {
17216
18050
  async function compactSessionHistory(callRPC, sessionId) {
17217
18051
  return callRPC("bichat.session.compact", { id: sessionId });
17218
18052
  }
18053
+ async function listUsers(callRPC) {
18054
+ const data = await callRPC("bichat.user.list", {});
18055
+ return data.users.map((user) => ({
18056
+ id: String(user.id),
18057
+ firstName: user.firstName || "",
18058
+ lastName: user.lastName || "",
18059
+ initials: user.initials || ""
18060
+ }));
18061
+ }
18062
+ async function listAllSessions(callRPC, options) {
18063
+ const data = await callRPC("bichat.session.listAll", {
18064
+ limit: options?.limit ?? 50,
18065
+ offset: options?.offset ?? 0,
18066
+ includeArchived: options?.includeArchived ?? false,
18067
+ userId: options?.userId ?? null
18068
+ });
18069
+ return {
18070
+ sessions: data.sessions.map(toSession),
18071
+ total: typeof data.total === "number" ? data.total : data.sessions.length,
18072
+ hasMore: Boolean(data.hasMore)
18073
+ };
18074
+ }
18075
+ async function listSessionMembers(callRPC, sessionId) {
18076
+ const data = await callRPC("bichat.session.members.list", { sessionId });
18077
+ return data.members.map((member) => ({
18078
+ user: {
18079
+ id: String(member.user.id),
18080
+ firstName: member.user.firstName,
18081
+ lastName: member.user.lastName,
18082
+ initials: member.user.initials
18083
+ },
18084
+ role: (() => {
18085
+ const normalizedRole = (member.role || "").toLowerCase();
18086
+ if (normalizedRole === "owner") {
18087
+ return "owner";
18088
+ }
18089
+ if (normalizedRole === "editor") {
18090
+ return "editor";
18091
+ }
18092
+ return "viewer";
18093
+ })(),
18094
+ createdAt: member.createdAt,
18095
+ updatedAt: member.updatedAt
18096
+ }));
18097
+ }
18098
+ async function addSessionMember(callRPC, sessionId, userId, role) {
18099
+ await callRPC("bichat.session.members.add", {
18100
+ sessionId,
18101
+ userId,
18102
+ role: role.toUpperCase()
18103
+ });
18104
+ }
18105
+ async function updateSessionMemberRole(callRPC, sessionId, userId, role) {
18106
+ await callRPC("bichat.session.members.updateRole", {
18107
+ sessionId,
18108
+ userId,
18109
+ role: role.toUpperCase()
18110
+ });
18111
+ }
18112
+ async function removeSessionMember(callRPC, sessionId, userId) {
18113
+ await callRPC("bichat.session.members.remove", { sessionId, userId });
18114
+ }
17219
18115
 
17220
18116
  // ui/src/bichat/utils/sseParser.ts
17221
18117
  function* processDataLines(lines) {
@@ -17774,12 +18670,19 @@ async function submitQuestionAnswers(callRPC, sessionId, questionId, answers) {
17774
18670
  flatAnswers[qId] = answerData.options.join(", ");
17775
18671
  }
17776
18672
  }
17777
- await callRPC("bichat.question.submit", {
18673
+ const result = await callRPC("bichat.question.submit", {
17778
18674
  sessionId,
17779
18675
  checkpointId: questionId,
17780
18676
  answers: flatAnswers
17781
18677
  });
17782
- return { success: true };
18678
+ return {
18679
+ success: true,
18680
+ data: {
18681
+ session: toSession(result.session),
18682
+ turns: normalizeTurns(sanitizeConversationTurns(result.turns, sessionId)),
18683
+ pendingQuestion: sanitizePendingQuestion(result.pendingQuestion, sessionId)
18684
+ }
18685
+ };
17783
18686
  } catch (err) {
17784
18687
  return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
17785
18688
  }
@@ -17821,7 +18724,12 @@ async function uploadSessionArtifacts(callRPC, sessionId, files, uploadFileFn) {
17821
18724
  const data = await callRPC("bichat.session.uploadArtifacts", {
17822
18725
  sessionId,
17823
18726
  attachments: uploads.map((upload) => ({
17824
- uploadId: upload.id
18727
+ id: String(upload.id),
18728
+ filename: upload.name,
18729
+ uploadId: upload.id,
18730
+ mimeType: upload.mimetype || "application/octet-stream",
18731
+ sizeBytes: upload.size,
18732
+ url: upload.url
17825
18733
  }))
17826
18734
  });
17827
18735
  return {
@@ -17948,6 +18856,24 @@ var HttpDataSource = class {
17948
18856
  async compactSessionHistory(sessionId) {
17949
18857
  return compactSessionHistory(this.boundCallRPC, sessionId);
17950
18858
  }
18859
+ async listUsers() {
18860
+ return listUsers(this.boundCallRPC);
18861
+ }
18862
+ async listAllSessions(options) {
18863
+ return listAllSessions(this.boundCallRPC, options);
18864
+ }
18865
+ async listSessionMembers(sessionId) {
18866
+ return listSessionMembers(this.boundCallRPC, sessionId);
18867
+ }
18868
+ async addSessionMember(sessionId, userId, role) {
18869
+ return addSessionMember(this.boundCallRPC, sessionId, userId, role);
18870
+ }
18871
+ async updateSessionMemberRole(sessionId, userId, role) {
18872
+ return updateSessionMemberRole(this.boundCallRPC, sessionId, userId, role);
18873
+ }
18874
+ async removeSessionMember(sessionId, userId) {
18875
+ return removeSessionMember(this.boundCallRPC, sessionId, userId);
18876
+ }
17951
18877
  // -------------------------------------------------------------------------
17952
18878
  // Message transport (delegates to MessageTransport)
17953
18879
  // -------------------------------------------------------------------------
@@ -18070,6 +18996,6 @@ function createHttpDataSource(config) {
18070
18996
  return new HttpDataSource(config);
18071
18997
  }
18072
18998
 
18073
- export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, ActivityTrace, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, InteractiveTableCard, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TabbedChartGroup, TabbedTableGroup, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getToolLabel, getValidChildren, groupSessionsByDate, groupSteps, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useDataTable, useFocusTrap, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming2 as useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
18999
+ export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, ActivityTrace, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, AvatarStack, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, InteractiveTableCard, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionMembersModal, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TabbedChartGroup, TabbedTableGroup, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getToolLabel, getValidChildren, groupSessionsByDate, groupSteps, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBichatRouter, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useDataTable, useFocusTrap, useHttpDataSourceConfigFromApplet, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming2 as useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
18074
19000
  //# sourceMappingURL=index.mjs.map
18075
19001
  //# sourceMappingURL=index.mjs.map