astryx-dracula 0.2.1 → 0.3.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/BRAND.md +30 -18
- package/README.md +17 -12
- package/USAGE.md +29 -10
- package/astryx-dracula.js +19 -13
- package/astryx-dracula.variants.d.ts +15 -0
- package/package.json +59 -17
- package/shared/auth-chrome-config.ts +18 -0
- package/shared/auth-chrome.tsx +25 -0
- package/shared/auth-copy.ts +22 -0
- package/shared/chaptered-doc-config.ts +92 -0
- package/shared/chaptered-doc.tsx +332 -0
- package/shared/chart-hues.ts +27 -0
- package/shared/chart-labels.tsx +44 -0
- package/shared/chart-legend.tsx +100 -0
- package/shared/chart-panel-style.ts +13 -0
- package/shared/data-bar.tsx +160 -0
- package/shared/gallery-image.ts +20 -0
- package/shared/login-demo.ts +7 -0
- package/shared/metric-delta.tsx +36 -0
- package/shared/order-desk.tsx +273 -0
- package/shared/revenue-chart.tsx +174 -0
- package/shared/scene-castle.tsx +322 -0
- package/shared/scene-frame.tsx +88 -0
- package/shared/scene-hues.ts +49 -0
- package/shared/scene-tile.tsx +128 -0
- package/shared/settings-data.ts +125 -0
- package/shared/settings-rows.tsx +179 -0
- package/shared/sparkline.tsx +94 -0
- package/shared/sso-icons.tsx +30 -0
- package/templates/ai-chat-landing.tsx +66 -33
- package/templates/ai-chat.tsx +36 -19
- package/templates/blank.tsx +4 -3
- package/templates/centered-hero.tsx +14 -125
- package/templates/classic-gallery.tsx +20 -66
- package/templates/contact-form.tsx +59 -39
- package/templates/dashboard-portfolio.tsx +143 -226
- package/templates/dashboard.tsx +122 -122
- package/templates/detail-page.tsx +95 -91
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +83 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +29 -19
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +52 -74
- package/templates/editor.tsx +112 -84
- package/templates/file-explorer.tsx +131 -84
- package/templates/form-two-column.tsx +61 -125
- package/templates/gallery-hero.tsx +15 -171
- package/templates/ide.tsx +22 -20
- package/templates/incident-console.tsx +104 -70
- package/templates/kanban-board.tsx +99 -42
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +37 -42
- package/templates/login-card.tsx +58 -87
- package/templates/login-split.tsx +88 -126
- package/templates/login-sso.tsx +105 -64
- package/templates/login.tsx +107 -53
- package/templates/messaging-shell.tsx +171 -144
- package/templates/mixed-gallery.tsx +29 -130
- package/templates/payment-form.tsx +368 -290
- package/templates/product-detail.tsx +39 -58
- package/templates/product-gallery.tsx +30 -41
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +135 -282
- package/templates/settings-sidebar.tsx +178 -268
- package/templates/settings.tsx +41 -14
- package/templates/shell-nav.tsx +31 -24
- package/templates/shell-side-nav.tsx +29 -20
- package/templates/shell-top-nav.tsx +80 -44
- package/templates/side-gallery.tsx +38 -94
- package/templates/table-grouped.tsx +62 -34
- package/templates/table-page-chart.tsx +23 -302
- package/templates/table-page-heatmap-status.tsx +156 -92
- package/templates/table-page-shoe-store-heatmap.tsx +18 -293
- package/templates/table-page.tsx +23 -7
- package/templates/table.tsx +23 -8
- package/templates/tech-report.template.mjs +6 -0
- package/templates/tech-report.tsx +1486 -0
- package/templates/theme-showcase.template.mjs +1 -1
- package/templates/theme-showcase.tsx +299 -237
- package/theme.css +76 -14
- package/tokens.css +425 -30
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > (LP[
|
|
3
|
+
// L > (LP[w=68] > V > Av + (V > IB*4) + IB) + (LP[w=260] > V > (H > Hd"Messages"[level=1] + IB) + TI"Jump to…" + (V > Tx"Channels"[t=label] + (List > (ListItem)*4)) + (V > Tx"Direct messages"[t=label] + (List > (ListItem)*3))) + (LC[p=0] > V > (H > Hd"design-systems"[level=2] + SD + IB) + (ChL > ChML > (ChM > ChB)*6 + ChC)) + (LP[w=340] > V > (H > Tx"Thread"[weight=semibold] + IB) + (ChL > ChML > (ChM > ChB)*3 + ChC))
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Messaging Shell — Slack-style column frame for team messaging tools.
|
|
7
|
-
*
|
|
8
|
-
* Frame (desktop, left to right):
|
|
9
|
-
* workspace rail 68px | channel sidebar 260px | message stream (fill) | thread panel 340px
|
|
10
|
-
*
|
|
11
|
-
* Container policy: dense rows, zero Cards. Channels and DMs are List/ListItem
|
|
12
|
-
* rows, presence is AvatarStatusDot/StatusDot, unread counts are the only
|
|
13
|
-
* Badge usage. The stream and thread are built on the Chat component family.
|
|
14
|
-
*
|
|
15
|
-
* Responsive contract:
|
|
16
|
-
* >1024px — full four-column frame (rail | sidebar | stream | thread)
|
|
17
|
-
* <=1024px — thread panel hidden; stream takes the reclaimed width
|
|
18
|
-
* <=768px — sidebar also hidden; rail + stream keep full width
|
|
19
|
-
*
|
|
20
|
-
* Fixtures are deterministic: fixed ISO timestamps rendered via <Timestamp>,
|
|
21
|
-
* no Date.now()/Math.random() anywhere.
|
|
6
|
+
* Messaging Shell — Slack-style column frame for team messaging tools. Frame (desktop, left to right): (Frame/responsive/container: see XLE header above.)
|
|
22
7
|
*/
|
|
23
8
|
|
|
24
9
|
import {useState, type CSSProperties} from 'react';
|
|
@@ -88,9 +73,6 @@ const styles: Record<string, CSSProperties> = {
|
|
|
88
73
|
paddingTop: 'var(--spacing-3)',
|
|
89
74
|
paddingBottom: 'var(--spacing-3)',
|
|
90
75
|
},
|
|
91
|
-
railSpacer: {
|
|
92
|
-
flex: 1,
|
|
93
|
-
},
|
|
94
76
|
sidebar: {
|
|
95
77
|
height: '100%',
|
|
96
78
|
minHeight: 0,
|
|
@@ -104,15 +86,22 @@ const styles: Record<string, CSSProperties> = {
|
|
|
104
86
|
paddingInline: 'var(--spacing-3)',
|
|
105
87
|
paddingBottom: 'var(--spacing-2)',
|
|
106
88
|
},
|
|
89
|
+
// Block-start only: the inline (8px) and bottom (12px) edges already clear
|
|
90
|
+
// Two different jobs on one object, so the two different values are not
|
|
91
|
+
// redundant. INLINE is a content gutter: it must equal sidebarHeader and
|
|
92
|
+
// sidebarSearch above it (both var(--spacing-3) = 12px) because the channel
|
|
93
|
+
// list sits directly under the search field in the same 260px start panel --
|
|
94
|
+
// at 8px the body sat 4px inside the field above it. BLOCK-START is a ring
|
|
95
|
+
// budget, not a gutter: 4px = ring offset 2 + thickness 2, so the focus ring
|
|
96
|
+
// lands inside this clip instead of on its edge. The bottom edge stays 12px
|
|
97
|
+
// because the list scrolls into it, not into a ring.
|
|
107
98
|
sidebarScroll: {
|
|
108
99
|
minHeight: 0,
|
|
109
100
|
overflowY: 'auto',
|
|
110
|
-
paddingInline: 'var(--spacing-
|
|
101
|
+
paddingInline: 'var(--spacing-3)',
|
|
102
|
+
paddingBlockStart: 'var(--spacing-1)',
|
|
111
103
|
paddingBottom: 'var(--spacing-3)',
|
|
112
104
|
},
|
|
113
|
-
sectionGap: {
|
|
114
|
-
marginTop: 'var(--spacing-4)',
|
|
115
|
-
},
|
|
116
105
|
streamColumn: {
|
|
117
106
|
height: '100%',
|
|
118
107
|
minHeight: 0,
|
|
@@ -141,7 +130,7 @@ const styles: Record<string, CSSProperties> = {
|
|
|
141
130
|
threadHeader: {
|
|
142
131
|
alignItems: 'center',
|
|
143
132
|
paddingInline: 'var(--spacing-3)',
|
|
144
|
-
paddingBlock: 'var(--spacing-
|
|
133
|
+
paddingBlock: 'var(--spacing-3)',
|
|
145
134
|
},
|
|
146
135
|
threadScroll: {
|
|
147
136
|
minHeight: 0,
|
|
@@ -213,7 +202,7 @@ const DIRECT_MESSAGES: DirectMessage[] = [
|
|
|
213
202
|
{id: 'dm-sasha', userId: 'sasha', presence: 'offline', unread: 0},
|
|
214
203
|
];
|
|
215
204
|
|
|
216
|
-
// AvatarStatusDot supports success | neutral | error (no warning).
|
|
205
|
+
// AvatarStatusDot supports success | neutral | error (no warning): busy stays error (do-not-disturb), never warning.
|
|
217
206
|
const PRESENCE_VARIANT: Record<Presence, 'success' | 'error' | 'neutral'> = {
|
|
218
207
|
online: 'success',
|
|
219
208
|
busy: 'error',
|
|
@@ -226,12 +215,18 @@ const PRESENCE_LABEL: Record<Presence, string> = {
|
|
|
226
215
|
offline: 'Offline',
|
|
227
216
|
};
|
|
228
217
|
|
|
218
|
+
/** One bubble's copy. Ids key the bubble; its position only sets the grouping. */
|
|
219
|
+
interface StreamBubble {
|
|
220
|
+
id: string;
|
|
221
|
+
text: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
229
224
|
/** One message group: consecutive bubbles from the same sender. */
|
|
230
225
|
interface StreamMessage {
|
|
231
226
|
id: string;
|
|
232
227
|
userId: string;
|
|
233
228
|
time: string;
|
|
234
|
-
bubbles:
|
|
229
|
+
bubbles: StreamBubble[];
|
|
235
230
|
}
|
|
236
231
|
|
|
237
232
|
const MESSAGES_BY_CHANNEL: Record<string, StreamMessage[]> = {
|
|
@@ -241,23 +236,40 @@ const MESSAGES_BY_CHANNEL: Record<string, StreamMessage[]> = {
|
|
|
241
236
|
userId: 'mira',
|
|
242
237
|
time: '2026-06-30T09:12:00',
|
|
243
238
|
bubbles: [
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
{
|
|
240
|
+
id: 'm1-1',
|
|
241
|
+
text: 'Morning! The Timestamp component now supports a `system_date` format, worth switching the audit log over.',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'm1-2',
|
|
245
|
+
text: 'I put the migration notes in the wiki under Decisions.',
|
|
246
|
+
},
|
|
246
247
|
],
|
|
247
248
|
},
|
|
248
249
|
{
|
|
249
250
|
id: 'm2',
|
|
250
251
|
userId: 'devon',
|
|
251
252
|
time: '2026-06-30T09:15:00',
|
|
252
|
-
bubbles: [
|
|
253
|
+
bubbles: [
|
|
254
|
+
{
|
|
255
|
+
id: 'm2-1',
|
|
256
|
+
text: 'Nice. Does that unblock the incident console timeline work?',
|
|
257
|
+
},
|
|
258
|
+
],
|
|
253
259
|
},
|
|
254
260
|
{
|
|
255
261
|
id: 'm3',
|
|
256
262
|
userId: 'you',
|
|
257
263
|
time: '2026-06-30T09:17:00',
|
|
258
264
|
bubbles: [
|
|
259
|
-
|
|
260
|
-
|
|
265
|
+
{
|
|
266
|
+
id: 'm3-1',
|
|
267
|
+
text: 'It does. I will pick that up after the template review.',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'm3-2',
|
|
271
|
+
text: 'One question on the List density defaults, will start a thread.',
|
|
272
|
+
},
|
|
261
273
|
],
|
|
262
274
|
},
|
|
263
275
|
{
|
|
@@ -265,14 +277,17 @@ const MESSAGES_BY_CHANNEL: Record<string, StreamMessage[]> = {
|
|
|
265
277
|
userId: 'sasha',
|
|
266
278
|
time: '2026-06-30T09:24:00',
|
|
267
279
|
bubbles: [
|
|
268
|
-
|
|
280
|
+
{
|
|
281
|
+
id: 'm4-1',
|
|
282
|
+
text: 'Heads up: the token sync job ran clean overnight, no drift between core and the theme packages.',
|
|
283
|
+
},
|
|
269
284
|
],
|
|
270
285
|
},
|
|
271
286
|
{
|
|
272
287
|
id: 'm5',
|
|
273
288
|
userId: 'mira',
|
|
274
289
|
time: '2026-06-30T09:26:00',
|
|
275
|
-
bubbles: ['Great
|
|
290
|
+
bubbles: [{id: 'm5-1', text: 'Great, closing out the drift task then.'}],
|
|
276
291
|
},
|
|
277
292
|
],
|
|
278
293
|
'frontend-guild': [
|
|
@@ -281,14 +296,17 @@ const MESSAGES_BY_CHANNEL: Record<string, StreamMessage[]> = {
|
|
|
281
296
|
userId: 'devon',
|
|
282
297
|
time: '2026-06-30T08:40:00',
|
|
283
298
|
bubbles: [
|
|
284
|
-
|
|
299
|
+
{
|
|
300
|
+
id: 'g1-1',
|
|
301
|
+
text: 'Guild sync moved to Thursday this week to avoid the release freeze.',
|
|
302
|
+
},
|
|
285
303
|
],
|
|
286
304
|
},
|
|
287
305
|
{
|
|
288
306
|
id: 'g2',
|
|
289
307
|
userId: 'you',
|
|
290
308
|
time: '2026-06-30T08:44:00',
|
|
291
|
-
bubbles: ['Works for me
|
|
309
|
+
bubbles: [{id: 'g2-1', text: 'Works for me, agenda doc is updated.'}],
|
|
292
310
|
},
|
|
293
311
|
],
|
|
294
312
|
};
|
|
@@ -304,7 +322,7 @@ const THREAD_ROOT: ThreadReply = {
|
|
|
304
322
|
id: 't0',
|
|
305
323
|
userId: 'you',
|
|
306
324
|
time: '2026-06-30T09:17:30',
|
|
307
|
-
text: 'One question on the List density defaults
|
|
325
|
+
text: 'One question on the List density defaults: should channel sidebars use compact or balanced? The spec shows both.',
|
|
308
326
|
};
|
|
309
327
|
|
|
310
328
|
const THREAD_REPLIES: ThreadReply[] = [
|
|
@@ -318,7 +336,7 @@ const THREAD_REPLIES: ThreadReply[] = [
|
|
|
318
336
|
id: 't2',
|
|
319
337
|
userId: 'devon',
|
|
320
338
|
time: '2026-06-30T09:21:00',
|
|
321
|
-
text: 'Agreed
|
|
339
|
+
text: 'Agreed, the sidebar rows here are a good reference implementation.',
|
|
322
340
|
},
|
|
323
341
|
];
|
|
324
342
|
|
|
@@ -343,9 +361,9 @@ function StreamMessageGroup({message}: {message: StreamMessage}) {
|
|
|
343
361
|
<ChatMessage
|
|
344
362
|
sender={isSelf ? 'user' : 'assistant'}
|
|
345
363
|
avatar={isSelf ? undefined : <Avatar name={user.name} size="md" />}>
|
|
346
|
-
{message.bubbles.map((
|
|
364
|
+
{message.bubbles.map((bubble, index) => (
|
|
347
365
|
<ChatMessageBubble
|
|
348
|
-
key={
|
|
366
|
+
key={bubble.id}
|
|
349
367
|
group={
|
|
350
368
|
message.bubbles.length === 1
|
|
351
369
|
? undefined
|
|
@@ -363,7 +381,7 @@ function StreamMessageGroup({message}: {message: StreamMessage}) {
|
|
|
363
381
|
/>
|
|
364
382
|
) : undefined
|
|
365
383
|
}>
|
|
366
|
-
{text}
|
|
384
|
+
{bubble.text}
|
|
367
385
|
</ChatMessageBubble>
|
|
368
386
|
))}
|
|
369
387
|
</ChatMessage>
|
|
@@ -400,7 +418,7 @@ export default function MessagingShell() {
|
|
|
400
418
|
|
|
401
419
|
const workspaceRail = (
|
|
402
420
|
<VStack gap={2} style={styles.rail}>
|
|
403
|
-
<Avatar name="Night
|
|
421
|
+
<Avatar name="Night watch" size="md" />
|
|
404
422
|
{RAIL_ITEMS.map(item => (
|
|
405
423
|
<IconButton
|
|
406
424
|
key={item.id}
|
|
@@ -411,7 +429,7 @@ export default function MessagingShell() {
|
|
|
411
429
|
onClick={() => {}}
|
|
412
430
|
/>
|
|
413
431
|
))}
|
|
414
|
-
<
|
|
432
|
+
<StackItem size="fill" />
|
|
415
433
|
<IconButton
|
|
416
434
|
label="Settings"
|
|
417
435
|
tooltip="Settings"
|
|
@@ -431,7 +449,7 @@ export default function MessagingShell() {
|
|
|
431
449
|
<Stack direction="vertical" style={styles.sidebar}>
|
|
432
450
|
<HStack gap={2} style={styles.sidebarHeader}>
|
|
433
451
|
<StackItem size="fill">
|
|
434
|
-
<Heading level={
|
|
452
|
+
<Heading level={1}>Messages</Heading>
|
|
435
453
|
</StackItem>
|
|
436
454
|
<IconButton
|
|
437
455
|
label="New message"
|
|
@@ -442,18 +460,23 @@ export default function MessagingShell() {
|
|
|
442
460
|
onClick={() => {}}
|
|
443
461
|
/>
|
|
444
462
|
</HStack>
|
|
445
|
-
<
|
|
463
|
+
<VStack gap={0} style={styles.sidebarSearch}>
|
|
446
464
|
<TextInput
|
|
447
465
|
label="Jump to"
|
|
448
466
|
isLabelHidden
|
|
449
467
|
size="sm"
|
|
450
|
-
placeholder="Jump to
|
|
468
|
+
placeholder="Jump to…"
|
|
451
469
|
startIcon={Search}
|
|
452
470
|
value={searchQuery}
|
|
453
471
|
onChange={setSearchQuery}
|
|
454
472
|
/>
|
|
455
|
-
</
|
|
456
|
-
<StackItem
|
|
473
|
+
</VStack>
|
|
474
|
+
<StackItem
|
|
475
|
+
size="fill"
|
|
476
|
+
style={styles.sidebarScroll}
|
|
477
|
+
role="region"
|
|
478
|
+
aria-label="Channels and direct messages"
|
|
479
|
+
tabIndex={0}>
|
|
457
480
|
<List
|
|
458
481
|
density="compact"
|
|
459
482
|
hasDividers={false}
|
|
@@ -482,42 +505,43 @@ export default function MessagingShell() {
|
|
|
482
505
|
/>
|
|
483
506
|
))}
|
|
484
507
|
</List>
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
508
|
+
{/* The section gap rides on this List's own margin: List is the row
|
|
509
|
+
container, so no wrapper element is needed. */}
|
|
510
|
+
<List
|
|
511
|
+
density="compact"
|
|
512
|
+
hasDividers={false}
|
|
513
|
+
style={styles.sectionGap}
|
|
514
|
+
header={
|
|
515
|
+
<Text type="label" size="sm" color="secondary">
|
|
516
|
+
Direct messages
|
|
517
|
+
</Text>
|
|
518
|
+
}>
|
|
519
|
+
{visibleDms.map(dm => (
|
|
520
|
+
<ListItem
|
|
521
|
+
key={dm.id}
|
|
522
|
+
label={USERS[dm.userId].name}
|
|
523
|
+
isSelected={selectedDmId === dm.id}
|
|
524
|
+
onClick={() => setSelectedDmId(dm.id)}
|
|
525
|
+
startContent={
|
|
526
|
+
<Avatar
|
|
527
|
+
name={USERS[dm.userId].name}
|
|
528
|
+
size="sm"
|
|
529
|
+
status={
|
|
530
|
+
<AvatarStatusDot
|
|
531
|
+
variant={PRESENCE_VARIANT[dm.presence]}
|
|
532
|
+
label={PRESENCE_LABEL[dm.presence]}
|
|
533
|
+
/>
|
|
534
|
+
}
|
|
535
|
+
/>
|
|
536
|
+
}
|
|
537
|
+
endContent={
|
|
538
|
+
dm.unread > 0 ? (
|
|
539
|
+
<Badge label={String(dm.unread)} variant="neutral" />
|
|
540
|
+
) : undefined
|
|
541
|
+
}
|
|
542
|
+
/>
|
|
543
|
+
))}
|
|
544
|
+
</List>
|
|
521
545
|
</StackItem>
|
|
522
546
|
</Stack>
|
|
523
547
|
);
|
|
@@ -526,9 +550,9 @@ export default function MessagingShell() {
|
|
|
526
550
|
<Stack direction="vertical" style={styles.streamColumn}>
|
|
527
551
|
<HStack gap={3} style={styles.streamHeader}>
|
|
528
552
|
<Icon icon={Hash} size="sm" color="secondary" />
|
|
529
|
-
<Heading level={
|
|
553
|
+
<Heading level={2}>{selectedChannel.name}</Heading>
|
|
530
554
|
<StackItem size="fill" style={styles.streamTopic}>
|
|
531
|
-
<Text type="
|
|
555
|
+
<Text type="body" color="secondary" maxLines={1}>
|
|
532
556
|
{selectedChannel.topic}
|
|
533
557
|
</Text>
|
|
534
558
|
</StackItem>
|
|
@@ -544,36 +568,35 @@ export default function MessagingShell() {
|
|
|
544
568
|
</HStack>
|
|
545
569
|
<Divider />
|
|
546
570
|
<StackItem size="fill" style={styles.chatArea}>
|
|
547
|
-
<
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
</div>
|
|
571
|
+
<ChatLayout
|
|
572
|
+
style={styles.chatFill}
|
|
573
|
+
composer={
|
|
574
|
+
<ChatComposer
|
|
575
|
+
placeholder={`Message #${selectedChannel.name}`}
|
|
576
|
+
onSubmit={() => {}}
|
|
577
|
+
/>
|
|
578
|
+
}
|
|
579
|
+
emptyState={
|
|
580
|
+
<EmptyState
|
|
581
|
+
icon={<Icon icon={Inbox} size="lg" color="secondary" />}
|
|
582
|
+
title="No messages yet"
|
|
583
|
+
description="Start the conversation. Messages posted here are visible to the whole channel."
|
|
584
|
+
/>
|
|
585
|
+
}>
|
|
586
|
+
{messages.length > 0 ? (
|
|
587
|
+
<ChatMessageList density="balanced">
|
|
588
|
+
<ChatSystemMessage variant="divider">
|
|
589
|
+
Tuesday, June 30
|
|
590
|
+
</ChatSystemMessage>
|
|
591
|
+
<ChatSystemMessage>
|
|
592
|
+
Sasha Ortiz joined #{selectedChannel.name}
|
|
593
|
+
</ChatSystemMessage>
|
|
594
|
+
{messages.map(message => (
|
|
595
|
+
<StreamMessageGroup key={message.id} message={message} />
|
|
596
|
+
))}
|
|
597
|
+
</ChatMessageList>
|
|
598
|
+
) : null}
|
|
599
|
+
</ChatLayout>
|
|
577
600
|
</StackItem>
|
|
578
601
|
</Stack>
|
|
579
602
|
);
|
|
@@ -599,7 +622,12 @@ export default function MessagingShell() {
|
|
|
599
622
|
/>
|
|
600
623
|
</HStack>
|
|
601
624
|
<Divider />
|
|
602
|
-
<StackItem
|
|
625
|
+
<StackItem
|
|
626
|
+
size="fill"
|
|
627
|
+
style={styles.threadScroll}
|
|
628
|
+
role="region"
|
|
629
|
+
aria-label="Thread messages"
|
|
630
|
+
tabIndex={0}>
|
|
603
631
|
<ChatMessageList density="compact">
|
|
604
632
|
<ChatMessage
|
|
605
633
|
sender="assistant"
|
|
@@ -637,41 +665,40 @@ export default function MessagingShell() {
|
|
|
637
665
|
))}
|
|
638
666
|
</ChatMessageList>
|
|
639
667
|
</StackItem>
|
|
640
|
-
<
|
|
668
|
+
<VStack gap={0} style={styles.threadComposer}>
|
|
641
669
|
<ChatComposer
|
|
642
670
|
density="compact"
|
|
643
|
-
placeholder="Reply in thread
|
|
671
|
+
placeholder="Reply in thread…"
|
|
644
672
|
onSubmit={() => {}}
|
|
645
673
|
/>
|
|
646
|
-
</
|
|
674
|
+
</VStack>
|
|
647
675
|
</Stack>
|
|
648
676
|
);
|
|
649
677
|
|
|
650
678
|
return (
|
|
651
|
-
<
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
679
|
+
<Layout
|
|
680
|
+
height="fill"
|
|
681
|
+
style={styles.root}
|
|
682
|
+
start={
|
|
683
|
+
<>
|
|
684
|
+
<LayoutPanel width={68} padding={0}>
|
|
685
|
+
{workspaceRail}
|
|
686
|
+
</LayoutPanel>
|
|
687
|
+
{!isSidebarHidden && (
|
|
688
|
+
<LayoutPanel width={260} padding={0}>
|
|
689
|
+
{channelSidebar}
|
|
658
690
|
</LayoutPanel>
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
) : undefined
|
|
672
|
-
}
|
|
673
|
-
content={<LayoutContent padding={0}>{messageStream}</LayoutContent>}
|
|
674
|
-
/>
|
|
675
|
-
</div>
|
|
691
|
+
)}
|
|
692
|
+
</>
|
|
693
|
+
}
|
|
694
|
+
end={
|
|
695
|
+
showThreadPanel ? (
|
|
696
|
+
<LayoutPanel width={340} padding={0}>
|
|
697
|
+
{threadPanel}
|
|
698
|
+
</LayoutPanel>
|
|
699
|
+
) : undefined
|
|
700
|
+
}
|
|
701
|
+
content={<LayoutContent padding={0}>{messageStream}</LayoutContent>}
|
|
702
|
+
/>
|
|
676
703
|
);
|
|
677
704
|
}
|