@terrafirmagreg-team/chat 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@terrafirmagreg-team/chat",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./src/ui/index.ts",
7
+ "./lib": "./src/lib/index.ts",
8
+ "./styles.css": "./src/styles.css",
9
+ "./*": [
10
+ "./src/*.ts",
11
+ "./src/*.tsx",
12
+ "./src/*/index.ts",
13
+ "./src/*/index.tsx"
14
+ ]
15
+ },
16
+ "files": [
17
+ "src",
18
+ "styles",
19
+ "styles.css"
20
+ ],
21
+ "dependencies": {
22
+ "@terrafirmagreg-team/ui": "0.1.0",
23
+ "lucide-react": "^1.63.0",
24
+ "@terrafirmagreg-team/translate": "0.1.0"
25
+ },
26
+ "peerDependencies": {
27
+ "react": ">=19"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^26.6.5",
31
+ "@types/react": "^19.3.0",
32
+ "@types/react-dom": "^19.3.0"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/TerraFirmaGreg-Team/Web-Foundation.git",
37
+ "directory": "packages/chat"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
6
42
  }
@@ -0,0 +1,28 @@
1
+ import type {ChatMessage} from '@terrafirmagreg-team/chat/types';
2
+
3
+ export function getStoredName(key: string): string {
4
+ try {
5
+ return window.localStorage?.getItem(key) ?? '';
6
+ } catch {
7
+ return '';
8
+ }
9
+ }
10
+
11
+ export function setStoredName(key: string, name: string): void {
12
+ try {
13
+ window.localStorage?.setItem(key, name);
14
+ } catch {
15
+
16
+ }
17
+ }
18
+
19
+
20
+ export function isOwnChatMessage(
21
+ message: ChatMessage,
22
+ names: {chatName?: string; loginName?: string},
23
+ ): boolean {
24
+ if (message.type !== 'chat' || message.source !== 'web') return false;
25
+ const chat = names.chatName?.trim();
26
+ const login = names.loginName?.trim();
27
+ return (!!chat && message.playerName === chat) || (!!login && message.playerName === login);
28
+ }
@@ -0,0 +1,12 @@
1
+ export type {
2
+ ChatBoxSettings,
3
+ ChatMessage,
4
+ ChatMessageType,
5
+ ChatSendingSettings,
6
+ } from '@terrafirmagreg-team/chat/types';
7
+ export {
8
+ getStoredName,
9
+ isOwnChatMessage,
10
+ setStoredName,
11
+ } from './identity';
12
+ export { type ChatTranslate } from '@terrafirmagreg-team/chat/types';
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "Chat",
4
+ "noMessages": "No messages yet",
5
+ "placeholder": "Message",
6
+ "namePlaceholder": "Name",
7
+ "nameLabel": "Name",
8
+ "send": "Send",
9
+ "login": "Log in",
10
+ "playerJoin": "{playername} joined the game",
11
+ "playerQuit": "{playername} left the game",
12
+ "anonymousJoin": "A player joined the game",
13
+ "anonymousQuit": "A player left the game",
14
+ "errorUnknown": "Unknown error"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "Czat",
4
+ "noMessages": "Brak wiadomości",
5
+ "placeholder": "Wiadomość",
6
+ "namePlaceholder": "Nazwa",
7
+ "nameLabel": "Nazwa",
8
+ "send": "Wyślij",
9
+ "login": "Zaloguj się",
10
+ "playerJoin": "{playername} dołączył do gry",
11
+ "playerQuit": "{playername} opuścił grę",
12
+ "anonymousJoin": "Gracz dołączył do gry",
13
+ "anonymousQuit": "Gracz opuścił grę",
14
+ "errorUnknown": "Nieznany błąd"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "Чат",
4
+ "noMessages": "Пока нет сообщений",
5
+ "placeholder": "Сообщение",
6
+ "namePlaceholder": "Имя",
7
+ "nameLabel": "Имя",
8
+ "send": "Отправить",
9
+ "login": "Войти",
10
+ "playerJoin": "{playername} зашёл в игру",
11
+ "playerQuit": "{playername} вышел из игры",
12
+ "anonymousJoin": "Игрок зашёл в игру",
13
+ "anonymousQuit": "Игрок вышел из игры",
14
+ "errorUnknown": "Неизвестная ошибка"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "Чат",
4
+ "noMessages": "Поки немає повідомлень",
5
+ "placeholder": "Повідомлення",
6
+ "namePlaceholder": "Ім'я",
7
+ "nameLabel": "Ім'я",
8
+ "send": "Надіслати",
9
+ "login": "Увійти",
10
+ "playerJoin": "{playername} зайшов у гру",
11
+ "playerQuit": "{playername} вийшов з гри",
12
+ "anonymousJoin": "Гравець зайшов у гру",
13
+ "anonymousQuit": "Гравець вийшов з гри",
14
+ "errorUnknown": "Невідома помилка"
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "",
4
+ "noMessages": "",
5
+ "placeholder": "",
6
+ "namePlaceholder": "",
7
+ "nameLabel": "",
8
+ "send": "",
9
+ "login": "",
10
+ "playerJoin": "{playername} ",
11
+ "playerQuit": "{playername} ",
12
+ "anonymousJoin": "",
13
+ "anonymousQuit": "",
14
+ "errorUnknown": ""
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "chat": {
3
+ "title": "",
4
+ "noMessages": "",
5
+ "placeholder": "",
6
+ "namePlaceholder": "",
7
+ "nameLabel": "",
8
+ "send": "",
9
+ "login": "",
10
+ "playerJoin": "{playername} ",
11
+ "playerQuit": "{playername} ",
12
+ "anonymousJoin": "",
13
+ "anonymousQuit": "",
14
+ "errorUnknown": ""
15
+ }
16
+ }
package/src/locales.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { createLocaleCatalog } from '@terrafirmagreg-team/translate/lib/locales';
2
+
3
+ export const locales = createLocaleCatalog(
4
+ import.meta.glob('./locales/*.json', { eager: true, import: 'default' }),
5
+ );
package/src/styles.css ADDED
@@ -0,0 +1,179 @@
1
+ .chat {
2
+ flex: 1 1 auto;
3
+ min-height: 0;
4
+ display: flex;
5
+ flex-direction: column;
6
+ }
7
+
8
+ .chat__header {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 0.8rem;
12
+ flex-shrink: 0;
13
+ min-height: 5.6rem;
14
+ padding: 0 1.2rem 0 1.6rem;
15
+ border-bottom: 1px solid var(--border);
16
+ }
17
+
18
+ .chat__title {
19
+ flex: 1 1 auto;
20
+ min-width: 0;
21
+ font-size: 1.5rem;
22
+ font-weight: 600;
23
+ color: var(--text-emphasis);
24
+ overflow: hidden;
25
+ text-overflow: ellipsis;
26
+ white-space: nowrap;
27
+ }
28
+
29
+ .chat__close {
30
+ display: none;
31
+ flex: 0 0 auto;
32
+ width: 3.2rem;
33
+ height: 3.2rem;
34
+ align-items: center;
35
+ justify-content: center;
36
+ border: none;
37
+ border-radius: 0.8rem;
38
+ background: transparent;
39
+ color: var(--muted-foreground);
40
+ cursor: pointer;
41
+ transition: background 0.15s, color 0.15s;
42
+ }
43
+
44
+ .chat__close:hover {
45
+ background: var(--accent);
46
+ color: var(--accent-foreground);
47
+ }
48
+
49
+ .chat__close .svg-icon {
50
+ width: 1.6rem;
51
+ height: 1.6rem;
52
+ }
53
+
54
+ @media (width < 1024px) {
55
+ .chat__close {
56
+ display: inline-flex;
57
+ }
58
+ }
59
+
60
+ .chat__offline {
61
+ flex: 1;
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ }
66
+
67
+ .chat__offline-chip {
68
+ display: inline-flex;
69
+ align-items: center;
70
+ gap: 0.6rem;
71
+ height: 2.8rem;
72
+ padding: 0 1.2rem;
73
+ border-radius: 999px;
74
+ background: color-mix(in srgb, var(--color-secondary) 50%, transparent);
75
+ border: 1px solid var(--border);
76
+ color: var(--muted-foreground);
77
+ font-size: 1.2rem;
78
+ }
79
+
80
+ .chat__scroll {
81
+ flex: 1 1 auto;
82
+ min-height: 0;
83
+ }
84
+
85
+ .chat__content {
86
+ padding: 0.8rem 1.2rem 1.2rem;
87
+ gap: 0.8rem;
88
+ }
89
+
90
+ .chat__face {
91
+ border-radius: 999px;
92
+ display: block;
93
+ }
94
+
95
+ .chat__meta {
96
+ gap: 0.6rem;
97
+ }
98
+
99
+ .chat__meta > *:not(:first-child)::before {
100
+ content: '·\00a0';
101
+ opacity: 0.6;
102
+ }
103
+
104
+ .chat__event {
105
+ text-align: center;
106
+ font-size: 1.2rem;
107
+ font-style: italic;
108
+ color: var(--muted-foreground);
109
+ }
110
+
111
+ .chat__name {
112
+ flex: 1;
113
+ min-width: 0;
114
+ border: none;
115
+ background: transparent;
116
+ padding: 0;
117
+ color: var(--foreground);
118
+ font-size: 1.3rem;
119
+ outline: none;
120
+ }
121
+
122
+ .chat__name::placeholder {
123
+ color: var(--muted-foreground);
124
+ }
125
+
126
+ .chat__form {
127
+ flex: 0 0 auto;
128
+ display: flex;
129
+ flex-direction: column;
130
+ gap: 0.4rem;
131
+ margin: 0 1.2rem 1.2rem;
132
+ }
133
+
134
+ .chat__error {
135
+ display: flex;
136
+ align-items: center;
137
+ gap: 1.2rem;
138
+ border-radius: 0.8rem;
139
+ background: color-mix(in srgb, var(--destructive) 5%, transparent);
140
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--destructive) 15%, transparent);
141
+ padding: 0.8rem 1.2rem;
142
+ font-size: 1.3rem;
143
+ }
144
+
145
+ .chat__error-dot {
146
+ width: 0.6rem;
147
+ height: 0.6rem;
148
+ border-radius: 999px;
149
+ background: var(--destructive);
150
+ flex: 0 0 auto;
151
+ }
152
+
153
+ .chat__login {
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ gap: 0.8rem;
158
+ flex-shrink: 0;
159
+ margin: 0 1.2rem 1.2rem;
160
+ height: 4rem;
161
+ border: 1px solid var(--border);
162
+ border-radius: 0.8rem;
163
+ background: var(--popover);
164
+ color: var(--muted-foreground);
165
+ font-size: 1.4rem;
166
+ cursor: pointer;
167
+ transition: background 0.15s, color 0.15s;
168
+ }
169
+
170
+ .chat__login:hover {
171
+ color: var(--accent-foreground);
172
+ background: var(--accent);
173
+ }
174
+
175
+ @media (max-width: 400px), (max-height: 480px) {
176
+ .chat {
177
+ max-width: calc(100% - 7rem);
178
+ }
179
+ }
package/src/types.ts ADDED
@@ -0,0 +1,31 @@
1
+ export type ChatMessageType = 'chat' | 'playerjoin' | 'playerleave';
2
+
3
+ export interface ChatMessage {
4
+ type: ChatMessageType;
5
+
6
+ message?: string;
7
+
8
+ playerName?: string;
9
+
10
+ playerAccount?: string;
11
+
12
+ channel?: string;
13
+
14
+ source?: string;
15
+ timestamp?: number;
16
+ }
17
+
18
+ export interface ChatBoxSettings {
19
+
20
+ messageHistory?: number;
21
+
22
+ showPlayerFaces?: boolean;
23
+ }
24
+
25
+ export interface ChatSendingSettings {
26
+ maxLength?: number;
27
+
28
+ loginRequired?: boolean;
29
+ }
30
+
31
+ export type ChatTranslate = (key: string, params?: Record<string, unknown>) => string;
package/src/ui/box.tsx ADDED
@@ -0,0 +1,228 @@
1
+ 'use client';
2
+ import {useRef, useState, type FormEvent, type ReactNode} from 'react';
3
+ import {ArrowUp, LogIn, User, WifiOff, X} from 'lucide-react';
4
+ import {
5
+ Empty,
6
+ EmptyDescription,
7
+ EmptyHeader,
8
+ EmptyTitle,
9
+ } from '@terrafirmagreg-team/ui/components/ui/empty';
10
+ import {
11
+ InputGroup,
12
+ InputGroupAddon,
13
+ InputGroupButton,
14
+ InputGroupTextarea,
15
+ } from '@terrafirmagreg-team/ui/components/ui/input-group';
16
+ import {
17
+ MessageScroller,
18
+ MessageScrollerButton,
19
+ MessageScrollerContent,
20
+ MessageScrollerItem,
21
+ MessageScrollerProvider,
22
+ MessageScrollerViewport,
23
+ } from '@terrafirmagreg-team/ui/components/ui/message-scroller';
24
+ import {
25
+ getStoredName,
26
+ isOwnChatMessage,
27
+ setStoredName,
28
+ } from '@terrafirmagreg-team/chat/lib';
29
+ import type { ChatMessage, ChatTranslate } from '@terrafirmagreg-team/chat/types';
30
+ import { Hint } from '@terrafirmagreg-team/ui/components/blocks/tooltip/hint';
31
+ import {ChatMessageRow} from './message';
32
+
33
+ export function ChatBox({
34
+ messages,
35
+ t,
36
+ offline = false,
37
+ loggedIn = false,
38
+ loginRequired = false,
39
+ maxLength,
40
+ messageHistory,
41
+ showPlayerFaces = false,
42
+ anonymousNameKey = 'plugin.chatname',
43
+ loginNameKey = 'plugin.loginname',
44
+ send,
45
+ onClose,
46
+ onLogin,
47
+ renderAvatar,
48
+ unknownError = 'chat.errorUnknown',
49
+ }: {
50
+ messages: readonly ChatMessage[];
51
+ t: ChatTranslate;
52
+
53
+ offline?: boolean;
54
+ loggedIn?: boolean;
55
+ loginRequired?: boolean;
56
+ maxLength?: number;
57
+
58
+ messageHistory?: number;
59
+ showPlayerFaces?: boolean;
60
+
61
+ anonymousNameKey?: string;
62
+ loginNameKey?: string;
63
+
64
+ send?: (message: string, name?: string) => Promise<void>;
65
+ onClose?: () => void;
66
+ onLogin?: () => void;
67
+
68
+ renderAvatar?: (playerAccount: string) => ReactNode;
69
+
70
+ unknownError?: string;
71
+ }) {
72
+ const inputRef = useRef<HTMLTextAreaElement>(null);
73
+ const [enteredMessage, setEnteredMessage] = useState('');
74
+ const [sendingMessage, setSendingMessage] = useState(false);
75
+ const [sendingError, setSendingError] = useState<string | null>(null);
76
+
77
+ const [chatName, setChatName] = useState(() => getStoredName(anonymousNameKey));
78
+ const rememberChatName = (name: string) => {
79
+ setChatName(name);
80
+ setStoredName(anonymousNameKey, name);
81
+ };
82
+ const loginName = getStoredName(loginNameKey);
83
+
84
+ const shown = messageHistory
85
+ ? messages.slice(0, messageHistory).reverse()
86
+ : [...messages].reverse();
87
+
88
+ const sendingEnabled = !loginRequired || loggedIn;
89
+ const nameMissing = !loggedIn && !chatName.trim();
90
+
91
+ const sendMessage = async (e?: FormEvent) => {
92
+ e?.preventDefault();
93
+ const message = enteredMessage.trim().substring(0, maxLength);
94
+ if (!message || nameMissing) return;
95
+
96
+ if (!send) return;
97
+
98
+ setSendingMessage(true);
99
+ setSendingError(null);
100
+ try {
101
+ await send(message, loggedIn ? undefined : chatName.trim() || undefined);
102
+ setEnteredMessage('');
103
+ } catch (err) {
104
+ setSendingError(err instanceof Error ? err.message : t(unknownError));
105
+ } finally {
106
+ setSendingMessage(false);
107
+ requestAnimationFrame(() => inputRef.current?.focus());
108
+ }
109
+ };
110
+
111
+ return (
112
+ <section className="chat">
113
+ <header className="chat__header">
114
+ <p className="chat__title">{t('chat.title')}</p>
115
+ {onClose && (
116
+ <Hint label={t('common.close')}>
117
+ <button
118
+ type="button"
119
+ className="chat__close"
120
+ aria-label={t('common.close')}
121
+ onClick={onClose}>
122
+ <X className="size-4" aria-hidden="true" />
123
+ </button>
124
+ </Hint>
125
+ )}
126
+ </header>
127
+ {offline ? (
128
+ <div className="chat__offline" role="status">
129
+ <span className="chat__offline-chip">
130
+ <WifiOff className="size-3.5" aria-hidden="true" />
131
+ {t('servers.offline')}
132
+ </span>
133
+ </div>
134
+ ) : (
135
+ <MessageScrollerProvider autoScroll defaultScrollPosition="end">
136
+ <MessageScroller className="chat__scroll">
137
+ <MessageScrollerViewport role="log" aria-live="polite" aria-relevant="additions">
138
+ <MessageScrollerContent className="chat__content">
139
+ {shown.map(message => (
140
+ <MessageScrollerItem key={message.timestamp + (message.playerName ?? '')}>
141
+ <ChatMessageRow
142
+ message={message}
143
+ own={isOwnChatMessage(message, {chatName, loginName})}
144
+ avatar={
145
+ showPlayerFaces && message.playerAccount
146
+ ? renderAvatar?.(message.playerAccount)
147
+ : undefined
148
+ }
149
+ t={t}
150
+ />
151
+ </MessageScrollerItem>
152
+ ))}
153
+ {!shown.length && (
154
+ <Empty>
155
+ <EmptyHeader>
156
+ <EmptyTitle>{t('chat.title')}</EmptyTitle>
157
+ <EmptyDescription>{t('chat.noMessages')}</EmptyDescription>
158
+ </EmptyHeader>
159
+ </Empty>
160
+ )}
161
+ </MessageScrollerContent>
162
+ </MessageScrollerViewport>
163
+ <MessageScrollerButton />
164
+ </MessageScroller>
165
+ </MessageScrollerProvider>
166
+ )}
167
+ {!offline && send && sendingEnabled && (
168
+ <form className="chat__form" onSubmit={sendMessage}>
169
+ {sendingError && (
170
+ <div className="chat__error" role="alert">
171
+ <span className="chat__error-dot" aria-hidden="true" />
172
+ <span className="flex-1 min-w-0">{sendingError}</span>
173
+ </div>
174
+ )}
175
+ <InputGroup>
176
+ {!loggedIn && (
177
+ <InputGroupAddon align="block-start" className="border-b">
178
+ <User aria-hidden="true" />
179
+ <input
180
+ className="chat__name"
181
+ type="text"
182
+ value={chatName}
183
+ maxLength={24}
184
+ placeholder={t('chat.namePlaceholder')}
185
+ aria-label={t('chat.nameLabel')}
186
+ onChange={e => rememberChatName(e.target.value)}
187
+ />
188
+ </InputGroupAddon>
189
+ )}
190
+ <InputGroupTextarea
191
+ ref={inputRef}
192
+ rows={2}
193
+ value={enteredMessage}
194
+ maxLength={maxLength}
195
+ placeholder={nameMissing ? t('chat.namePlaceholder') : t('chat.placeholder')}
196
+ disabled={sendingMessage || nameMissing}
197
+ aria-label={t('chat.placeholder')}
198
+ onChange={e => setEnteredMessage(e.target.value)}
199
+ onKeyDown={e => {
200
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
201
+ if (e.key === 'Enter' && !e.shiftKey) {
202
+ e.preventDefault();
203
+ void sendMessage();
204
+ }
205
+ }}
206
+ />
207
+ <InputGroupAddon align="block-end" className="justify-end">
208
+ <InputGroupButton
209
+ type="submit"
210
+ variant="default"
211
+ size="icon-sm"
212
+ disabled={!enteredMessage.trim() || sendingMessage || nameMissing}
213
+ aria-label={t('chat.send')}>
214
+ <ArrowUp strokeWidth={2.25} aria-hidden="true" />
215
+ </InputGroupButton>
216
+ </InputGroupAddon>
217
+ </InputGroup>
218
+ </form>
219
+ )}
220
+ {send && loginRequired && !loggedIn && onLogin && (
221
+ <button type="button" className="chat__login" onClick={onLogin}>
222
+ <LogIn className="size-4" aria-hidden="true" />
223
+ {t('chat.login')}
224
+ </button>
225
+ )}
226
+ </section>
227
+ );
228
+ }
@@ -0,0 +1,10 @@
1
+ export {ChatBox} from './box';
2
+ export {ChatMessageRow} from './message';
3
+ export type {
4
+ ChatBoxSettings,
5
+ ChatMessage,
6
+ ChatMessageType,
7
+ ChatSendingSettings,
8
+ ChatTranslate,
9
+ } from '@terrafirmagreg-team/chat/types';
10
+ export {getStoredName, isOwnChatMessage, setStoredName} from '@terrafirmagreg-team/chat/lib';
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ import type {ReactNode} from 'react';
3
+ import {Bubble, BubbleContent} from '@terrafirmagreg-team/ui/components/ui/bubble';
4
+ import {Message, MessageAvatar, MessageContent, MessageHeader} from '@terrafirmagreg-team/ui/components/ui/message';
5
+ import type {ChatMessage, ChatTranslate} from '@terrafirmagreg-team/chat/types';
6
+
7
+ export function ChatMessageRow({
8
+ message,
9
+ own = false,
10
+ avatar,
11
+ t,
12
+ }: {
13
+ message: ChatMessage;
14
+ own?: boolean;
15
+ avatar?: ReactNode;
16
+ t: ChatTranslate;
17
+ }) {
18
+ const channel = message.type === 'chat' ? message.channel : undefined;
19
+
20
+ const content = (() => {
21
+ switch (message.type) {
22
+ case 'chat':
23
+ return message.message;
24
+ case 'playerjoin':
25
+ return message.playerName
26
+ ? t('chat.playerJoin', {playername: message.playerName})
27
+ : t('chat.anonymousJoin');
28
+ case 'playerleave':
29
+ return message.playerName
30
+ ? t('chat.playerQuit', {playername: message.playerName})
31
+ : t('chat.anonymousQuit');
32
+ }
33
+ })();
34
+
35
+ if (!content) return null;
36
+
37
+ if (message.type !== 'chat') {
38
+ return <p className="chat__event">{content}</p>;
39
+ }
40
+
41
+ return (
42
+ <Message align={own ? 'end' : 'start'}>
43
+ {!own && avatar && <MessageAvatar>{avatar}</MessageAvatar>}
44
+ <MessageContent>
45
+ {!own && (
46
+ <MessageHeader className="chat__meta">
47
+ {}
48
+ <span>{message.playerName || channel || '…'}</span>
49
+ {!!message.playerName && channel && <span>{channel}</span>}
50
+ </MessageHeader>
51
+ )}
52
+ <Bubble variant={own ? 'default' : 'muted'} align={own ? 'end' : 'start'}>
53
+ <BubbleContent>{content}</BubbleContent>
54
+ </Bubble>
55
+ </MessageContent>
56
+ </Message>
57
+ );
58
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.