@copilotz/chat-ui 0.9.20 → 0.9.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -64,6 +64,10 @@ var defaultChatConfig = {
64
64
  inputPlaceholder: "Type your message...",
65
65
  sendButton: "Send",
66
66
  sendMessageTooltip: "Send message",
67
+ stageMessageTooltip: "Stage message",
68
+ sendNowTooltip: "Send now",
69
+ stagedMessageLabel: "Not sent yet",
70
+ discardStagedMessageTooltip: "Discard",
67
71
  newThread: "New Conversation",
68
72
  deleteThread: "Delete Conversation",
69
73
  copyMessage: "Copy",
@@ -4309,6 +4313,9 @@ var VoiceComposer = ({
4309
4313
  // src/components/chat/ChatInput.tsx
4310
4314
  var import_lucide_react12 = require("lucide-react");
4311
4315
  var import_jsx_runtime25 = require("react/jsx-runtime");
4316
+ function createStagedSendId() {
4317
+ return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now()}_${Math.random().toString(36).slice(2)}`;
4318
+ }
4312
4319
  function getActiveMentionMatch(value, caret) {
4313
4320
  const prefix = value.slice(0, caret);
4314
4321
  const match = /(^|\s)@([\w.-]*)$/.exec(prefix);
@@ -4594,6 +4601,7 @@ var ChatInput = (0, import_react8.memo)(function ChatInput2({
4594
4601
  const [activeMention, setActiveMention] = (0, import_react8.useState)(null);
4595
4602
  const [activeMentionIndex, setActiveMentionIndex] = (0, import_react8.useState)(0);
4596
4603
  const [isDraggingFiles, setIsDraggingFiles] = (0, import_react8.useState)(false);
4604
+ const [stagedSends, setStagedSends] = (0, import_react8.useState)([]);
4597
4605
  const textareaRef = (0, import_react8.useRef)(null);
4598
4606
  const fileInputRef = (0, import_react8.useRef)(null);
4599
4607
  const dragDepthRef = (0, import_react8.useRef)(0);
@@ -4686,18 +4694,48 @@ var ChatInput = (0, import_react8.memo)(function ChatInput2({
4686
4694
  textareaRef.current?.setSelectionRange(nextCaret, nextCaret);
4687
4695
  });
4688
4696
  }, [activeMention, draftValue, onTargetAgentChange, updateDraftValue]);
4689
- const handleSubmit = (e) => {
4690
- e.preventDefault();
4691
- if (!draftValue.trim() && attachments.length === 0 || disabled || isGenerating) return;
4692
- const mentionedAgent = resolveTargetFromMentions(draftValue, mentionAgents);
4693
- if (mentionedAgent) {
4694
- onTargetAgentChange?.(mentionedAgent.id);
4695
- }
4696
- onSubmit(draftValue.trim(), attachments);
4697
+ const clearComposer = (0, import_react8.useCallback)(() => {
4697
4698
  updateDraftValue("");
4698
4699
  onAttachmentsChange([]);
4699
4700
  setActiveMention(null);
4700
4701
  setActiveMentionIndex(0);
4702
+ }, [onAttachmentsChange, updateDraftValue]);
4703
+ const submitResolvedMessage = (0, import_react8.useCallback)((content, messageAttachments) => {
4704
+ const mentionedAgent = resolveTargetFromMentions(content, mentionAgents);
4705
+ if (mentionedAgent) {
4706
+ onTargetAgentChange?.(mentionedAgent.id);
4707
+ }
4708
+ onSubmit(content.trim(), messageAttachments);
4709
+ }, [mentionAgents, onSubmit, onTargetAgentChange]);
4710
+ const stageCurrentDraft = (0, import_react8.useCallback)(() => {
4711
+ const content = draftValue.trim();
4712
+ if (!content && attachments.length === 0) return;
4713
+ setStagedSends((prev) => [
4714
+ ...prev,
4715
+ {
4716
+ id: createStagedSendId(),
4717
+ content,
4718
+ attachments: [...attachments]
4719
+ }
4720
+ ]);
4721
+ clearComposer();
4722
+ }, [attachments, clearComposer, draftValue]);
4723
+ const sendStagedMessage = (0, import_react8.useCallback)((staged) => {
4724
+ setStagedSends((prev) => prev.filter((item) => item.id !== staged.id));
4725
+ submitResolvedMessage(staged.content, staged.attachments);
4726
+ }, [submitResolvedMessage]);
4727
+ const removeStagedMessage = (0, import_react8.useCallback)((id) => {
4728
+ setStagedSends((prev) => prev.filter((item) => item.id !== id));
4729
+ }, []);
4730
+ const handleSubmit = (e) => {
4731
+ e.preventDefault();
4732
+ if (!draftValue.trim() && attachments.length === 0 || disabled) return;
4733
+ if (isGenerating) {
4734
+ stageCurrentDraft();
4735
+ return;
4736
+ }
4737
+ submitResolvedMessage(draftValue.trim(), attachments);
4738
+ clearComposer();
4701
4739
  };
4702
4740
  const handleKeyDown = (e) => {
4703
4741
  if (isMentionMenuOpen) {
@@ -5221,6 +5259,51 @@ var ChatInput = (0, import_react8.memo)(function ChatInput2({
5221
5259
  { remaining: Math.max(0, maxAttachments - attachments.length) }
5222
5260
  ) })
5223
5261
  ] }) }),
5262
+ stagedSends.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex flex-col gap-1.5", children: stagedSends.map((staged) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5263
+ "div",
5264
+ {
5265
+ className: "flex min-h-10 items-center gap-2 rounded-2xl border border-border/80 bg-muted/45 px-2.5 py-1.5",
5266
+ children: [
5267
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "min-w-0 flex-1", children: [
5268
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "truncate text-sm text-foreground", children: staged.content || staged.attachments[0]?.fileName || config?.labels?.attachmentsCount || "Attachment" }),
5269
+ staged.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "text-xs text-muted-foreground", children: formatLabel(
5270
+ config?.labels?.attachmentsCount,
5271
+ "{{count}}/{{max}} attachments",
5272
+ { count: staged.attachments.length, max: maxAttachments }
5273
+ ) }),
5274
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "text-xs text-muted-foreground", children: config?.labels?.stagedMessageLabel || "Not sent yet" })
5275
+ ] }),
5276
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5277
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5278
+ Button,
5279
+ {
5280
+ type: "button",
5281
+ size: "icon",
5282
+ className: "h-8 w-8 rounded-full",
5283
+ onClick: () => sendStagedMessage(staged),
5284
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Send, { className: "h-4 w-4" })
5285
+ }
5286
+ ) }),
5287
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.sendNowTooltip || config?.labels?.sendMessageTooltip })
5288
+ ] }),
5289
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5290
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5291
+ Button,
5292
+ {
5293
+ type: "button",
5294
+ variant: "ghost",
5295
+ size: "icon",
5296
+ className: "h-8 w-8 rounded-full text-muted-foreground hover:text-foreground",
5297
+ onClick: () => removeStagedMessage(staged.id),
5298
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.X, { className: "h-4 w-4" })
5299
+ }
5300
+ ) }),
5301
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.discardStagedMessageTooltip || config?.labels?.voiceCancel })
5302
+ ] })
5303
+ ]
5304
+ },
5305
+ staged.id
5306
+ )) }),
5224
5307
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative min-w-0", children: [
5225
5308
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5226
5309
  Textarea,
@@ -5328,19 +5411,34 @@ var ChatInput = (0, import_react8.memo)(function ChatInput2({
5328
5411
  ) }),
5329
5412
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.voiceEnter })
5330
5413
  ] }),
5331
- isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5332
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5333
- Button,
5334
- {
5335
- type: "button",
5336
- variant: "secondary",
5337
- size: "icon",
5338
- className: "h-9 w-9 rounded-full",
5339
- onClick: onStopGeneration,
5340
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Square, { className: "h-4 w-4" })
5341
- }
5342
- ) }),
5343
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.stopGenerationTooltip })
5414
+ isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5415
+ (draftValue.trim() || attachments.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5416
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5417
+ Button,
5418
+ {
5419
+ type: "submit",
5420
+ size: "icon",
5421
+ className: "h-9 w-9 rounded-full",
5422
+ disabled,
5423
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Send, { className: "h-4 w-4" })
5424
+ }
5425
+ ) }),
5426
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.stageMessageTooltip || config?.labels?.sendMessageTooltip })
5427
+ ] }),
5428
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5429
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5430
+ Button,
5431
+ {
5432
+ type: "button",
5433
+ variant: "secondary",
5434
+ size: "icon",
5435
+ className: "h-9 w-9 rounded-full",
5436
+ onClick: onStopGeneration,
5437
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Square, { className: "h-4 w-4" })
5438
+ }
5439
+ ) }),
5440
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipContent, { children: config?.labels?.stopGenerationTooltip })
5441
+ ] })
5344
5442
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Tooltip, { children: [
5345
5443
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5346
5444
  Button,
@@ -5924,6 +6022,7 @@ var ChatUI = ({
5924
6022
  isMessagesLoading = false,
5925
6023
  isLoadingOlderMessages = false,
5926
6024
  hasMoreMessagesBefore = false,
6025
+ activityNotice = null,
5927
6026
  callbacks = {},
5928
6027
  onLoadOlderMessages,
5929
6028
  user,
@@ -6572,41 +6671,50 @@ var ChatUI = ({
6572
6671
  ] })
6573
6672
  }
6574
6673
  ),
6575
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "-mt-8 bg-gradient-to-t from-background via-background/95 to-transparent px-0 pb-[env(safe-area-inset-bottom)] pt-10", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6576
- ChatInput,
6577
- {
6578
- value: inputValue,
6579
- onChange: (value) => {
6580
- inputValueRef.current = value;
6581
- if (initialInputApplied.current && !initialInputConsumedRef.current) {
6582
- initialInputConsumedRef.current = true;
6583
- onInitialInputConsumed?.();
6584
- }
6585
- },
6586
- onSubmit: handleSendMessage,
6587
- attachments,
6588
- onAttachmentsChange: setAttachments,
6589
- placeholder: config.labels.inputPlaceholder,
6590
- disabled: false,
6591
- isGenerating,
6592
- onStopGeneration: callbacks.onStopGeneration,
6593
- enableFileUpload: config.features.enableFileUpload,
6594
- enableAudioRecording: config.features.enableAudioRecording,
6595
- maxAttachments: config.features.maxAttachments,
6596
- maxFileSize: config.features.maxFileSize,
6597
- acceptedFileTypes: config.features.acceptedFileTypes,
6598
- config,
6599
- mentionAgents: participantIds && participantIds.length > 0 ? agentOptions.filter(
6600
- (a) => participantIds.includes(a.id)
6601
- ) : agentOptions,
6602
- targetAgentId,
6603
- showTargetAgentSelector: Boolean(
6604
- isMultiAgentMode && shouldShowAgentSelector && onTargetAgentChange
6605
- ),
6606
- targetAgentSelectorPlaceholder: config.agentSelector?.label || "Select agent",
6607
- onTargetAgentChange
6608
- }
6609
- ) })
6674
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "-mt-8 bg-gradient-to-t from-background via-background/95 to-transparent px-0 pb-[env(safe-area-inset-bottom)] pt-10", children: [
6675
+ activityNotice && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "mx-auto mb-2 w-full max-w-3xl px-3 md:px-2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6676
+ "div",
6677
+ {
6678
+ className: activityNotice.tone === "error" ? "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive" : "rounded-md border border-border bg-muted/70 px-3 py-2 text-sm text-muted-foreground",
6679
+ children: activityNotice.message
6680
+ }
6681
+ ) }),
6682
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6683
+ ChatInput,
6684
+ {
6685
+ value: inputValue,
6686
+ onChange: (value) => {
6687
+ inputValueRef.current = value;
6688
+ if (initialInputApplied.current && !initialInputConsumedRef.current) {
6689
+ initialInputConsumedRef.current = true;
6690
+ onInitialInputConsumed?.();
6691
+ }
6692
+ },
6693
+ onSubmit: handleSendMessage,
6694
+ attachments,
6695
+ onAttachmentsChange: setAttachments,
6696
+ placeholder: config.labels.inputPlaceholder,
6697
+ disabled: false,
6698
+ isGenerating,
6699
+ onStopGeneration: callbacks.onStopGeneration,
6700
+ enableFileUpload: config.features.enableFileUpload,
6701
+ enableAudioRecording: config.features.enableAudioRecording,
6702
+ maxAttachments: config.features.maxAttachments,
6703
+ maxFileSize: config.features.maxFileSize,
6704
+ acceptedFileTypes: config.features.acceptedFileTypes,
6705
+ config,
6706
+ mentionAgents: participantIds && participantIds.length > 0 ? agentOptions.filter(
6707
+ (a) => participantIds.includes(a.id)
6708
+ ) : agentOptions,
6709
+ targetAgentId,
6710
+ showTargetAgentSelector: Boolean(
6711
+ isMultiAgentMode && shouldShowAgentSelector && onTargetAgentChange
6712
+ ),
6713
+ targetAgentSelectorPlaceholder: config.agentSelector?.label || "Select agent",
6714
+ onTargetAgentChange
6715
+ }
6716
+ )
6717
+ ] })
6610
6718
  ] }),
6611
6719
  config?.customComponent?.component && !isMobile && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
6612
6720
  "div",