@liveblocks/react-ui 3.21.0-exp2 → 3.21.0-private1

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.
@@ -3,9 +3,9 @@
3
3
 
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var core = require('@liveblocks/core');
6
- var react$1 = require('@liveblocks/react');
6
+ var react = require('@liveblocks/react');
7
7
  var _private = require('@liveblocks/react/_private');
8
- var react = require('react');
8
+ var react$1 = require('react');
9
9
  var config = require('../config.cjs');
10
10
  var constants = require('../constants.cjs');
11
11
  require('../icons/index.cjs');
@@ -36,6 +36,17 @@ var User = require('./internal/User.cjs');
36
36
  var Users = require('../icons/Users.cjs');
37
37
 
38
38
 
39
+ function useThreadVisibility(threadId) {
40
+ const client = react.useClient();
41
+ const store = _private.getUmbrellaStoreForClient(client);
42
+ return _private.useSignal(
43
+ store.outputs.threads,
44
+ react$1.useCallback(
45
+ (threads) => threadId !== void 0 ? threads.get(threadId)?.visibility : void 0,
46
+ [threadId]
47
+ )
48
+ );
49
+ }
39
50
  function ComposerInsertMentionEditorAction({
40
51
  label,
41
52
  tooltipLabel,
@@ -44,10 +55,10 @@ function ComposerInsertMentionEditorAction({
44
55
  ...props
45
56
  }) {
46
57
  const { createMention } = contexts.useComposer();
47
- const preventDefault = react.useCallback((event) => {
58
+ const preventDefault = react$1.useCallback((event) => {
48
59
  event.preventDefault();
49
60
  }, []);
50
- const handleClick = react.useCallback(
61
+ const handleClick = react$1.useCallback(
51
62
  (event) => {
52
63
  onClick?.(event);
53
64
  if (!event.isDefaultPrevented()) {
@@ -77,10 +88,10 @@ function ComposerInsertEmojiEditorAction({
77
88
  ...props
78
89
  }) {
79
90
  const { insertText } = contexts.useComposer();
80
- const preventDefault = react.useCallback((event) => {
91
+ const preventDefault = react$1.useCallback((event) => {
81
92
  event.preventDefault();
82
93
  }, []);
83
- const stopPropagation = react.useCallback((event) => {
94
+ const stopPropagation = react$1.useCallback((event) => {
84
95
  event.stopPropagation();
85
96
  }, []);
86
97
  return /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker.EmojiPicker, { onEmojiSelect: insertText, onOpenChange: onPickerOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { content: tooltipLabel ?? label, children: /* @__PURE__ */ jsxRuntime.jsx(EmojiPicker.EmojiPickerTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -101,10 +112,10 @@ function ComposerAttachFilesEditorAction({
101
112
  className,
102
113
  ...props
103
114
  }) {
104
- const preventDefault = react.useCallback((event) => {
115
+ const preventDefault = react$1.useCallback((event) => {
105
116
  event.preventDefault();
106
117
  }, []);
107
- const stopPropagation = react.useCallback((event) => {
118
+ const stopPropagation = react$1.useCallback((event) => {
108
119
  event.stopPropagation();
109
120
  }, []);
110
121
  return /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { content: tooltipLabel ?? label, children: /* @__PURE__ */ jsxRuntime.jsx(index.AttachFiles, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -210,7 +221,7 @@ function MarkToggle({
210
221
  ...props
211
222
  }) {
212
223
  const $ = overrides.useOverrides();
213
- const label = react.useMemo(() => {
224
+ const label = react$1.useMemo(() => {
214
225
  return $.COMPOSER_TOGGLE_MARK(mark);
215
226
  }, [$, mark]);
216
227
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -251,7 +262,7 @@ function ComposerFileAttachment({
251
262
  }) {
252
263
  const { removeAttachment } = contexts.useComposer();
253
264
  const { roomId } = contexts.useComposerEditorContext();
254
- const handleDeleteClick = react.useCallback(() => {
265
+ const handleDeleteClick = react$1.useCallback(() => {
255
266
  removeAttachment(attachment.id);
256
267
  }, [attachment.id, removeAttachment]);
257
268
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -310,7 +321,7 @@ function ComposerEditorContainer({
310
321
  const { isEmpty } = contexts.useComposer();
311
322
  const { hasMaxAttachments } = contexts.useComposerAttachmentsContext();
312
323
  const $ = overrides.useOverrides(overrides$1);
313
- const components = react.useMemo(() => {
324
+ const components = react$1.useMemo(() => {
314
325
  return {
315
326
  ...editorRequiredComponents,
316
327
  FloatingToolbar: showFormattingControls ? ComposerFloatingToolbar : void 0
@@ -322,10 +333,10 @@ function ComposerEditorContainer({
322
333
  _private.useLayoutEffect(() => {
323
334
  onEmptyChange(isEmpty);
324
335
  }, [isEmpty, onEmptyChange]);
325
- const preventDefault = react.useCallback((event) => {
336
+ const preventDefault = react$1.useCallback((event) => {
326
337
  event.preventDefault();
327
338
  }, []);
328
- const stopPropagation = react.useCallback((event) => {
339
+ const stopPropagation = react$1.useCallback((event) => {
329
340
  event.stopPropagation();
330
341
  }, []);
331
342
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lb-composer-editor-container", ...dropAreaProps, children: [
@@ -387,12 +398,13 @@ function ComposerEditorContainer({
387
398
  ] }) })
388
399
  ] });
389
400
  }
390
- const ComposerRoomIdContext = react.createContext(null);
391
- const Composer = react.forwardRef(
401
+ const ComposerRoomIdContext = react$1.createContext(null);
402
+ const Composer = react$1.forwardRef(
392
403
  ({
393
404
  threadId,
394
405
  commentId,
395
406
  metadata,
407
+ visibility,
396
408
  commentMetadata,
397
409
  defaultValue,
398
410
  defaultAttachments,
@@ -414,7 +426,7 @@ const Composer = react.forwardRef(
414
426
  roomId: _roomId,
415
427
  ...props
416
428
  }, forwardedRef) => {
417
- const room = react$1.useRoom({ allowOutsideRoom: true });
429
+ const room = react.useRoom({ allowOutsideRoom: true });
418
430
  const roomId = _roomId !== void 0 ? _roomId : room?.id;
419
431
  if (roomId === void 0) {
420
432
  throw new Error(
@@ -426,22 +438,28 @@ const Composer = react.forwardRef(
426
438
  const editComment = _private.useEditRoomComment(roomId);
427
439
  const { preventUnsavedComposerChanges } = config.useLiveblocksUiConfig();
428
440
  const hasResolveMentionSuggestions = _private.useResolveMentionSuggestions() !== void 0;
429
- const isEmptyRef = react.useRef(true);
430
- const isEmojiPickerOpenRef = react.useRef(false);
441
+ const isEmptyRef = react$1.useRef(true);
442
+ const isEmojiPickerOpenRef = react$1.useRef(false);
431
443
  const $ = overrides.useOverrides(overrides$1);
432
444
  const [isCollapsed, onCollapsedChange] = useControllableState.useControllableState(
433
445
  defaultCollapsed ?? false,
434
446
  controlledCollapsed,
435
447
  controlledOnCollapsedChange
436
448
  );
437
- const canComment = _private.useHasPermissionAccess(roomId, "comments", "write");
438
- const setEmptyRef = react.useCallback((isEmpty) => {
449
+ const threadVisibility = useThreadVisibility(threadId);
450
+ const composerVisibility = threadId ? threadVisibility : visibility ?? "public";
451
+ const canComment = _private.useHasPermissionAccess(
452
+ roomId,
453
+ composerVisibility ? `comments:${composerVisibility}` : "comments",
454
+ "write"
455
+ );
456
+ const setEmptyRef = react$1.useCallback((isEmpty) => {
439
457
  isEmptyRef.current = isEmpty;
440
458
  }, []);
441
- const setEmojiPickerOpenRef = react.useCallback((isEmojiPickerOpen) => {
459
+ const setEmojiPickerOpenRef = react$1.useCallback((isEmojiPickerOpen) => {
442
460
  isEmojiPickerOpenRef.current = isEmojiPickerOpen;
443
461
  }, []);
444
- const handleFocus = react.useCallback(
462
+ const handleFocus = react$1.useCallback(
445
463
  (event) => {
446
464
  onFocus?.(event);
447
465
  if (event.isDefaultPrevented()) {
@@ -453,7 +471,7 @@ const Composer = react.forwardRef(
453
471
  },
454
472
  [onCollapsedChange, onFocus, canComment]
455
473
  );
456
- const handleBlur = react.useCallback(
474
+ const handleBlur = react$1.useCallback(
457
475
  (event) => {
458
476
  onBlur?.(event);
459
477
  if (event.isDefaultPrevented()) {
@@ -468,7 +486,7 @@ const Composer = react.forwardRef(
468
486
  },
469
487
  [onBlur, onCollapsedChange]
470
488
  );
471
- const handleEditorClick = react.useCallback(
489
+ const handleEditorClick = react$1.useCallback(
472
490
  (event) => {
473
491
  event.stopPropagation();
474
492
  if (isEmptyRef.current && canComment) {
@@ -477,7 +495,7 @@ const Composer = react.forwardRef(
477
495
  },
478
496
  [onCollapsedChange, canComment]
479
497
  );
480
- const handleComposerSubmit = react.useCallback(
498
+ const handleComposerSubmit = react$1.useCallback(
481
499
  (comment, event) => {
482
500
  onComposerSubmit?.(comment, event);
483
501
  if (event.isDefaultPrevented()) {
@@ -503,6 +521,7 @@ const Composer = react.forwardRef(
503
521
  createThread({
504
522
  body: comment.body,
505
523
  metadata,
524
+ visibility,
506
525
  commentMetadata,
507
526
  attachments: comment.attachments
508
527
  });
@@ -515,6 +534,7 @@ const Composer = react.forwardRef(
515
534
  editComment,
516
535
  commentMetadata,
517
536
  metadata,
537
+ visibility,
518
538
  onComposerSubmit,
519
539
  threadId
520
540
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.cjs","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DCM,\n DTM,\n GroupMentionData,\n Patchable,\n} from \"@liveblocks/core\";\nimport { assertNever, MENTION_CHARACTER } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useHasPermissionAccess,\n useLayoutEffect,\n useResolveMentionSuggestions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport { createContext, forwardRef, useCallback, useMemo, useRef } from \"react\";\n\nimport { useLiveblocksUiConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET } from \"../constants\";\nimport { UsersIcon } from \"../icons\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { GroupAvatar, UserAvatar } from \"./Avatar\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { GroupDescription } from \"./internal/GroupDescription\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\nexport type ComposerCreateThreadProps<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = {\n threadId?: never;\n\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: TM;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerCreateCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n\n commentId?: never;\n\n metadata?: never;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerEditCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n\n metadata?: never;\n\n /**\n * The metadata of the comment to edit.\n */\n commentMetadata?: Patchable<CM>;\n};\n\nexport type ComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = Omit<ComponentPropsWithoutRef<\"form\">, \"defaultValue\"> &\n (\n | ComposerCreateThreadProps<TM, CM>\n | ComposerCreateCommentProps<CM>\n | ComposerEditCommentProps<CM>\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurOnSubmit?: boolean;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n> {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\ninterface ComposerMentionProps extends ComposerEditorMentionProps {\n overrides?: ComposerProps[\"overrides\"];\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerUserMention({ mention }: ComposerMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-mention lb-composer-mention\">\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerGroupMention({ mention }: ComposerMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <ComposerPrimitive.Mention\n className=\"lb-mention lb-composer-mention\"\n data-self={isMember ? \"\" : undefined}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nexport function ComposerMention({ mention, ...props }: ComposerMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <ComposerUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <ComposerGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nfunction ComposerMentionSuggestions({\n mentions,\n}: ComposerEditorMentionSuggestionsProps) {\n return mentions.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {mentions.map((mention) => {\n return (\n <ComposerPrimitive.SuggestionsListItem\n key={mention.id}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={mention.id}\n >\n {mention.kind === \"user\" ? (\n <>\n <UserAvatar\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </>\n ) : mention.kind === \"group\" ? (\n <>\n <GroupAvatar\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n icon={<UsersIcon />}\n />\n <Group\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group\"\n >\n <GroupDescription\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group-description\"\n />\n </Group>\n </>\n ) : (\n assertNever(mention, \"Unhandled mention kind\")\n )}\n </ComposerPrimitive.SuggestionsListItem>\n );\n })}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={cn(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-composer-attachments\", className)} {...props}>\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || !showAttachments || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n {\n threadId,\n commentId,\n metadata,\n commentMetadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n blurOnSubmit = true,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<TM, CM>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUiConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const canComment = useHasPermissionAccess(roomId, \"comments\", \"write\");\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n metadata: commentMetadata,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n metadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata,\n commentMetadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n commentMetadata,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={cn(\"lb-root lb-composer lb-composer-form\", className)}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n blurOnSubmit={blurOnSubmit}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n props: ComposerProps<TM, CM> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkQA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AAEI;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACO;AACf;AAAA;AAIZ;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAIQ;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACK;AACb;AAAA;AAMhB;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAGM;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACU;AAClB;AAAA;AAKd;AAEA;AACE;AAEI;AAAuD;AAC7B;AAGhC;AAEA;AACE;AAEA;AACE;AAAmB;AAAlB;AACW;AACiB;AAE3B;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAyD;AAGzD;AAA0D;AAG1D;AAAoD;AAE1D;AAEA;AAAoC;AAEpC;AACE;AAIQ;AACE;AAAmB;AAAlB;AAEW;AACK;AAIX;AAAA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AACA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AAIA;AAAA;AAAC;AAAA;AACkB;AACP;AACO;AAAA;AACnB;AACA;AAAC;AAAA;AACkB;AACP;AAEV;AAAC;AAAA;AACkB;AACP;AAAA;AACZ;AAAA;AACF;AAG2C;AAAA;AAjClC;AAmCf;AAMZ;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACE;AAAC;AAAA;AACU;AACT;AAC2C;AAM3C;AAAA;AAGN;AAMA;AAAiC;AAC0C;AAEN;AAGjE;AAAC;AAAA;AACM;AACI;AACgB;AAAA;AAC3B;AAGJ;AAEA;AAIA;AACE;AAKF;AAEA;AACE;AAKF;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACE;AAAC;AAAA;AACkD;AAC7C;AACJ;AACe;AACK;AACpB;AACA;AAAA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AAIQ;AACE;AAAC;AAAA;AAEC;AACA;AAAA;AAFgB;AAGlB;AAMZ;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC3B;AAG5C;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAEI;AAAA;AAAmB;AAAlB;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AAAA;AACT;AAC+D;AAG3D;AACG;AACC;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEF;AAAC;AAAA;AACU;AACW;AACpB;AAAA;AACF;AAEE;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEJ;AACiC;AAMvB;AAAC;AAAA;AACgB;AACN;AACC;AACF;AACM;AACE;AAAA;AAM5B;AACF;AAKI;AAAgB;AACb;AAEP;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACG;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AAEA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACJ;AACW;AACtB;AAED;AAAc;AACZ;AACc;AACJ;AACW;AACtB;AAED;AAAa;AACG;AACd;AACA;AACqB;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEI;AAAmB;AAAlB;AACmB;AAC6C;AACxD;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AACA;AAEA;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AAAA;AACF;AAAA;AAEJ;AAGN;;;;"}
1
+ {"version":3,"file":"Composer.cjs","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DCM,\n DTM,\n GroupMentionData,\n Patchable,\n ThreadData,\n ThreadVisibility,\n} from \"@liveblocks/core\";\nimport { assertNever, MENTION_CHARACTER } from \"@liveblocks/core\";\nimport { useClient, useRoom } from \"@liveblocks/react\";\nimport {\n getUmbrellaStoreForClient,\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useHasPermissionAccess,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useSignal,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport { createContext, forwardRef, useCallback, useMemo, useRef } from \"react\";\n\nimport { useLiveblocksUiConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET } from \"../constants\";\nimport { UsersIcon } from \"../icons\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { GroupAvatar, UserAvatar } from \"./Avatar\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { GroupDescription } from \"./internal/GroupDescription\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\nexport type ComposerCreateThreadProps<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = {\n threadId?: never;\n\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: TM;\n\n /**\n * The visibility of the thread to create.\n */\n visibility?: ThreadVisibility;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerCreateCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n\n commentId?: never;\n\n metadata?: never;\n\n visibility?: never;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerEditCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n\n metadata?: never;\n\n visibility?: never;\n\n /**\n * The metadata of the comment to edit.\n */\n commentMetadata?: Patchable<CM>;\n};\n\nexport type ComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = Omit<ComponentPropsWithoutRef<\"form\">, \"defaultValue\"> &\n (\n | ComposerCreateThreadProps<TM, CM>\n | ComposerCreateCommentProps<CM>\n | ComposerEditCommentProps<CM>\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurOnSubmit?: boolean;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n> {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction useThreadVisibility(\n threadId: string | undefined\n): ThreadData[\"visibility\"] | undefined {\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n\n return useSignal(\n store.outputs.threads,\n useCallback(\n (threads) =>\n threadId !== undefined ? threads.get(threadId)?.visibility : undefined,\n [threadId]\n )\n );\n}\n\ninterface ComposerMentionProps extends ComposerEditorMentionProps {\n overrides?: ComposerProps[\"overrides\"];\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerUserMention({ mention }: ComposerMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-mention lb-composer-mention\">\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerGroupMention({ mention }: ComposerMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <ComposerPrimitive.Mention\n className=\"lb-mention lb-composer-mention\"\n data-self={isMember ? \"\" : undefined}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nexport function ComposerMention({ mention, ...props }: ComposerMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <ComposerUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <ComposerGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nfunction ComposerMentionSuggestions({\n mentions,\n}: ComposerEditorMentionSuggestionsProps) {\n return mentions.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {mentions.map((mention) => {\n return (\n <ComposerPrimitive.SuggestionsListItem\n key={mention.id}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={mention.id}\n >\n {mention.kind === \"user\" ? (\n <>\n <UserAvatar\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </>\n ) : mention.kind === \"group\" ? (\n <>\n <GroupAvatar\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n icon={<UsersIcon />}\n />\n <Group\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group\"\n >\n <GroupDescription\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group-description\"\n />\n </Group>\n </>\n ) : (\n assertNever(mention, \"Unhandled mention kind\")\n )}\n </ComposerPrimitive.SuggestionsListItem>\n );\n })}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={cn(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-composer-attachments\", className)} {...props}>\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || !showAttachments || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n {\n threadId,\n commentId,\n metadata,\n visibility,\n commentMetadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n blurOnSubmit = true,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<TM, CM>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUiConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const threadVisibility = useThreadVisibility(threadId);\n const composerVisibility = threadId\n ? threadVisibility\n : (visibility ?? \"public\");\n const canComment = useHasPermissionAccess(\n roomId,\n composerVisibility ? `comments:${composerVisibility}` : \"comments\",\n \"write\"\n );\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n metadata: commentMetadata,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n metadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata,\n visibility,\n commentMetadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n commentMetadata,\n metadata,\n visibility,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={cn(\"lb-root lb-composer lb-composer-form\", className)}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n blurOnSubmit={blurOnSubmit}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n props: ComposerProps<TM, CM> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2QA;AAGE;AACA;AAEA;AAAO;AACS;AACd;AAEiE;AACtD;AACX;AAEJ;AAMA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AAEI;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACO;AACf;AAAA;AAIZ;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAIQ;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACK;AACb;AAAA;AAMhB;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAGM;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACU;AAClB;AAAA;AAKd;AAEA;AACE;AAEI;AAAuD;AAC7B;AAGhC;AAEA;AACE;AAEA;AACE;AAAmB;AAAlB;AACW;AACiB;AAE3B;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAyD;AAGzD;AAA0D;AAG1D;AAAoD;AAE1D;AAEA;AAAoC;AAEpC;AACE;AAIQ;AACE;AAAmB;AAAlB;AAEW;AACK;AAIX;AAAA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AACA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AAIA;AAAA;AAAC;AAAA;AACkB;AACP;AACO;AAAA;AACnB;AACA;AAAC;AAAA;AACkB;AACP;AAEV;AAAC;AAAA;AACkB;AACP;AAAA;AACZ;AAAA;AACF;AAG2C;AAAA;AAjClC;AAmCf;AAMZ;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACE;AAAC;AAAA;AACU;AACT;AAC2C;AAM3C;AAAA;AAGN;AAMA;AAAiC;AAC0C;AAEN;AAGjE;AAAC;AAAA;AACM;AACI;AACgB;AAAA;AAC3B;AAGJ;AAEA;AAIA;AACE;AAKF;AAEA;AACE;AAKF;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACE;AAAC;AAAA;AACkD;AAC7C;AACJ;AACe;AACK;AACpB;AACA;AAAA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AAIQ;AACE;AAAC;AAAA;AAEC;AACA;AAAA;AAFgB;AAGlB;AAMZ;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC3B;AAG5C;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAEI;AAAA;AAAmB;AAAlB;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AAAA;AACT;AAC+D;AAG3D;AACG;AACC;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEF;AAAC;AAAA;AACU;AACW;AACpB;AAAA;AACF;AAEE;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEJ;AACiC;AAMvB;AAAC;AAAA;AACgB;AACN;AACC;AACF;AACM;AACE;AAAA;AAM5B;AACF;AAKI;AAAgB;AACb;AAEP;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACG;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AACA;AAGA;AAAmB;AACjB;AACwD;AACxD;AAGF;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACJ;AACW;AACtB;AAED;AAAc;AACZ;AACc;AACJ;AACW;AACtB;AAED;AAAa;AACG;AACd;AACA;AACA;AACqB;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEI;AAAmB;AAAlB;AACmB;AAC6C;AACxD;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AACA;AAEA;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AAAA;AACF;AAAA;AAEJ;AAGN;;;;"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { MENTION_CHARACTER, assertNever } from '@liveblocks/core';
4
- import { useRoom } from '@liveblocks/react';
5
- import { useLayoutEffect, useCreateRoomThread, useCreateRoomComment, useEditRoomComment, useResolveMentionSuggestions, useHasPermissionAccess } from '@liveblocks/react/_private';
4
+ import { useClient, useRoom } from '@liveblocks/react';
5
+ import { getUmbrellaStoreForClient, useSignal, useLayoutEffect, useCreateRoomThread, useCreateRoomComment, useEditRoomComment, useResolveMentionSuggestions, useHasPermissionAccess } from '@liveblocks/react/_private';
6
6
  import { useCallback, useMemo, createContext, forwardRef, useRef } from 'react';
7
7
  import { useLiveblocksUiConfig } from '../config.js';
8
8
  import { FLOATING_ELEMENT_SIDE_OFFSET } from '../constants.js';
@@ -34,6 +34,17 @@ import { User } from './internal/User.js';
34
34
  import { UsersIcon } from '../icons/Users.js';
35
35
 
36
36
 
37
+ function useThreadVisibility(threadId) {
38
+ const client = useClient();
39
+ const store = getUmbrellaStoreForClient(client);
40
+ return useSignal(
41
+ store.outputs.threads,
42
+ useCallback(
43
+ (threads) => threadId !== void 0 ? threads.get(threadId)?.visibility : void 0,
44
+ [threadId]
45
+ )
46
+ );
47
+ }
37
48
  function ComposerInsertMentionEditorAction({
38
49
  label,
39
50
  tooltipLabel,
@@ -391,6 +402,7 @@ const Composer = forwardRef(
391
402
  threadId,
392
403
  commentId,
393
404
  metadata,
405
+ visibility,
394
406
  commentMetadata,
395
407
  defaultValue,
396
408
  defaultAttachments,
@@ -432,7 +444,13 @@ const Composer = forwardRef(
432
444
  controlledCollapsed,
433
445
  controlledOnCollapsedChange
434
446
  );
435
- const canComment = useHasPermissionAccess(roomId, "comments", "write");
447
+ const threadVisibility = useThreadVisibility(threadId);
448
+ const composerVisibility = threadId ? threadVisibility : visibility ?? "public";
449
+ const canComment = useHasPermissionAccess(
450
+ roomId,
451
+ composerVisibility ? `comments:${composerVisibility}` : "comments",
452
+ "write"
453
+ );
436
454
  const setEmptyRef = useCallback((isEmpty) => {
437
455
  isEmptyRef.current = isEmpty;
438
456
  }, []);
@@ -501,6 +519,7 @@ const Composer = forwardRef(
501
519
  createThread({
502
520
  body: comment.body,
503
521
  metadata,
522
+ visibility,
504
523
  commentMetadata,
505
524
  attachments: comment.attachments
506
525
  });
@@ -513,6 +532,7 @@ const Composer = forwardRef(
513
532
  editComment,
514
533
  commentMetadata,
515
534
  metadata,
535
+ visibility,
516
536
  onComposerSubmit,
517
537
  threadId
518
538
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DCM,\n DTM,\n GroupMentionData,\n Patchable,\n} from \"@liveblocks/core\";\nimport { assertNever, MENTION_CHARACTER } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport {\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useHasPermissionAccess,\n useLayoutEffect,\n useResolveMentionSuggestions,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport { createContext, forwardRef, useCallback, useMemo, useRef } from \"react\";\n\nimport { useLiveblocksUiConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET } from \"../constants\";\nimport { UsersIcon } from \"../icons\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { GroupAvatar, UserAvatar } from \"./Avatar\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { GroupDescription } from \"./internal/GroupDescription\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\nexport type ComposerCreateThreadProps<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = {\n threadId?: never;\n\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: TM;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerCreateCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n\n commentId?: never;\n\n metadata?: never;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerEditCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n\n metadata?: never;\n\n /**\n * The metadata of the comment to edit.\n */\n commentMetadata?: Patchable<CM>;\n};\n\nexport type ComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = Omit<ComponentPropsWithoutRef<\"form\">, \"defaultValue\"> &\n (\n | ComposerCreateThreadProps<TM, CM>\n | ComposerCreateCommentProps<CM>\n | ComposerEditCommentProps<CM>\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurOnSubmit?: boolean;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n> {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\ninterface ComposerMentionProps extends ComposerEditorMentionProps {\n overrides?: ComposerProps[\"overrides\"];\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerUserMention({ mention }: ComposerMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-mention lb-composer-mention\">\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerGroupMention({ mention }: ComposerMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <ComposerPrimitive.Mention\n className=\"lb-mention lb-composer-mention\"\n data-self={isMember ? \"\" : undefined}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nexport function ComposerMention({ mention, ...props }: ComposerMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <ComposerUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <ComposerGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nfunction ComposerMentionSuggestions({\n mentions,\n}: ComposerEditorMentionSuggestionsProps) {\n return mentions.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {mentions.map((mention) => {\n return (\n <ComposerPrimitive.SuggestionsListItem\n key={mention.id}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={mention.id}\n >\n {mention.kind === \"user\" ? (\n <>\n <UserAvatar\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </>\n ) : mention.kind === \"group\" ? (\n <>\n <GroupAvatar\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n icon={<UsersIcon />}\n />\n <Group\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group\"\n >\n <GroupDescription\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group-description\"\n />\n </Group>\n </>\n ) : (\n assertNever(mention, \"Unhandled mention kind\")\n )}\n </ComposerPrimitive.SuggestionsListItem>\n );\n })}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={cn(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-composer-attachments\", className)} {...props}>\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || !showAttachments || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n {\n threadId,\n commentId,\n metadata,\n commentMetadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n blurOnSubmit = true,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<TM, CM>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUiConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const canComment = useHasPermissionAccess(roomId, \"comments\", \"write\");\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n metadata: commentMetadata,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n metadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata,\n commentMetadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n commentMetadata,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={cn(\"lb-root lb-composer lb-composer-form\", className)}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n blurOnSubmit={blurOnSubmit}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n props: ComposerProps<TM, CM> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkQA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AAEI;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACO;AACf;AAAA;AAIZ;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAIQ;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACK;AACb;AAAA;AAMhB;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAGM;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACU;AAClB;AAAA;AAKd;AAEA;AACE;AAEI;AAAuD;AAC7B;AAGhC;AAEA;AACE;AAEA;AACE;AAAmB;AAAlB;AACW;AACiB;AAE3B;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAyD;AAGzD;AAA0D;AAG1D;AAAoD;AAE1D;AAEA;AAAoC;AAEpC;AACE;AAIQ;AACE;AAAmB;AAAlB;AAEW;AACK;AAIX;AAAA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AACA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AAIA;AAAA;AAAC;AAAA;AACkB;AACP;AACO;AAAA;AACnB;AACA;AAAC;AAAA;AACkB;AACP;AAEV;AAAC;AAAA;AACkB;AACP;AAAA;AACZ;AAAA;AACF;AAG2C;AAAA;AAjClC;AAmCf;AAMZ;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACE;AAAC;AAAA;AACU;AACT;AAC2C;AAM3C;AAAA;AAGN;AAMA;AAAiC;AAC0C;AAEN;AAGjE;AAAC;AAAA;AACM;AACI;AACgB;AAAA;AAC3B;AAGJ;AAEA;AAIA;AACE;AAKF;AAEA;AACE;AAKF;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACE;AAAC;AAAA;AACkD;AAC7C;AACJ;AACe;AACK;AACpB;AACA;AAAA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AAIQ;AACE;AAAC;AAAA;AAEC;AACA;AAAA;AAFgB;AAGlB;AAMZ;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC3B;AAG5C;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAEI;AAAA;AAAmB;AAAlB;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AAAA;AACT;AAC+D;AAG3D;AACG;AACC;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEF;AAAC;AAAA;AACU;AACW;AACpB;AAAA;AACF;AAEE;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEJ;AACiC;AAMvB;AAAC;AAAA;AACgB;AACN;AACC;AACF;AACM;AACE;AAAA;AAM5B;AACF;AAKI;AAAgB;AACb;AAEP;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACG;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AAEA;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACJ;AACW;AACtB;AAED;AAAc;AACZ;AACc;AACJ;AACW;AACtB;AAED;AAAa;AACG;AACd;AACA;AACqB;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEI;AAAmB;AAAlB;AACmB;AAC6C;AACxD;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AACA;AAEA;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AAAA;AACF;AAAA;AAEJ;AAGN;;"}
1
+ {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DCM,\n DTM,\n GroupMentionData,\n Patchable,\n ThreadData,\n ThreadVisibility,\n} from \"@liveblocks/core\";\nimport { assertNever, MENTION_CHARACTER } from \"@liveblocks/core\";\nimport { useClient, useRoom } from \"@liveblocks/react\";\nimport {\n getUmbrellaStoreForClient,\n useCreateRoomComment,\n useCreateRoomThread,\n useEditRoomComment,\n useHasPermissionAccess,\n useLayoutEffect,\n useResolveMentionSuggestions,\n useSignal,\n} from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n ComponentType,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n PropsWithChildren,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport { createContext, forwardRef, useCallback, useMemo, useRef } from \"react\";\n\nimport { useLiveblocksUiConfig } from \"../config\";\nimport { FLOATING_ELEMENT_SIDE_OFFSET } from \"../constants\";\nimport { UsersIcon } from \"../icons\";\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { BoldIcon } from \"../icons/Bold\";\nimport { CodeIcon } from \"../icons/Code\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { ItalicIcon } from \"../icons/Italic\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport { StrikethroughIcon } from \"../icons/Strikethrough\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport {\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n} from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerMarkToggleProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport type { ComposerBodyMark } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { useIsGroupMentionMember } from \"../utils/use-group-mention\";\nimport { GroupAvatar, UserAvatar } from \"./Avatar\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport { Group } from \"./internal/Group\";\nimport { GroupDescription } from \"./internal/GroupDescription\";\nimport { ShortcutTooltip, Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n tooltipLabel?: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ninterface MarkToggleProps extends ComposerMarkToggleProps {\n icon?: ReactNode;\n shortcut?: string;\n}\n\nexport type ComposerCreateThreadProps<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = {\n threadId?: never;\n\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: TM;\n\n /**\n * The visibility of the thread to create.\n */\n visibility?: ThreadVisibility;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerCreateCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n\n commentId?: never;\n\n metadata?: never;\n\n visibility?: never;\n\n /**\n * The metadata of the comment to create.\n */\n commentMetadata?: CM;\n};\n\nexport type ComposerEditCommentProps<CM extends BaseMetadata> = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n\n metadata?: never;\n\n visibility?: never;\n\n /**\n * The metadata of the comment to edit.\n */\n commentMetadata?: Patchable<CM>;\n};\n\nexport type ComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = Omit<ComponentPropsWithoutRef<\"form\">, \"defaultValue\"> &\n (\n | ComposerCreateThreadProps<TM, CM>\n | ComposerCreateCommentProps<CM>\n | ComposerEditCommentProps<CM>\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether to show formatting controls (e.g. a floating toolbar with formatting toggles when selecting text)\n */\n showFormattingControls?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Whether to blur the composer editor when the composer is submitted.\n */\n blurOnSubmit?: boolean;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n\n /**\n * @internal\n */\n roomId?: string;\n };\n\ninterface ComposerEditorContainerProps extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showFormattingControls\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n | \"disabled\"\n> {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction useThreadVisibility(\n threadId: string | undefined\n): ThreadData[\"visibility\"] | undefined {\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n\n return useSignal(\n store.outputs.threads,\n useCallback(\n (threads) =>\n threadId !== undefined ? threads.get(threadId)?.visibility : undefined,\n [threadId]\n )\n );\n}\n\ninterface ComposerMentionProps extends ComposerEditorMentionProps {\n overrides?: ComposerProps[\"overrides\"];\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n tooltipLabel,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n icon={<MentionIcon />}\n {...props}\n />\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n tooltipLabel,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={tooltipLabel ?? label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<EmojiIcon />}\n {...props}\n />\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n tooltipLabel,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={tooltipLabel ?? label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={cn(\"lb-composer-editor-action\", className)}\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n icon={<AttachmentIcon />}\n {...props}\n />\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerUserMention({ mention }: ComposerMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-mention lb-composer-mention\">\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <User userId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerGroupMention({ mention }: ComposerMentionProps) {\n const isMember = useIsGroupMentionMember(mention as GroupMentionData);\n\n return (\n <ComposerPrimitive.Mention\n className=\"lb-mention lb-composer-mention\"\n data-self={isMember ? \"\" : undefined}\n >\n <span className=\"lb-mention-symbol\">{MENTION_CHARACTER}</span>\n <Group groupId={mention.id} />\n </ComposerPrimitive.Mention>\n );\n}\n\nexport function ComposerMention({ mention, ...props }: ComposerMentionProps) {\n switch (mention.kind) {\n case \"user\":\n return <ComposerUserMention mention={mention} {...props} />;\n\n case \"group\":\n return <ComposerGroupMention mention={mention} {...props} />;\n\n default:\n return assertNever(mention, \"Unhandled mention kind\");\n }\n}\n\nfunction ComposerMentionSuggestions({\n mentions,\n}: ComposerEditorMentionSuggestionsProps) {\n return mentions.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {mentions.map((mention) => {\n return (\n <ComposerPrimitive.SuggestionsListItem\n key={mention.id}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={mention.id}\n >\n {mention.kind === \"user\" ? (\n <>\n <UserAvatar\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={mention.id}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </>\n ) : mention.kind === \"group\" ? (\n <>\n <GroupAvatar\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-avatar\"\n icon={<UsersIcon />}\n />\n <Group\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group\"\n >\n <GroupDescription\n groupId={mention.id}\n className=\"lb-composer-mention-suggestion-group-description\"\n />\n </Group>\n </>\n ) : (\n assertNever(mention, \"Unhandled mention kind\")\n )}\n </ComposerPrimitive.SuggestionsListItem>\n );\n })}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction MarkToggle({\n mark,\n icon,\n shortcut,\n children,\n ...props\n}: MarkToggleProps) {\n const $ = useOverrides();\n const label = useMemo(() => {\n return $.COMPOSER_TOGGLE_MARK(mark);\n }, [$, mark]);\n\n return (\n <ShortcutTooltip\n content={label}\n shortcut={shortcut}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET + 2}\n >\n <ComposerPrimitive.MarkToggle mark={mark} asChild {...props}>\n <Button aria-label={label} variant=\"toolbar\" icon={icon}>\n {children}\n </Button>\n </ComposerPrimitive.MarkToggle>\n </ShortcutTooltip>\n );\n}\n\ntype MarkToggles = {\n [K in ComposerBodyMark]: ComponentType<PropsWithChildren>;\n};\n\nconst markToggles: MarkToggles = {\n bold: () => <MarkToggle mark=\"bold\" shortcut=\"Mod-B\" icon={<BoldIcon />} />,\n italic: () => (\n <MarkToggle mark=\"italic\" shortcut=\"Mod-I\" icon={<ItalicIcon />} />\n ),\n strikethrough: () => (\n <MarkToggle\n mark=\"strikethrough\"\n shortcut=\"Mod-Shift-S\"\n icon={<StrikethroughIcon />}\n />\n ),\n code: () => <MarkToggle mark=\"code\" shortcut=\"Mod-E\" icon={<CodeIcon />} />,\n};\n\nconst markTogglesList = Object.entries(markToggles).map(([mark, Toggle]) => (\n <Toggle key={mark} />\n));\n\nfunction ComposerFloatingToolbar() {\n return (\n <ComposerPrimitive.FloatingToolbar className=\"lb-root lb-portal lb-elevation lb-composer-floating-toolbar\">\n {markTogglesList}\n </ComposerPrimitive.FloatingToolbar>\n );\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n const { roomId } = useComposerEditorContext();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={cn(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n roomId={roomId}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div className={cn(\"lb-composer-attachments\", className)} {...props}>\n <div className=\"lb-attachments\">\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst editorRequiredComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n disabled,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const { isEmpty } = useComposer();\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const $ = useOverrides(overrides);\n const components = useMemo(() => {\n return {\n ...editorRequiredComponents,\n FloatingToolbar: showFormattingControls\n ? ComposerFloatingToolbar\n : undefined,\n };\n }, [showFormattingControls]);\n\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n disabled: disabled || !showAttachments || hasMaxAttachments,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={components}\n disabled={disabled}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={disabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n disabled={disabled}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n disabled={disabled}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip content={$.COMPOSER_SEND} shortcut=\"Enter\">\n <ComposerPrimitive.Submit asChild>\n <Button\n onPointerDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n icon={<SendIcon />}\n />\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport const ComposerRoomIdContext = createContext<string | null>(null);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n {\n threadId,\n commentId,\n metadata,\n visibility,\n commentMetadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n blurOnSubmit = true,\n showAttachments = true,\n showFormattingControls = true,\n showAttribution,\n roomId: _roomId,\n ...props\n }: ComposerProps<TM, CM>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const room = useRoom({ allowOutsideRoom: true });\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\n \"Composer must be a descendant of RoomProvider component\"\n );\n }\n\n const createThread = useCreateRoomThread(roomId);\n const createComment = useCreateRoomComment(roomId);\n const editComment = useEditRoomComment(roomId);\n const { preventUnsavedComposerChanges } = useLiveblocksUiConfig();\n const hasResolveMentionSuggestions =\n useResolveMentionSuggestions() !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n defaultCollapsed ?? false,\n controlledCollapsed,\n controlledOnCollapsedChange\n );\n\n const threadVisibility = useThreadVisibility(threadId);\n const composerVisibility = threadId\n ? threadVisibility\n : (visibility ?? \"public\");\n const canComment = useHasPermissionAccess(\n roomId,\n composerVisibility ? `comments:${composerVisibility}` : \"comments\",\n \"write\"\n );\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus, canComment]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current && canComment) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, canComment]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n event.stopPropagation();\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n metadata: commentMetadata,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n metadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata,\n visibility,\n commentMetadata: commentMetadata as CM | undefined,\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n commentMetadata,\n metadata,\n visibility,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleComposerSubmit}\n className={cn(\"lb-root lb-composer lb-composer-form\", className)}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled || !canComment}\n defaultAttachments={defaultAttachments}\n pasteFilesAsAttachments={showAttachments}\n preventUnsavedChanges={preventUnsavedComposerChanges}\n blurOnSubmit={blurOnSubmit}\n roomId={roomId}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n showFormattingControls={showFormattingControls}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n disabled={disabled}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM>(\n props: ComposerProps<TM, CM> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2QA;AAGE;AACA;AAEA;AAAO;AACS;AACd;AAEiE;AACtD;AACX;AAEJ;AAMA;AAA2C;AACzC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AAEI;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACO;AACf;AAAA;AAIZ;AAEA;AAAyC;AACvC;AACA;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAIQ;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACK;AACb;AAAA;AAMhB;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAGM;AAAC;AAAA;AACqD;AACrC;AACN;AACG;AACU;AAClB;AAAA;AAKd;AAEA;AACE;AAEI;AAAuD;AAC7B;AAGhC;AAEA;AACE;AAEA;AACE;AAAmB;AAAlB;AACW;AACiB;AAE3B;AAAuD;AAC3B;AAAA;AAAA;AAGlC;AAEO;AACL;AAAsB;AAElB;AAAyD;AAGzD;AAA0D;AAG1D;AAAoD;AAE1D;AAEA;AAAoC;AAEpC;AACE;AAIQ;AACE;AAAmB;AAAlB;AAEW;AACK;AAIX;AAAA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AACA;AAAC;AAAA;AACiB;AACN;AAAA;AACZ;AAIA;AAAA;AAAC;AAAA;AACkB;AACP;AACO;AAAA;AACnB;AACA;AAAC;AAAA;AACkB;AACP;AAEV;AAAC;AAAA;AACkB;AACP;AAAA;AACZ;AAAA;AACF;AAG2C;AAAA;AAjClC;AAmCf;AAMZ;AAEA;AAAoB;AAClB;AACA;AACA;AACA;AAEF;AACE;AACA;AACE;AAAkC;AAGpC;AACE;AAAC;AAAA;AACU;AACT;AAC2C;AAM3C;AAAA;AAGN;AAMA;AAAiC;AAC0C;AAEN;AAGjE;AAAC;AAAA;AACM;AACI;AACgB;AAAA;AAC3B;AAGJ;AAEA;AAIA;AACE;AAKF;AAEA;AACE;AAKF;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AACA;AAEA;AACE;AAA8B;AAGhC;AACE;AAAC;AAAA;AACkD;AAC7C;AACJ;AACe;AACK;AACpB;AACA;AAAA;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AAIQ;AACE;AAAC;AAAA;AAEC;AACA;AAAA;AAFgB;AAGlB;AAMZ;AAEA;AAA2D;AAChD;AACW;AAEtB;AAEA;AAAiC;AACb;AACO;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACE;AAAO;AACF;AAGC;AACN;AAGF;AAAuE;AAC3B;AAG5C;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAEI;AAAA;AAAmB;AAAlB;AACW;AACD;AACM;AACf;AACA;AACA;AACA;AACO;AAAA;AACT;AAC+D;AAG3D;AACG;AACC;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEF;AAAC;AAAA;AACU;AACW;AACpB;AAAA;AACF;AAEE;AAAC;AAAA;AACU;AACT;AAAA;AACF;AAEJ;AACiC;AAMvB;AAAC;AAAA;AACgB;AACN;AACC;AACF;AACM;AACE;AAAA;AAM5B;AACF;AAKI;AAAgB;AACb;AAEP;AAIR;AAEa;AAQN;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACG;AACO;AACzB;AACQ;AACL;AAIL;AAEA;AACA;AACE;AAAU;AACR;AACF;AAGF;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AACnB;AACpB;AACA;AAGF;AACA;AAGA;AAAmB;AACjB;AACwD;AACxD;AAGF;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACuC;AAGzC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAGlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC8B;AAGhC;AAA6B;AAEzB;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAY;AACV;AACA;AACc;AACJ;AACW;AACtB;AAED;AAAc;AACZ;AACc;AACJ;AACW;AACtB;AAED;AAAa;AACG;AACd;AACA;AACA;AACqB;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEI;AAAmB;AAAlB;AACmB;AAC6C;AACxD;AACH;AACC;AAC8B;AAC1B;AACD;AACe;AACvB;AACyB;AACF;AACvB;AACA;AAEA;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AACA;AAAA;AACF;AAAA;AAEJ;AAGN;;"}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ComponentPropsWithoutRef, ElementType, ReactNode, ComponentProps, FormEvent, RefAttributes, PropsWithChildren, MouseEvent } from 'react';
3
- import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, WithNavigation, AiAssistantMessage, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DTM, DCM, Patchable, CommentData, ThreadData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD } from '@liveblocks/core';
3
+ import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, WithNavigation, AiAssistantMessage, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DTM, DCM, ThreadVisibility, Patchable, CommentData, ThreadData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD } from '@liveblocks/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { Popover } from 'radix-ui';
6
6
 
@@ -1002,6 +1002,10 @@ type ComposerCreateThreadProps<TM extends BaseMetadata, CM extends BaseMetadata>
1002
1002
  * The metadata of the thread to create.
1003
1003
  */
1004
1004
  metadata?: TM;
1005
+ /**
1006
+ * The visibility of the thread to create.
1007
+ */
1008
+ visibility?: ThreadVisibility;
1005
1009
  /**
1006
1010
  * The metadata of the comment to create.
1007
1011
  */
@@ -1014,6 +1018,7 @@ type ComposerCreateCommentProps<CM extends BaseMetadata> = {
1014
1018
  threadId: string;
1015
1019
  commentId?: never;
1016
1020
  metadata?: never;
1021
+ visibility?: never;
1017
1022
  /**
1018
1023
  * The metadata of the comment to create.
1019
1024
  */
@@ -1029,6 +1034,7 @@ type ComposerEditCommentProps<CM extends BaseMetadata> = {
1029
1034
  */
1030
1035
  commentId: string;
1031
1036
  metadata?: never;
1037
+ visibility?: never;
1032
1038
  /**
1033
1039
  * The metadata of the comment to edit.
1034
1040
  */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ComponentPropsWithoutRef, ElementType, ReactNode, ComponentProps, FormEvent, RefAttributes, PropsWithChildren, MouseEvent } from 'react';
3
- import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, WithNavigation, AiAssistantMessage, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DTM, DCM, Patchable, CommentData, ThreadData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD } from '@liveblocks/core';
3
+ import { CommentAttachment, AiReasoningPart, AiRetrievalPart, Relax, WithNavigation, AiAssistantMessage, CopilotId, AiKnowledgeSource, AiOpaqueToolDefinition, AiToolTypePack, JsonObject, AiToolExecuteCallback, NoInfr, CommentBody, MentionData, BaseMetadata, DTM, DCM, ThreadVisibility, Patchable, CommentData, ThreadData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD } from '@liveblocks/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { Popover } from 'radix-ui';
6
6
 
@@ -1002,6 +1002,10 @@ type ComposerCreateThreadProps<TM extends BaseMetadata, CM extends BaseMetadata>
1002
1002
  * The metadata of the thread to create.
1003
1003
  */
1004
1004
  metadata?: TM;
1005
+ /**
1006
+ * The visibility of the thread to create.
1007
+ */
1008
+ visibility?: ThreadVisibility;
1005
1009
  /**
1006
1010
  * The metadata of the comment to create.
1007
1011
  */
@@ -1014,6 +1018,7 @@ type ComposerCreateCommentProps<CM extends BaseMetadata> = {
1014
1018
  threadId: string;
1015
1019
  commentId?: never;
1016
1020
  metadata?: never;
1021
+ visibility?: never;
1017
1022
  /**
1018
1023
  * The metadata of the comment to create.
1019
1024
  */
@@ -1029,6 +1034,7 @@ type ComposerEditCommentProps<CM extends BaseMetadata> = {
1029
1034
  */
1030
1035
  commentId: string;
1031
1036
  metadata?: never;
1037
+ visibility?: never;
1032
1038
  /**
1033
1039
  * The metadata of the comment to edit.
1034
1040
  */
package/dist/version.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const PKG_NAME = "@liveblocks/react-ui";
4
- const PKG_VERSION = typeof "3.21.0-exp2" === "string" && "3.21.0-exp2";
4
+ const PKG_VERSION = typeof "3.21.0-private1" === "string" && "3.21.0-private1";
5
5
  const PKG_FORMAT = typeof "cjs" === "string" && "cjs";
6
6
 
7
7
  exports.PKG_FORMAT = PKG_FORMAT;
@@ -1 +1 @@
1
- {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
1
+ {"version":3,"file":"version.cjs","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":";;AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,iBAAA,KAAgB,QAAY,IAAA,kBAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;;;"}
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const PKG_NAME = "@liveblocks/react-ui";
2
- const PKG_VERSION = typeof "3.21.0-exp2" === "string" && "3.21.0-exp2";
2
+ const PKG_VERSION = typeof "3.21.0-private1" === "string" && "3.21.0-private1";
3
3
  const PKG_FORMAT = typeof "esm" === "string" && "esm";
4
4
 
5
5
  export { PKG_FORMAT, PKG_NAME, PKG_VERSION };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,aAAA,KAAgB,QAAY,IAAA,cAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
1
+ {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["declare const __VERSION__: string;\ndeclare const ROLLUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/react-ui\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof ROLLUP_FORMAT === \"string\" && ROLLUP_FORMAT;\n"],"names":[],"mappings":"AAGO,MAAM,QAAW,GAAA,uBAAA;AACX,MAAA,WAAA,GAAc,OAAO,iBAAA,KAAgB,QAAY,IAAA,kBAAA;AACjD,MAAA,UAAA,GAAa,OAAO,KAAA,KAAkB,QAAY,IAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/react-ui",
3
- "version": "3.21.0-exp2",
3
+ "version": "3.21.0-private1",
4
4
  "description": "A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Liveblocks Inc.",
@@ -73,9 +73,9 @@
73
73
  "slate-history": "^0.110.3",
74
74
  "slate-hyperscript": "^0.100.0",
75
75
  "slate-react": "^0.110.3",
76
- "@liveblocks/client": "3.21.0-exp2",
77
- "@liveblocks/core": "3.21.0-exp2",
78
- "@liveblocks/react": "3.21.0-exp2"
76
+ "@liveblocks/core": "3.21.0-private1",
77
+ "@liveblocks/client": "3.21.0-private1",
78
+ "@liveblocks/react": "3.21.0-private1"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "@types/react": "^18 || ^19",