@dxos/react-ui-thread 0.5.9-main.44fe51d → 0.5.9-main.4ed8273

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.
@@ -68,12 +68,13 @@ var ThreadFooter = /* @__PURE__ */ forwardRef(({ activity, classNames, children,
68
68
  });
69
69
 
70
70
  // packages/ui/react-ui-thread/src/Message/Message.tsx
71
+ import { X } from "@phosphor-icons/react";
71
72
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
72
73
  import React2, { forwardRef as forwardRef2, useMemo } from "react";
73
- import { Avatar, useTranslation as useTranslation2 } from "@dxos/react-ui";
74
+ import { Avatar, Button, useTranslation as useTranslation2 } from "@dxos/react-ui";
74
75
  import { keymap, listener, TextEditor } from "@dxos/react-ui-editor";
75
- import { focusRing, mx as mx2 } from "@dxos/react-ui-theme";
76
- import { hexToEmoji, hexToHue, isNotFalsy } from "@dxos/util";
76
+ import { focusRing, hoverableControlItem as hoverableControlItem2, hoverableControls, hoverableFocusedWithinControls as hoverableFocusedWithinControls2, mx as mx2 } from "@dxos/react-ui-theme";
77
+ import { safeParseJson, hexToEmoji, hexToHue, isNotFalsy } from "@dxos/util";
77
78
  var avatarSize = 7;
78
79
  var MessageMeta = /* @__PURE__ */ forwardRef2(({ authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps }, forwardedRef) => {
79
80
  return /* @__PURE__ */ React2.createElement(Avatar.Root, {
@@ -100,20 +101,41 @@ var MessageMeta = /* @__PURE__ */ forwardRef2(({ authorImgSrc, authorId, authorN
100
101
  className: "plb-1 min-is-0"
101
102
  }, children)));
102
103
  });
103
- var Message = ({ timestamp, children, ...messageMeta }) => {
104
+ var DefaultMessageBlock = ({ block, onBlockDelete }) => {
105
+ const contentWidth = onBlockDelete ? "col-span-2" : "col-span-3";
106
+ return /* @__PURE__ */ React2.createElement("div", {
107
+ role: "none",
108
+ className: mx2("grid grid-cols-subgrid col-span-3", hoverableControls, hoverableFocusedWithinControls2)
109
+ }, block.data ? /* @__PURE__ */ React2.createElement("pre", {
110
+ className: mx2("font-mono max-is-full overflow-x-auto", contentWidth)
111
+ }, /* @__PURE__ */ React2.createElement("code", null, JSON.stringify(safeParseJson(block.data), void 0, 2))) : /* @__PURE__ */ React2.createElement("p", {
112
+ className: contentWidth
113
+ }, block.text ?? ""), onBlockDelete && /* @__PURE__ */ React2.createElement(Button, {
114
+ variant: "ghost",
115
+ classNames: [
116
+ "p-1.5 min-bs-0 mie-1 items-start transition-opacity",
117
+ hoverableControlItem2
118
+ ],
119
+ onClick: () => onBlockDelete()
120
+ }, /* @__PURE__ */ React2.createElement(X, null)));
121
+ };
122
+ var Message = (props) => {
123
+ const { id, authorName, onDelete, blocks, MessageBlockComponent = DefaultMessageBlock, ...metaProps } = props;
104
124
  const { t, dtLocale } = useTranslation2(translationKey);
105
- const dt = timestamp ? new Date(timestamp) : void 0;
125
+ const firstBlock = blocks[0];
126
+ const dt = firstBlock.timestamp ? new Date(firstBlock.timestamp) : void 0;
106
127
  return /* @__PURE__ */ React2.createElement(MessageMeta, {
107
- ...messageMeta,
128
+ ...metaProps,
129
+ id,
108
130
  continues: true
109
131
  }, /* @__PURE__ */ React2.createElement("p", {
110
132
  className: "grid grid-cols-[1fr_max-content] gap-2 pie-2"
111
133
  }, /* @__PURE__ */ React2.createElement(Avatar.Label, {
112
134
  classNames: [
113
135
  "truncate text-sm font-medium",
114
- !messageMeta.authorName && "fg-description"
136
+ !authorName && "fg-description"
115
137
  ]
116
- }, messageMeta.authorName ?? t("anonymous label")), /* @__PURE__ */ React2.createElement("time", {
138
+ }, authorName ?? t("anonymous label")), /* @__PURE__ */ React2.createElement("time", {
117
139
  className: "fg-description text-xs pbs-0.5",
118
140
  dateTime: dt?.toISOString()
119
141
  }, dt ? formatDistanceToNow(dt, {
@@ -122,7 +144,12 @@ var Message = ({ timestamp, children, ...messageMeta }) => {
122
144
  }) : "")), /* @__PURE__ */ React2.createElement("div", {
123
145
  role: "none",
124
146
  className: "grid gap-y-1 grid-cols-[min-content_1fr_min-content]"
125
- }, children));
147
+ }, blocks.map((block, i) => /* @__PURE__ */ React2.createElement(MessageBlockComponent, {
148
+ key: i,
149
+ block,
150
+ authorId: metaProps.authorId,
151
+ onBlockDelete: onDelete && (() => onDelete(id, i))
152
+ }))));
126
153
  };
127
154
  var keyBindings = ({ onSend, onClear }) => [
128
155
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/translations.ts", "../../../src/Thread/Thread.tsx", "../../../src/Message/Message.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport const translationKey = 'thread';\n\nexport default [\n {\n 'en-US': {\n [translationKey]: {\n 'plugin name': 'Threads',\n 'thread title placeholder': 'New thread',\n 'thread title label': 'Title',\n 'delete thread label': 'Delete',\n 'create thread label': 'Create thread',\n 'message placeholder': 'Enter message...',\n 'comment placeholder': 'Enter comment...',\n 'settings standalone label': 'Enable standalone thread creation',\n 'anonymous label': 'Anonymous',\n 'enter to send message': 'Enter to add ⏎',\n },\n },\n },\n];\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { ArrowBendLeftDown, Spinner } from '@phosphor-icons/react';\nimport React, { type ComponentProps, type ComponentPropsWithRef, forwardRef } from 'react';\n\nimport { type ThemedClassName, useTranslation } from '@dxos/react-ui';\nimport { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';\n\nimport { translationKey } from '../translations';\nimport type { ThreadEntity } from '../types';\n\nexport type ThreadProps = ThemedClassName<ComponentPropsWithRef<'div'>> &\n ThreadEntity & { current?: boolean | ComponentProps<'div'>['aria-current'] };\n\nexport const threadLayout = 'is-full place-self-start grid grid-cols-[var(--rail-size)_1fr]';\n\nexport const Thread = forwardRef<HTMLDivElement, ThreadProps>(\n ({ current, children, classNames, ...props }, forwardedRef) => {\n return (\n <div\n role='group'\n data-testid='thread'\n {...(current && { 'aria-current': typeof current === 'string' ? current : 'location' })}\n {...props}\n className={mx(\n threadLayout,\n hoverableFocusedWithinControls,\n 'bg-[var(--surface-bg)] border-[color:var(--surface-separator)] border-bs border-be plb-1.5 attention attention-within attention-current [--controls-opacity:0]',\n classNames,\n )}\n ref={forwardedRef}\n >\n {children}\n </div>\n );\n },\n);\n\nexport type ThreadHeadingProps = ThemedClassName<ComponentPropsWithRef<'div'>> & { detached?: boolean };\n\nexport const ThreadHeading = forwardRef<HTMLParagraphElement, ThreadHeadingProps>(\n ({ classNames, children, detached, ...props }, forwardedRef) => {\n return (\n <>\n <div role='none' className='grid place-items-end pie-3.5 fg-description'>\n <ArrowBendLeftDown />\n </div>\n <p\n role='heading'\n data-testid='thread.heading'\n {...props}\n className={mx(\n 'fg-description font-medium truncate before:content-[open-quote] after:content-[close-quote]',\n detached && 'line-through decoration-1',\n )}\n ref={forwardedRef}\n >\n {children}\n </p>\n </>\n );\n },\n);\n\nexport type ThreadFooterProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {\n activity?: boolean;\n};\n\nexport const ThreadFooter = forwardRef<HTMLDivElement, ThreadFooterProps>(\n ({ activity, classNames, children, ...props }, forwardedRef) => {\n const { t } = useTranslation(translationKey);\n return (\n <div\n {...props}\n className={mx(\n 'col-start-2 grid grid-cols-[min-content_1fr_max-content] text-xs fg-description pb-2',\n classNames,\n )}\n ref={forwardedRef}\n >\n <Spinner\n weight='bold'\n data-visible={activity ? 'show' : 'hide'}\n className='is-6 bs-4 invisible data-[visible=show]:visible animate-spin-slow'\n />\n <span className='truncate min-is-0' aria-live='polite'>\n {activity ? children : null}\n </span>\n <span className={mx('text-end pie-1', hoverableControlItem)}>{t('enter to send message')}</span>\n </div>\n );\n },\n);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { formatDistanceToNow } from 'date-fns/formatDistanceToNow';\nimport React, { type ComponentPropsWithRef, forwardRef, useMemo, type PropsWithChildren } from 'react';\n\n// TODO(burdon): Remove dep.\nimport { Avatar, type ThemedClassName, useTranslation } from '@dxos/react-ui';\nimport { type TextEditorProps, keymap, listener, TextEditor } from '@dxos/react-ui-editor';\nimport { focusRing, mx } from '@dxos/react-ui-theme';\nimport { hexToEmoji, hexToHue, isNotFalsy } from '@dxos/util';\n\nimport { translationKey } from '../translations';\nimport { type MessageMetadata } from '../types';\n\nconst avatarSize = 7;\n\nexport type MessageMetaProps = ThemedClassName<ComponentPropsWithRef<'div'>> &\n MessageMetadata &\n Partial<{ continues: boolean }>;\n\nexport const MessageMeta = forwardRef<HTMLDivElement, MessageMetaProps>(\n (\n { authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps },\n forwardedRef,\n ) => {\n return (\n <Avatar.Root size={avatarSize} hue={authorAvatarProps?.hue || hexToHue(authorId ?? '0')}>\n <div\n role='none'\n data-testid='thread.message'\n {...rootProps}\n className={mx('grid grid-cols-subgrid col-span-2', classNames)}\n ref={forwardedRef}\n >\n <div role='none' className={mx('flex flex-col items-center gap-2 plb-2')}>\n <Avatar.Frame>\n <Avatar.Fallback text={authorAvatarProps?.emoji || hexToEmoji(authorId ?? '0')} />\n {authorImgSrc && <Avatar.Image href={authorImgSrc} />}\n </Avatar.Frame>\n {continues && <div role='none' className='is-px grow surface-separator' />}\n </div>\n <div role='none' className='plb-1 min-is-0'>\n {children}\n </div>\n </div>\n </Avatar.Root>\n );\n },\n);\n\nexport type MessageProps = PropsWithChildren<MessageMetadata>;\n\nexport const Message = ({ timestamp, children, ...messageMeta }: MessageProps) => {\n const { t, dtLocale } = useTranslation(translationKey);\n const dt = timestamp ? new Date(timestamp) : undefined;\n\n return (\n <MessageMeta {...messageMeta} continues>\n <p className='grid grid-cols-[1fr_max-content] gap-2 pie-2'>\n <Avatar.Label classNames={['truncate text-sm font-medium', !messageMeta.authorName && 'fg-description']}>\n {messageMeta.authorName ?? t('anonymous label')}\n </Avatar.Label>\n <time className='fg-description text-xs pbs-0.5' dateTime={dt?.toISOString()}>\n {dt ? formatDistanceToNow(dt, { locale: dtLocale, addSuffix: true }) : ''}\n </time>\n </p>\n <div role='none' className='grid gap-y-1 grid-cols-[min-content_1fr_min-content]'>\n {children}\n </div>\n </MessageMeta>\n );\n};\n\nexport type MessageTextboxProps = {\n disabled?: boolean;\n onSend?: () => void;\n onClear?: () => void;\n onEditorFocus?: () => void;\n} & MessageMetadata &\n TextEditorProps;\n\nconst keyBindings = ({ onSend, onClear }: Pick<MessageTextboxProps, 'onSend' | 'onClear'>) => [\n {\n key: 'Enter',\n run: () => {\n if (onSend) {\n onSend();\n return true;\n } else {\n return false;\n }\n },\n },\n {\n // TODO(burdon): Other key bindings.\n key: 'Meta+Backspace',\n run: () => {\n if (onClear) {\n onClear();\n return true;\n } else {\n return false;\n }\n },\n },\n];\n\nexport const MessageTextbox = ({\n id,\n onSend,\n onClear,\n onEditorFocus,\n authorId,\n authorName,\n authorImgSrc,\n authorAvatarProps,\n disabled,\n extensions: _extensions,\n ...editorProps\n}: MessageTextboxProps) => {\n const extensions = useMemo<TextEditorProps['extensions']>(() => {\n return [\n keymap.of(keyBindings({ onSend, onClear })),\n listener({\n onFocus: (focusing) => {\n if (focusing) {\n onEditorFocus?.();\n }\n },\n }),\n _extensions,\n ].filter(isNotFalsy);\n // TODO(wittjosiah): Should probably include callbacks in the dependency array.\n }, [_extensions]);\n\n return (\n <MessageMeta {...{ id, authorId, authorName, authorImgSrc, authorAvatarProps }} continues={false}>\n <TextEditor\n {...editorProps}\n extensions={extensions}\n className={mx('plb-0.5 mie-1 rounded-sm', focusRing, disabled && 'opacity-50')}\n />\n </MessageMeta>\n );\n};\n"],
5
- "mappings": "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",
6
- "names": ["translationKey", "ArrowBendLeftDown", "Spinner", "React", "forwardRef", "useTranslation", "hoverableControlItem", "hoverableFocusedWithinControls", "mx", "threadLayout", "Thread", "forwardRef", "current", "children", "classNames", "props", "forwardedRef", "div", "role", "data-testid", "className", "mx", "hoverableFocusedWithinControls", "ref", "ThreadHeading", "detached", "ArrowBendLeftDown", "p", "ThreadFooter", "activity", "t", "useTranslation", "translationKey", "Spinner", "weight", "data-visible", "span", "aria-live", "hoverableControlItem", "formatDistanceToNow", "React", "forwardRef", "useMemo", "Avatar", "useTranslation", "keymap", "listener", "TextEditor", "focusRing", "mx", "hexToEmoji", "hexToHue", "isNotFalsy", "avatarSize", "MessageMeta", "forwardRef", "authorImgSrc", "authorId", "authorName", "authorAvatarProps", "continues", "children", "classNames", "rootProps", "forwardedRef", "React", "Avatar", "Root", "size", "hue", "hexToHue", "div", "role", "data-testid", "className", "mx", "ref", "Frame", "Fallback", "text", "emoji", "hexToEmoji", "Image", "href", "Message", "timestamp", "messageMeta", "t", "dtLocale", "useTranslation", "translationKey", "dt", "Date", "undefined", "p", "Label", "time", "dateTime", "toISOString", "formatDistanceToNow", "locale", "addSuffix", "keyBindings", "onSend", "onClear", "key", "run", "MessageTextbox", "id", "onEditorFocus", "disabled", "extensions", "_extensions", "editorProps", "useMemo", "keymap", "of", "listener", "onFocus", "focusing", "filter", "isNotFalsy", "TextEditor", "focusRing"]
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport const translationKey = 'thread';\n\nexport default [\n {\n 'en-US': {\n [translationKey]: {\n 'plugin name': 'Threads',\n 'thread title placeholder': 'New thread',\n 'thread title label': 'Title',\n 'delete thread label': 'Delete',\n 'create thread label': 'Create thread',\n 'message placeholder': 'Enter message...',\n 'comment placeholder': 'Enter comment...',\n 'settings standalone label': 'Enable standalone thread creation',\n 'anonymous label': 'Anonymous',\n 'enter to send message': 'Enter to add ⏎',\n },\n },\n },\n];\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { ArrowBendLeftDown, Spinner } from '@phosphor-icons/react';\nimport React, { type ComponentProps, type ComponentPropsWithRef, forwardRef } from 'react';\n\nimport { type ThemedClassName, useTranslation } from '@dxos/react-ui';\nimport { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';\n\nimport { translationKey } from '../translations';\nimport type { ThreadEntity } from '../types';\n\nexport type ThreadProps = ThemedClassName<ComponentPropsWithRef<'div'>> &\n ThreadEntity & { current?: boolean | ComponentProps<'div'>['aria-current'] };\n\nexport const threadLayout = 'is-full place-self-start grid grid-cols-[var(--rail-size)_1fr]';\n\nexport const Thread = forwardRef<HTMLDivElement, ThreadProps>(\n ({ current, children, classNames, ...props }, forwardedRef) => {\n return (\n <div\n role='group'\n data-testid='thread'\n {...(current && { 'aria-current': typeof current === 'string' ? current : 'location' })}\n {...props}\n className={mx(\n threadLayout,\n hoverableFocusedWithinControls,\n 'bg-[var(--surface-bg)] border-[color:var(--surface-separator)] border-bs border-be plb-1.5 attention attention-within attention-current [--controls-opacity:0]',\n classNames,\n )}\n ref={forwardedRef}\n >\n {children}\n </div>\n );\n },\n);\n\nexport type ThreadHeadingProps = ThemedClassName<ComponentPropsWithRef<'div'>> & { detached?: boolean };\n\nexport const ThreadHeading = forwardRef<HTMLParagraphElement, ThreadHeadingProps>(\n ({ classNames, children, detached, ...props }, forwardedRef) => {\n return (\n <>\n <div role='none' className='grid place-items-end pie-3.5 fg-description'>\n <ArrowBendLeftDown />\n </div>\n <p\n role='heading'\n data-testid='thread.heading'\n {...props}\n className={mx(\n 'fg-description font-medium truncate before:content-[open-quote] after:content-[close-quote]',\n detached && 'line-through decoration-1',\n )}\n ref={forwardedRef}\n >\n {children}\n </p>\n </>\n );\n },\n);\n\nexport type ThreadFooterProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {\n activity?: boolean;\n};\n\nexport const ThreadFooter = forwardRef<HTMLDivElement, ThreadFooterProps>(\n ({ activity, classNames, children, ...props }, forwardedRef) => {\n const { t } = useTranslation(translationKey);\n return (\n <div\n {...props}\n className={mx(\n 'col-start-2 grid grid-cols-[min-content_1fr_max-content] text-xs fg-description pb-2',\n classNames,\n )}\n ref={forwardedRef}\n >\n <Spinner\n weight='bold'\n data-visible={activity ? 'show' : 'hide'}\n className='is-6 bs-4 invisible data-[visible=show]:visible animate-spin-slow'\n />\n <span className='truncate min-is-0' aria-live='polite'>\n {activity ? children : null}\n </span>\n <span className={mx('text-end pie-1', hoverableControlItem)}>{t('enter to send message')}</span>\n </div>\n );\n },\n);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { X } from '@phosphor-icons/react';\nimport { formatDistanceToNow } from 'date-fns/formatDistanceToNow';\nimport React, { type ComponentPropsWithRef, type FC, forwardRef, useMemo } from 'react';\n\n// TODO(burdon): Remove dep.\nimport { Avatar, Button, type ThemedClassName, useTranslation } from '@dxos/react-ui';\nimport { type TextEditorProps, keymap, listener, TextEditor } from '@dxos/react-ui-editor';\nimport {\n focusRing,\n hoverableControlItem,\n hoverableControls,\n hoverableFocusedWithinControls,\n mx,\n} from '@dxos/react-ui-theme';\nimport { safeParseJson, hexToEmoji, hexToHue, isNotFalsy } from '@dxos/util';\n\nimport { translationKey } from '../translations';\nimport { type MessageEntity, type MessageEntityBlock, type MessageMetadata } from '../types';\n\nconst avatarSize = 7;\n\nexport type MessageMetaProps = ThemedClassName<ComponentPropsWithRef<'div'>> &\n MessageMetadata &\n Partial<{ continues: boolean }>;\n\nexport const MessageMeta = forwardRef<HTMLDivElement, MessageMetaProps>(\n (\n { authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps },\n forwardedRef,\n ) => {\n return (\n <Avatar.Root size={avatarSize} hue={authorAvatarProps?.hue || hexToHue(authorId ?? '0')}>\n <div\n role='none'\n data-testid='thread.message'\n {...rootProps}\n className={mx('grid grid-cols-subgrid col-span-2', classNames)}\n ref={forwardedRef}\n >\n <div role='none' className={mx('flex flex-col items-center gap-2 plb-2')}>\n <Avatar.Frame>\n <Avatar.Fallback text={authorAvatarProps?.emoji || hexToEmoji(authorId ?? '0')} />\n {authorImgSrc && <Avatar.Image href={authorImgSrc} />}\n </Avatar.Frame>\n {continues && <div role='none' className='is-px grow surface-separator' />}\n </div>\n <div role='none' className='plb-1 min-is-0'>\n {children}\n </div>\n </div>\n </Avatar.Root>\n );\n },\n);\n\nexport type MessageBlockProps<BlockValue> = {\n block: MessageEntityBlock<BlockValue>;\n authorId?: string;\n onBlockDelete?: () => void;\n};\n\nconst DefaultMessageBlock = ({ block, onBlockDelete }: MessageBlockProps<{ data?: any; text?: string }>) => {\n const contentWidth = onBlockDelete ? 'col-span-2' : 'col-span-3';\n return (\n <div\n role='none'\n className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}\n >\n {block.data ? (\n <pre className={mx('font-mono max-is-full overflow-x-auto', contentWidth)}>\n <code>{JSON.stringify(safeParseJson(block.data), undefined, 2)}</code>\n </pre>\n ) : (\n <p className={contentWidth}>{block.text ?? ''}</p>\n )}\n {onBlockDelete && (\n <Button\n variant='ghost'\n classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}\n onClick={() => onBlockDelete()}\n >\n <X />\n </Button>\n )}\n </div>\n );\n};\n\nexport type MessageProps<BlockValue> = MessageEntity<BlockValue> & {\n onDelete?: (messageId: string, blockIndex: number) => void;\n MessageBlockComponent?: FC<MessageBlockProps<BlockValue>>;\n};\n\nexport const Message = <BlockValue,>(props: MessageProps<BlockValue>) => {\n const { id, authorName, onDelete, blocks, MessageBlockComponent = DefaultMessageBlock, ...metaProps } = props;\n const { t, dtLocale } = useTranslation(translationKey);\n const firstBlock = blocks[0];\n const dt = firstBlock.timestamp ? new Date(firstBlock.timestamp) : undefined;\n\n return (\n <MessageMeta {...metaProps} id={id} continues>\n <p className='grid grid-cols-[1fr_max-content] gap-2 pie-2'>\n <Avatar.Label classNames={['truncate text-sm font-medium', !authorName && 'fg-description']}>\n {authorName ?? t('anonymous label')}\n </Avatar.Label>\n <time className='fg-description text-xs pbs-0.5' dateTime={dt?.toISOString()}>\n {dt ? formatDistanceToNow(dt, { locale: dtLocale, addSuffix: true }) : ''}\n </time>\n </p>\n <div role='none' className='grid gap-y-1 grid-cols-[min-content_1fr_min-content]'>\n {blocks.map((block, i) => (\n <MessageBlockComponent\n key={i}\n block={block}\n authorId={metaProps.authorId}\n onBlockDelete={onDelete && (() => onDelete(id, i))}\n />\n ))}\n </div>\n </MessageMeta>\n );\n};\n\nexport type MessageTextboxProps = {\n disabled?: boolean;\n onSend?: () => void;\n onClear?: () => void;\n onEditorFocus?: () => void;\n} & MessageMetadata &\n TextEditorProps;\n\nconst keyBindings = ({ onSend, onClear }: Pick<MessageTextboxProps, 'onSend' | 'onClear'>) => [\n {\n key: 'Enter',\n run: () => {\n if (onSend) {\n onSend();\n return true;\n } else {\n return false;\n }\n },\n },\n {\n // TODO(burdon): Other key bindings.\n key: 'Meta+Backspace',\n run: () => {\n if (onClear) {\n onClear();\n return true;\n } else {\n return false;\n }\n },\n },\n];\n\nexport const MessageTextbox = ({\n id,\n onSend,\n onClear,\n onEditorFocus,\n authorId,\n authorName,\n authorImgSrc,\n authorAvatarProps,\n disabled,\n extensions: _extensions,\n ...editorProps\n}: MessageTextboxProps) => {\n const extensions = useMemo<TextEditorProps['extensions']>(() => {\n return [\n keymap.of(keyBindings({ onSend, onClear })),\n listener({\n onFocus: (focusing) => {\n if (focusing) {\n onEditorFocus?.();\n }\n },\n }),\n _extensions,\n ].filter(isNotFalsy);\n // TODO(wittjosiah): Should probably include callbacks in the dependency array.\n }, [_extensions]);\n\n return (\n <MessageMeta {...{ id, authorId, authorName, authorImgSrc, authorAvatarProps }} continues={false}>\n <TextEditor\n {...editorProps}\n extensions={extensions}\n className={mx('plb-0.5 mie-1 rounded-sm', focusRing, disabled && 'opacity-50')}\n />\n </MessageMeta>\n );\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["translationKey", "ArrowBendLeftDown", "Spinner", "React", "forwardRef", "useTranslation", "hoverableControlItem", "hoverableFocusedWithinControls", "mx", "threadLayout", "Thread", "forwardRef", "current", "children", "classNames", "props", "forwardedRef", "div", "role", "data-testid", "className", "mx", "hoverableFocusedWithinControls", "ref", "ThreadHeading", "detached", "ArrowBendLeftDown", "p", "ThreadFooter", "activity", "t", "useTranslation", "translationKey", "Spinner", "weight", "data-visible", "span", "aria-live", "hoverableControlItem", "X", "formatDistanceToNow", "React", "forwardRef", "useMemo", "Avatar", "Button", "useTranslation", "keymap", "listener", "TextEditor", "focusRing", "hoverableControlItem", "hoverableControls", "hoverableFocusedWithinControls", "mx", "safeParseJson", "hexToEmoji", "hexToHue", "isNotFalsy", "avatarSize", "MessageMeta", "forwardRef", "authorImgSrc", "authorId", "authorName", "authorAvatarProps", "continues", "children", "classNames", "rootProps", "forwardedRef", "React", "Avatar", "Root", "size", "hue", "hexToHue", "div", "role", "data-testid", "className", "mx", "ref", "Frame", "Fallback", "text", "emoji", "hexToEmoji", "Image", "href", "DefaultMessageBlock", "block", "onBlockDelete", "contentWidth", "hoverableControls", "hoverableFocusedWithinControls", "data", "pre", "code", "JSON", "stringify", "safeParseJson", "undefined", "p", "Button", "variant", "hoverableControlItem", "onClick", "X", "Message", "props", "id", "onDelete", "blocks", "MessageBlockComponent", "metaProps", "t", "dtLocale", "useTranslation", "translationKey", "firstBlock", "dt", "timestamp", "Date", "Label", "time", "dateTime", "toISOString", "formatDistanceToNow", "locale", "addSuffix", "map", "i", "key", "keyBindings", "onSend", "onClear", "run", "MessageTextbox", "onEditorFocus", "disabled", "extensions", "_extensions", "editorProps", "useMemo", "keymap", "of", "listener", "onFocus", "focusing", "filter", "isNotFalsy", "TextEditor", "focusRing"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/ui/react-ui-thread/src/translations.ts":{"bytes":2337,"imports":[],"format":"esm"},"packages/ui/react-ui-thread/src/Thread/Thread.tsx":{"bytes":9488,"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"../translations"}],"format":"esm"},"packages/ui/react-ui-thread/src/Thread/index.ts":{"bytes":500,"imports":[{"path":"packages/ui/react-ui-thread/src/Thread/Thread.tsx","kind":"import-statement","original":"./Thread"}],"format":"esm"},"packages/ui/react-ui-thread/src/Message/Message.tsx":{"bytes":15102,"imports":[{"path":"date-fns/formatDistanceToNow","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-editor","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"../translations"}],"format":"esm"},"packages/ui/react-ui-thread/src/Message/index.ts":{"bytes":506,"imports":[{"path":"packages/ui/react-ui-thread/src/Message/Message.tsx","kind":"import-statement","original":"./Message"}],"format":"esm"},"packages/ui/react-ui-thread/src/types.ts":{"bytes":903,"imports":[],"format":"esm"},"packages/ui/react-ui-thread/src/index.ts":{"bytes":913,"imports":[{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"./translations"},{"path":"packages/ui/react-ui-thread/src/Thread/index.ts","kind":"import-statement","original":"./Thread"},{"path":"packages/ui/react-ui-thread/src/Message/index.ts","kind":"import-statement","original":"./Message"},{"path":"packages/ui/react-ui-thread/src/types.ts","kind":"import-statement","original":"./types"}],"format":"esm"}},"outputs":{"packages/ui/react-ui-thread/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":13913},"packages/ui/react-ui-thread/dist/lib/browser/index.mjs":{"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"date-fns/formatDistanceToNow","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-editor","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Message","MessageMeta","MessageTextbox","Thread","ThreadFooter","ThreadHeading","threadLayout","translations"],"entryPoint":"packages/ui/react-ui-thread/src/index.ts","inputs":{"packages/ui/react-ui-thread/src/translations.ts":{"bytesInOutput":609},"packages/ui/react-ui-thread/src/Thread/Thread.tsx":{"bytesInOutput":2492},"packages/ui/react-ui-thread/src/Thread/index.ts":{"bytesInOutput":0},"packages/ui/react-ui-thread/src/index.ts":{"bytesInOutput":0},"packages/ui/react-ui-thread/src/Message/Message.tsx":{"bytesInOutput":3877},"packages/ui/react-ui-thread/src/Message/index.ts":{"bytesInOutput":0}},"bytes":7328}}}
1
+ {"inputs":{"packages/ui/react-ui-thread/src/translations.ts":{"bytes":2337,"imports":[],"format":"esm"},"packages/ui/react-ui-thread/src/Thread/Thread.tsx":{"bytes":9488,"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"../translations"}],"format":"esm"},"packages/ui/react-ui-thread/src/Thread/index.ts":{"bytes":500,"imports":[{"path":"packages/ui/react-ui-thread/src/Thread/Thread.tsx","kind":"import-statement","original":"./Thread"}],"format":"esm"},"packages/ui/react-ui-thread/src/Message/Message.tsx":{"bytes":20403,"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"date-fns/formatDistanceToNow","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-editor","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"../translations"}],"format":"esm"},"packages/ui/react-ui-thread/src/Message/index.ts":{"bytes":506,"imports":[{"path":"packages/ui/react-ui-thread/src/Message/Message.tsx","kind":"import-statement","original":"./Message"}],"format":"esm"},"packages/ui/react-ui-thread/src/types.ts":{"bytes":1143,"imports":[],"format":"esm"},"packages/ui/react-ui-thread/src/index.ts":{"bytes":913,"imports":[{"path":"packages/ui/react-ui-thread/src/translations.ts","kind":"import-statement","original":"./translations"},{"path":"packages/ui/react-ui-thread/src/Thread/index.ts","kind":"import-statement","original":"./Thread"},{"path":"packages/ui/react-ui-thread/src/Message/index.ts","kind":"import-statement","original":"./Message"},{"path":"packages/ui/react-ui-thread/src/types.ts","kind":"import-statement","original":"./types"}],"format":"esm"}},"outputs":{"packages/ui/react-ui-thread/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16946},"packages/ui/react-ui-thread/dist/lib/browser/index.mjs":{"imports":[{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@phosphor-icons/react","kind":"import-statement","external":true},{"path":"date-fns/formatDistanceToNow","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-editor","kind":"import-statement","external":true},{"path":"@dxos/react-ui-theme","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["Message","MessageMeta","MessageTextbox","Thread","ThreadFooter","ThreadHeading","threadLayout","translations"],"entryPoint":"packages/ui/react-ui-thread/src/index.ts","inputs":{"packages/ui/react-ui-thread/src/translations.ts":{"bytesInOutput":609},"packages/ui/react-ui-thread/src/Thread/Thread.tsx":{"bytesInOutput":2492},"packages/ui/react-ui-thread/src/Thread/index.ts":{"bytesInOutput":0},"packages/ui/react-ui-thread/src/index.ts":{"bytesInOutput":0},"packages/ui/react-ui-thread/src/Message/Message.tsx":{"bytesInOutput":5333},"packages/ui/react-ui-thread/src/Message/index.ts":{"bytesInOutput":0}},"bytes":8784}}}
@@ -1,13 +1,21 @@
1
- import React, { type ComponentPropsWithRef, type PropsWithChildren } from 'react';
1
+ import React, { type ComponentPropsWithRef, type FC } from 'react';
2
2
  import { type ThemedClassName } from '@dxos/react-ui';
3
3
  import { type TextEditorProps } from '@dxos/react-ui-editor';
4
- import { type MessageMetadata } from '../types';
4
+ import { type MessageEntity, type MessageEntityBlock, type MessageMetadata } from '../types';
5
5
  export type MessageMetaProps = ThemedClassName<ComponentPropsWithRef<'div'>> & MessageMetadata & Partial<{
6
6
  continues: boolean;
7
7
  }>;
8
8
  export declare const MessageMeta: React.ForwardRefExoticComponent<Pick<MessageMetaProps, "slot" | "style" | "title" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "children" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "classNames" | "continues" | keyof MessageMetadata> & React.RefAttributes<HTMLDivElement>>;
9
- export type MessageProps = PropsWithChildren<MessageMetadata>;
10
- export declare const Message: ({ timestamp, children, ...messageMeta }: MessageProps) => JSX.Element;
9
+ export type MessageBlockProps<BlockValue> = {
10
+ block: MessageEntityBlock<BlockValue>;
11
+ authorId?: string;
12
+ onBlockDelete?: () => void;
13
+ };
14
+ export type MessageProps<BlockValue> = MessageEntity<BlockValue> & {
15
+ onDelete?: (messageId: string, blockIndex: number) => void;
16
+ MessageBlockComponent?: FC<MessageBlockProps<BlockValue>>;
17
+ };
18
+ export declare const Message: <BlockValue>(props: MessageProps<BlockValue>) => JSX.Element;
11
19
  export type MessageTextboxProps = {
12
20
  disabled?: boolean;
13
21
  onSend?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../../src/Message/Message.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAE,KAAK,qBAAqB,EAAuB,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAGvG,OAAO,EAAU,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,KAAK,eAAe,EAAgC,MAAM,uBAAuB,CAAC;AAK3F,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAIhD,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,GAC1E,eAAe,GACf,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAElC,eAAO,MAAM,WAAW,qnJA4BvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,OAAO,4CAA6C,YAAY,gBAmB5E,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B,GAAG,eAAe,GACjB,eAAe,CAAC;AA4BlB,eAAO,MAAM,cAAc,qJAYxB,mBAAmB,gBAyBrB,CAAC"}
1
+ {"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../../src/Message/Message.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,EAAE,KAAK,qBAAqB,EAAE,KAAK,EAAE,EAAuB,MAAM,OAAO,CAAC;AAGxF,OAAO,EAAkB,KAAK,eAAe,EAAkB,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAE,KAAK,eAAe,EAAgC,MAAM,uBAAuB,CAAC;AAW3F,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAI7F,MAAM,MAAM,gBAAgB,GAAG,eAAe,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,GAC1E,eAAe,GACf,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAElC,eAAO,MAAM,WAAW,qnJA4BvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,UAAU,IAAI;IAC1C,KAAK,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AA6BF,MAAM,MAAM,YAAY,CAAC,UAAU,IAAI,aAAa,CAAC,UAAU,CAAC,GAAG;IACjE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,qBAAqB,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;CAC3D,CAAC;AAEF,eAAO,MAAM,OAAO,sBAAwB,aAAa,UAAU,CAAC,gBA4BnE,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B,GAAG,eAAe,GACjB,eAAe,CAAC;AA4BlB,eAAO,MAAM,cAAc,qJAYxB,mBAAmB,gBAyBrB,CAAC"}
@@ -2,7 +2,7 @@
2
2
  import '@dxosTheme';
3
3
  declare const _default: {
4
4
  title: string;
5
- component: ({ timestamp, children, ...messageMeta }: import("./Message").MessageProps) => JSX.Element;
5
+ component: <BlockValue>(props: import("./Message").MessageProps<BlockValue>) => JSX.Element;
6
6
  render: () => JSX.Element;
7
7
  decorators: import("@storybook/react/*").Decorator[];
8
8
  parameters: {
@@ -1 +1 @@
1
- {"version":3,"file":"Message.stories.d.ts","sourceRoot":"","sources":["../../../../src/Message/Message.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA6BpB,wBAME;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
1
+ {"version":3,"file":"Message.stories.d.ts","sourceRoot":"","sources":["../../../../src/Message/Message.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AAgCpB,wBAME;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
@@ -1,13 +1,16 @@
1
1
  import '@dxosTheme';
2
2
  import React from 'react';
3
+ import { TextV0Type } from '@braneframe/types';
3
4
  import { type Range } from '@dxos/react-ui-editor';
4
- import { type MessageEntity } from '../testing';
5
+ import { type MessageEntity } from '../types';
5
6
  type StoryCommentThread = {
6
7
  id: string;
7
8
  cursor?: string;
8
9
  range?: Range;
9
10
  yPos?: number;
10
- messages: MessageEntity[];
11
+ messages: MessageEntity<{
12
+ content?: TextV0Type;
13
+ }>[];
11
14
  };
12
15
  type StoryProps = {
13
16
  text?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Comments.stories.d.ts","sourceRoot":"","sources":["../../../../src/Thread/Comments.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AAGpB,OAAO,KAAwD,MAAM,OAAO,CAAC;AAS7E,OAAO,EAML,KAAK,KAAK,EAMX,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAqEhD,KAAK,kBAAkB,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC;AA+IF,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;;;;;;;;;;mBA2He,UAAU;;;;;;;;;;;;;;;;;;;;;AAJ3B,wBAME;AAWF,eAAO,MAAM,OAAO;;;;CAInB,CAAC"}
1
+ {"version":3,"file":"Comments.stories.d.ts","sourceRoot":"","sources":["../../../../src/Thread/Comments.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AAGpB,OAAO,KAAwD,MAAM,OAAO,CAAC;AAE7E,OAAO,EAAe,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAO5D,OAAO,EAML,KAAK,KAAK,EAMX,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAoE9C,KAAK,kBAAkB,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;QAAE,OAAO,CAAC,EAAE,UAAU,CAAA;KAAE,CAAC,EAAE,CAAC;CACrD,CAAC;AA6IF,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;;;;;;;;;;mBA+He,UAAU;;;;;;;;;;;;;;;;;;;;;AAJ3B,wBAME;AAWF,eAAO,MAAM,OAAO;;;;CAInB,CAAC"}
@@ -2,7 +2,7 @@ import '@dxosTheme';
2
2
  import React from 'react';
3
3
  declare const _default: {
4
4
  title: string;
5
- component: React.ForwardRefExoticComponent<Pick<import("./Thread").ThreadProps, "slot" | "style" | "title" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "children" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "current" | "classNames" | keyof import("..").ThreadEntity> & React.RefAttributes<HTMLDivElement>>;
5
+ component: React.ForwardRefExoticComponent<Pick<import("./Thread").ThreadProps, "slot" | "style" | "title" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "children" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "current" | "classNames" | keyof import("../types").ThreadEntity> & React.RefAttributes<HTMLDivElement>>;
6
6
  render: () => JSX.Element;
7
7
  decorators: import("@storybook/react/*").Decorator[];
8
8
  parameters: {
@@ -1 +1 @@
1
- {"version":3,"file":"Thread.stories.d.ts","sourceRoot":"","sources":["../../../../src/Thread/Thread.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AACpB,OAAO,KAAoC,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA2EzD,wBAME;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
1
+ {"version":3,"file":"Thread.stories.d.ts","sourceRoot":"","sources":["../../../../src/Thread/Thread.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AACpB,OAAO,KAAoC,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AAwFzD,wBAME;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
@@ -1,12 +1,17 @@
1
1
  import { type FallbackValue } from '@dxos/util';
2
2
  export type MessageMetadata = {
3
3
  id: string;
4
- timestamp?: string;
5
4
  authorId?: string;
6
5
  authorName?: string;
7
6
  authorImgSrc?: string;
8
7
  authorAvatarProps?: FallbackValue;
9
8
  };
9
+ export type MessageEntityBlock<BlockValue> = BlockValue & {
10
+ timestamp?: string;
11
+ };
12
+ export type MessageEntity<BlockValue> = MessageMetadata & {
13
+ blocks: MessageEntityBlock<BlockValue>[];
14
+ };
10
15
  export type ThreadEntity = {
11
16
  topicId?: string;
12
17
  topicTitle?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,UAAU,IAAI,UAAU,GAAG;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,UAAU,IAAI,eAAe,GAAG;IACxD,MAAM,EAAE,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-thread",
3
- "version": "0.5.9-main.44fe51d",
3
+ "version": "0.5.9-main.4ed8273",
4
4
  "description": "A set of components to use with threads, including comments and messages.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -18,11 +18,11 @@
18
18
  "dependencies": {
19
19
  "@radix-ui/react-primitive": "^1.0.2",
20
20
  "date-fns": "^3.3.1",
21
- "@dxos/react-ui-editor": "0.5.9-main.44fe51d",
22
- "@dxos/react-ui": "0.5.9-main.44fe51d",
23
- "@dxos/react-ui-mosaic": "0.5.9-main.44fe51d",
24
- "@dxos/util": "0.5.9-main.44fe51d",
25
- "@dxos/react-ui-theme": "0.5.9-main.44fe51d"
21
+ "@dxos/react-ui": "0.5.9-main.4ed8273",
22
+ "@dxos/react-ui-mosaic": "0.5.9-main.4ed8273",
23
+ "@dxos/react-ui-editor": "0.5.9-main.4ed8273",
24
+ "@dxos/util": "0.5.9-main.4ed8273",
25
+ "@dxos/react-ui-theme": "0.5.9-main.4ed8273"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@phosphor-icons/react": "^2.1.5",
@@ -31,13 +31,13 @@
31
31
  "react": "^18.2.0",
32
32
  "react-dom": "^18.2.0",
33
33
  "vite": "^5.2.9",
34
- "@braneframe/types": "0.5.9-main.44fe51d",
35
- "@dxos/echo-db": "0.5.9-main.44fe51d",
36
- "@dxos/echo-schema": "0.5.9-main.44fe51d",
37
- "@dxos/keys": "0.5.9-main.44fe51d",
38
- "@dxos/log": "0.5.9-main.44fe51d",
39
- "@dxos/random": "0.5.9-main.44fe51d",
40
- "@dxos/storybook-utils": "0.5.9-main.44fe51d"
34
+ "@dxos/echo-db": "0.5.9-main.4ed8273",
35
+ "@braneframe/types": "0.5.9-main.4ed8273",
36
+ "@dxos/keys": "0.5.9-main.4ed8273",
37
+ "@dxos/echo-schema": "0.5.9-main.4ed8273",
38
+ "@dxos/log": "0.5.9-main.4ed8273",
39
+ "@dxos/random": "0.5.9-main.4ed8273",
40
+ "@dxos/storybook-utils": "0.5.9-main.4ed8273"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@phosphor-icons/react": "^2.1.5",
@@ -10,23 +10,26 @@ import { PublicKey } from '@dxos/keys';
10
10
  import { withTheme } from '@dxos/storybook-utils';
11
11
 
12
12
  import { Message } from './Message';
13
- import { DefaultMessageText, type MessageEntity } from '../testing';
14
13
  import translations from '../translations';
14
+ import { type MessageEntity } from '../types';
15
15
 
16
16
  const Story = () => {
17
17
  const [identityKey] = useState(PublicKey.random());
18
- const [message] = useState<MessageEntity>({
18
+ const [message] = useState<MessageEntity<{ id: string; text: string }>>({
19
19
  id: 'm1',
20
- timestamp: new Date().toISOString(),
21
20
  authorId: identityKey.toHex(),
22
- text: 'hello',
21
+ blocks: [
22
+ {
23
+ id: 'b1',
24
+ timestamp: new Date().toISOString(),
25
+ text: 'hello',
26
+ },
27
+ ],
23
28
  });
24
29
 
25
30
  return (
26
31
  <div className='grid grid-cols-[var(--rail-size)_1fr]'>
27
- <Message {...message}>
28
- <DefaultMessageText text={message.text} onDelete={() => console.log('delete')} />
29
- </Message>
32
+ <Message {...message} />
30
33
  </div>
31
34
  );
32
35
  };
@@ -2,17 +2,24 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { X } from '@phosphor-icons/react';
5
6
  import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
6
- import React, { type ComponentPropsWithRef, forwardRef, useMemo, type PropsWithChildren } from 'react';
7
+ import React, { type ComponentPropsWithRef, type FC, forwardRef, useMemo } from 'react';
7
8
 
8
9
  // TODO(burdon): Remove dep.
9
- import { Avatar, type ThemedClassName, useTranslation } from '@dxos/react-ui';
10
+ import { Avatar, Button, type ThemedClassName, useTranslation } from '@dxos/react-ui';
10
11
  import { type TextEditorProps, keymap, listener, TextEditor } from '@dxos/react-ui-editor';
11
- import { focusRing, mx } from '@dxos/react-ui-theme';
12
- import { hexToEmoji, hexToHue, isNotFalsy } from '@dxos/util';
12
+ import {
13
+ focusRing,
14
+ hoverableControlItem,
15
+ hoverableControls,
16
+ hoverableFocusedWithinControls,
17
+ mx,
18
+ } from '@dxos/react-ui-theme';
19
+ import { safeParseJson, hexToEmoji, hexToHue, isNotFalsy } from '@dxos/util';
13
20
 
14
21
  import { translationKey } from '../translations';
15
- import { type MessageMetadata } from '../types';
22
+ import { type MessageEntity, type MessageEntityBlock, type MessageMetadata } from '../types';
16
23
 
17
24
  const avatarSize = 7;
18
25
 
@@ -50,24 +57,69 @@ export const MessageMeta = forwardRef<HTMLDivElement, MessageMetaProps>(
50
57
  },
51
58
  );
52
59
 
53
- export type MessageProps = PropsWithChildren<MessageMetadata>;
60
+ export type MessageBlockProps<BlockValue> = {
61
+ block: MessageEntityBlock<BlockValue>;
62
+ authorId?: string;
63
+ onBlockDelete?: () => void;
64
+ };
65
+
66
+ const DefaultMessageBlock = ({ block, onBlockDelete }: MessageBlockProps<{ data?: any; text?: string }>) => {
67
+ const contentWidth = onBlockDelete ? 'col-span-2' : 'col-span-3';
68
+ return (
69
+ <div
70
+ role='none'
71
+ className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}
72
+ >
73
+ {block.data ? (
74
+ <pre className={mx('font-mono max-is-full overflow-x-auto', contentWidth)}>
75
+ <code>{JSON.stringify(safeParseJson(block.data), undefined, 2)}</code>
76
+ </pre>
77
+ ) : (
78
+ <p className={contentWidth}>{block.text ?? ''}</p>
79
+ )}
80
+ {onBlockDelete && (
81
+ <Button
82
+ variant='ghost'
83
+ classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}
84
+ onClick={() => onBlockDelete()}
85
+ >
86
+ <X />
87
+ </Button>
88
+ )}
89
+ </div>
90
+ );
91
+ };
92
+
93
+ export type MessageProps<BlockValue> = MessageEntity<BlockValue> & {
94
+ onDelete?: (messageId: string, blockIndex: number) => void;
95
+ MessageBlockComponent?: FC<MessageBlockProps<BlockValue>>;
96
+ };
54
97
 
55
- export const Message = ({ timestamp, children, ...messageMeta }: MessageProps) => {
98
+ export const Message = <BlockValue,>(props: MessageProps<BlockValue>) => {
99
+ const { id, authorName, onDelete, blocks, MessageBlockComponent = DefaultMessageBlock, ...metaProps } = props;
56
100
  const { t, dtLocale } = useTranslation(translationKey);
57
- const dt = timestamp ? new Date(timestamp) : undefined;
101
+ const firstBlock = blocks[0];
102
+ const dt = firstBlock.timestamp ? new Date(firstBlock.timestamp) : undefined;
58
103
 
59
104
  return (
60
- <MessageMeta {...messageMeta} continues>
105
+ <MessageMeta {...metaProps} id={id} continues>
61
106
  <p className='grid grid-cols-[1fr_max-content] gap-2 pie-2'>
62
- <Avatar.Label classNames={['truncate text-sm font-medium', !messageMeta.authorName && 'fg-description']}>
63
- {messageMeta.authorName ?? t('anonymous label')}
107
+ <Avatar.Label classNames={['truncate text-sm font-medium', !authorName && 'fg-description']}>
108
+ {authorName ?? t('anonymous label')}
64
109
  </Avatar.Label>
65
110
  <time className='fg-description text-xs pbs-0.5' dateTime={dt?.toISOString()}>
66
111
  {dt ? formatDistanceToNow(dt, { locale: dtLocale, addSuffix: true }) : ''}
67
112
  </time>
68
113
  </p>
69
114
  <div role='none' className='grid gap-y-1 grid-cols-[min-content_1fr_min-content]'>
70
- {children}
115
+ {blocks.map((block, i) => (
116
+ <MessageBlockComponent
117
+ key={i}
118
+ block={block}
119
+ authorId={metaProps.authorId}
120
+ onBlockDelete={onDelete && (() => onDelete(id, i))}
121
+ />
122
+ ))}
71
123
  </div>
72
124
  </MessageMeta>
73
125
  );
@@ -7,7 +7,7 @@ import '@dxosTheme';
7
7
  import { Check, Trash } from '@phosphor-icons/react';
8
8
  import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
9
9
 
10
- import { MessageType, TextType } from '@braneframe/types';
10
+ import { MessageType, TextV0Type } from '@braneframe/types';
11
11
  import { createDocAccessor, createEchoObject } from '@dxos/echo-db';
12
12
  import { create } from '@dxos/echo-schema';
13
13
  import { PublicKey } from '@dxos/keys';
@@ -30,9 +30,9 @@ import {
30
30
  import { withTheme } from '@dxos/storybook-utils';
31
31
 
32
32
  import { Thread, ThreadFooter, ThreadHeading } from './Thread';
33
- import { Message, MessageTextbox } from '../Message';
34
- import { type MessageEntity } from '../testing';
33
+ import { Message, type MessageBlockProps, MessageTextbox } from '../Message';
35
34
  import translations from '../translations';
35
+ import { type MessageEntity } from '../types';
36
36
 
37
37
  faker.seed(101);
38
38
 
@@ -44,7 +44,7 @@ const authorId = PublicKey.random().toHex();
44
44
 
45
45
  const Editor: FC<{
46
46
  id?: string;
47
- item: TextType;
47
+ item: TextV0Type;
48
48
  comments: Comment[];
49
49
  selected?: string;
50
50
  onCreateComment: CommentsOptions['onCreate'];
@@ -105,22 +105,18 @@ type StoryCommentThread = {
105
105
  cursor?: string;
106
106
  range?: Range;
107
107
  yPos?: number;
108
- messages: MessageEntity[];
108
+ messages: MessageEntity<{ content?: TextV0Type }>[];
109
109
  };
110
110
 
111
- type MessageTextProps = {
112
- message: MessageEntity;
113
- };
114
-
115
- const StoryMessageText = ({ message }: MessageTextProps) => {
111
+ const StoryMessageBlock = (props: MessageBlockProps<{ content?: TextV0Type }>) => {
116
112
  const { themeMode } = useThemeContext();
117
- const doc = message.text;
118
- const accessor = createDocAccessor(message, ['text']);
113
+ const doc = props.block.content?.content;
114
+ const accessor = createDocAccessor(props.block.content!, ['content']);
119
115
  const { parentRef } = useTextEditor(
120
116
  () => ({
121
117
  doc,
122
118
  extensions: [
123
- // prettier-ignore
119
+ //
124
120
  createBasicExtensions(),
125
121
  createThemeExtensions({ themeMode }),
126
122
  automerge(accessor),
@@ -167,9 +163,13 @@ const StoryThread: FC<{
167
163
  const handleCreateMessage = () => {
168
164
  if (messageRef.current?.length) {
169
165
  const message = create(MessageType, {
170
- timestamp: new Date().toISOString(),
171
- sender: { identityKey: authorId },
172
- text: messageRef.current,
166
+ from: { identityKey: authorId },
167
+ blocks: [
168
+ {
169
+ timestamp: new Date().toISOString(),
170
+ content: create(TextV0Type, { content: messageRef.current }),
171
+ },
172
+ ],
173
173
  });
174
174
  thread.messages.push(message);
175
175
 
@@ -193,9 +193,7 @@ const StoryThread: FC<{
193
193
  </ThreadHeading>
194
194
 
195
195
  {thread.messages.map((message) => (
196
- <Message key={message.id} {...message}>
197
- <StoryMessageText message={message} />
198
- </Message>
196
+ <Message<{ content?: TextV0Type }> key={message.id} {...message} MessageBlockComponent={StoryMessageBlock} />
199
197
  ))}
200
198
 
201
199
  <div ref={containerRef} className='contents'>
@@ -255,7 +253,7 @@ type StoryProps = {
255
253
  };
256
254
 
257
255
  const Story = ({ text, autoCreate }: StoryProps) => {
258
- const [item] = useState(createEchoObject(create(TextType, { content: text ?? '' })));
256
+ const [item] = useState(createEchoObject(create(TextV0Type, { content: text ?? '' })));
259
257
  const [threads, setThreads] = useState<StoryCommentThread[]>([]);
260
258
  const [selected, setSelected] = useState<string>();
261
259
 
@@ -277,9 +275,13 @@ const Story = ({ text, autoCreate }: StoryProps) => {
277
275
  ? faker.helpers.multiple(
278
276
  () => ({
279
277
  id: PublicKey.random().toHex(),
280
- timestamp: new Date().toISOString(),
281
278
  authorId: PublicKey.random().toHex(),
282
- text: faker.lorem.sentence(),
279
+ blocks: [
280
+ {
281
+ timestamp: new Date().toISOString(),
282
+ content: create(TextV0Type, { content: faker.lorem.sentence() }),
283
+ },
284
+ ],
283
285
  }),
284
286
  { count: { min: 1, max: 3 } },
285
287
  )
@@ -11,8 +11,8 @@ import { withTheme } from '@dxos/storybook-utils';
11
11
 
12
12
  import { Thread, ThreadFooter } from './Thread';
13
13
  import { Message, MessageTextbox } from '../Message';
14
- import { DefaultMessageText, type MessageEntity } from '../testing';
15
14
  import translations from '../translations';
15
+ import { type MessageEntity } from '../types';
16
16
 
17
17
  const Story = () => {
18
18
  const [pending, setPending] = useState(false);
@@ -21,15 +21,25 @@ const Story = () => {
21
21
  const [messages, setMesssages] = useState<MessageEntity<{ id: string; text: string }>[]>([
22
22
  {
23
23
  id: 'm1',
24
- timestamp: new Date().toISOString(),
25
24
  authorId: identityKey1.toHex(),
26
- text: 'hello',
25
+ blocks: [
26
+ {
27
+ id: 'b1',
28
+ timestamp: new Date().toISOString(),
29
+ text: 'hello',
30
+ },
31
+ ],
27
32
  },
28
33
  {
29
34
  id: 'm2',
30
- timestamp: new Date().toISOString(),
31
35
  authorId: identityKey2.toHex(),
32
- text: 'hi there',
36
+ blocks: [
37
+ {
38
+ id: 'b2',
39
+ timestamp: new Date().toISOString(),
40
+ text: 'hi there',
41
+ },
42
+ ],
33
43
  },
34
44
  ]);
35
45
 
@@ -48,9 +58,14 @@ const Story = () => {
48
58
  ...messages,
49
59
  {
50
60
  id: `m${_count}`,
51
- timestamp: new Date().toISOString(),
52
61
  authorId: identityKey1.toHex(),
53
- text: messageRef.current,
62
+ blocks: [
63
+ {
64
+ id: `b${_count}`,
65
+ timestamp: new Date().toISOString(),
66
+ text: messageRef.current,
67
+ },
68
+ ],
54
69
  },
55
70
  ]);
56
71
  messageRef.current = '';
@@ -62,9 +77,7 @@ const Story = () => {
62
77
  return (
63
78
  <Thread id='t1'>
64
79
  {messages.map((message) => (
65
- <Message key={message.id} {...message}>
66
- <DefaultMessageText text={message.text} onDelete={() => console.log('delete')} />
67
- </Message>
80
+ <Message key={message.id} {...message} />
68
81
  ))}
69
82
  <MessageTextbox
70
83
  id={String(_count)}
package/src/types.ts CHANGED
@@ -6,13 +6,20 @@ import { type FallbackValue } from '@dxos/util';
6
6
 
7
7
  export type MessageMetadata = {
8
8
  id: string; // TODO(burdon): Remove/rename?
9
- timestamp?: string;
10
9
  authorId?: string;
11
10
  authorName?: string;
12
11
  authorImgSrc?: string;
13
12
  authorAvatarProps?: FallbackValue;
14
13
  };
15
14
 
15
+ export type MessageEntityBlock<BlockValue> = BlockValue & {
16
+ timestamp?: string;
17
+ };
18
+
19
+ export type MessageEntity<BlockValue> = MessageMetadata & {
20
+ blocks: MessageEntityBlock<BlockValue>[];
21
+ };
22
+
16
23
  export type ThreadEntity = {
17
24
  topicId?: string;
18
25
  topicTitle?: string;
@@ -1,16 +0,0 @@
1
- /// <reference types="react" />
2
- import { type MessageMetadata } from './types';
3
- export type MessageEntity<PartValue = any> = MessageMetadata & {
4
- text: string;
5
- parts?: PartValue[];
6
- };
7
- export type MessageTextProps = {
8
- text: string;
9
- onDelete?: () => void;
10
- };
11
- export declare const DefaultMessageText: ({ text, onDelete }: MessageTextProps) => JSX.Element;
12
- export type MessagePartProps<PartValue> = {
13
- part: PartValue;
14
- };
15
- export declare const DefaultMessageBlock: <PartValue>({ part }: MessagePartProps<PartValue>) => JSX.Element;
16
- //# sourceMappingURL=testing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../src/testing.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,aAAa,CAAC,SAAS,GAAG,GAAG,IAAI,eAAe,GAAG;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,kBAAkB,uBAAwB,gBAAgB,gBAmBtE,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,SAAS,IAAI;IACxC,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,mBAAmB,wBAA0B,iBAAiB,SAAS,CAAC,gBAWpF,CAAC"}
package/src/testing.tsx DELETED
@@ -1,59 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { X } from '@phosphor-icons/react';
6
- import React from 'react';
7
-
8
- import { Button } from '@dxos/react-ui';
9
- import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
10
-
11
- import { type MessageMetadata } from './types';
12
-
13
- export type MessageEntity<PartValue = any> = MessageMetadata & {
14
- text: string;
15
- parts?: PartValue[];
16
- };
17
-
18
- export type MessageTextProps = {
19
- text: string;
20
- onDelete?: () => void;
21
- };
22
-
23
- export const DefaultMessageText = ({ text, onDelete }: MessageTextProps) => {
24
- const contentWidth = onDelete ? 'col-span-2' : 'col-span-3';
25
- return (
26
- <div
27
- role='none'
28
- className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}
29
- >
30
- <p className={contentWidth}>{text}</p>
31
- {onDelete && (
32
- <Button
33
- variant='ghost'
34
- classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}
35
- onClick={() => onDelete()}
36
- >
37
- <X />
38
- </Button>
39
- )}
40
- </div>
41
- );
42
- };
43
-
44
- export type MessagePartProps<PartValue> = {
45
- part: PartValue;
46
- };
47
-
48
- export const DefaultMessageBlock = <PartValue,>({ part }: MessagePartProps<PartValue>) => {
49
- return (
50
- <div
51
- role='none'
52
- className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}
53
- >
54
- <pre className='font-mono max-is-full overflow-x-auto col-span-3'>
55
- <code>{JSON.stringify(part, undefined, 2)}</code>
56
- </pre>
57
- </div>
58
- );
59
- };