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