@qoretechnologies/reqraft 0.10.27-pr.74.gc5f6ca3 → 0.10.27

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 (64) hide show
  1. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  2. package/dist/components/form/engine/CompactRow.js +8 -1
  3. package/dist/components/form/engine/CompactRow.js.map +1 -1
  4. package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
  5. package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
  6. package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
  7. package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
  8. package/dist/components/searchFilterBar/index.d.ts +2 -0
  9. package/dist/components/searchFilterBar/index.d.ts.map +1 -0
  10. package/dist/components/searchFilterBar/index.js +18 -0
  11. package/dist/components/searchFilterBar/index.js.map +1 -0
  12. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +9 -1
  13. package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -1
  14. package/dist/components/supportTicket/InterfaceReferenceTags.js +2 -2
  15. package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -1
  16. package/dist/components/supportTicket/ReferencePicker.d.ts +52 -6
  17. package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -1
  18. package/dist/components/supportTicket/ReferencePicker.js +240 -31
  19. package/dist/components/supportTicket/ReferencePicker.js.map +1 -1
  20. package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
  21. package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
  22. package/dist/components/supportTicket/attachmentAccess.js +132 -0
  23. package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
  24. package/dist/components/supportTicket/index.d.ts +2 -0
  25. package/dist/components/supportTicket/index.d.ts.map +1 -1
  26. package/dist/components/supportTicket/index.js +2 -0
  27. package/dist/components/supportTicket/index.js.map +1 -1
  28. package/dist/components/supportTicket/meta.d.ts +8 -0
  29. package/dist/components/supportTicket/meta.d.ts.map +1 -1
  30. package/dist/components/supportTicket/meta.js +37 -1
  31. package/dist/components/supportTicket/meta.js.map +1 -1
  32. package/dist/components/supportTicket/ticketModel.d.ts +102 -0
  33. package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
  34. package/dist/components/supportTicket/ticketModel.js +3 -0
  35. package/dist/components/supportTicket/ticketModel.js.map +1 -0
  36. package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -1
  37. package/dist/components/ticketReferences/TicketReferences.js +109 -78
  38. package/dist/components/ticketReferences/TicketReferences.js.map +1 -1
  39. package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
  40. package/dist/components/ticketThread/TicketThread.js +19 -2
  41. package/dist/components/ticketThread/TicketThread.js.map +1 -1
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -1
  45. package/dist/index.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/components/form/engine/CompactRow.tsx +7 -0
  48. package/src/components/form/engine/FormEngine.stories.tsx +90 -0
  49. package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
  50. package/src/components/searchFilterBar/index.ts +1 -0
  51. package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +37 -0
  52. package/src/components/supportTicket/InterfaceReferenceTags.tsx +11 -0
  53. package/src/components/supportTicket/ReferencePicker.stories.tsx +257 -34
  54. package/src/components/supportTicket/ReferencePicker.tsx +421 -95
  55. package/src/components/supportTicket/TicketConversation.stories.tsx +0 -1
  56. package/src/components/supportTicket/TicketReplyBox.stories.tsx +0 -4
  57. package/src/components/supportTicket/attachmentAccess.ts +97 -0
  58. package/src/components/supportTicket/index.ts +2 -0
  59. package/src/components/supportTicket/meta.ts +40 -0
  60. package/src/components/supportTicket/ticketModel.ts +131 -0
  61. package/src/components/ticketReferences/TicketReferences.tsx +124 -145
  62. package/src/components/ticketThread/TicketThread.stories.tsx +24 -2
  63. package/src/components/ticketThread/TicketThread.tsx +23 -3
  64. package/src/index.tsx +3 -1
@@ -5,7 +5,6 @@ import {
5
5
  ReqoreDropdown,
6
6
  ReqoreEmptyState,
7
7
  ReqoreEntityRow,
8
- ReqoreInput,
9
8
  ReqorePanel,
10
9
  ReqoreSpan,
11
10
  ReqoreSpinner,
@@ -17,6 +16,7 @@ import type { IReqoreEntityRowAction } from '@qoretechnologies/reqore/dist/compo
17
16
  import type { IReqorePanelSubAction } from '@qoretechnologies/reqore/dist/components/Panel';
18
17
  import { useCallback, useEffect, useMemo, useState } from 'react';
19
18
  import timeago from 'epoch-timeago';
19
+ import { SearchFilterBar } from '../searchFilterBar';
20
20
  import styled from 'styled-components';
21
21
  import { formatBytes, isImage } from '../../helpers/common';
22
22
  import { ImageLightbox } from '../imageLightbox';
@@ -233,23 +233,23 @@ const useImageUrls = (
233
233
  }
234
234
  let active = true;
235
235
  const created: string[] = [];
236
- Promise.allSettled(
237
- ids.map((id) => fetchAttachmentUrl(id).then((url) => ({ id, url })))
238
- ).then((results) => {
239
- if (!active) {
240
- return;
241
- }
242
- const next: Record<string, string | null> = {};
243
- results.forEach((r, i) => {
244
- if (r.status === 'fulfilled') {
245
- next[r.value.id] = r.value.url;
246
- created.push(r.value.url);
247
- } else {
248
- next[ids[i]] = null;
236
+ Promise.allSettled(ids.map((id) => fetchAttachmentUrl(id).then((url) => ({ id, url })))).then(
237
+ (results) => {
238
+ if (!active) {
239
+ return;
249
240
  }
250
- });
251
- setUrls(next);
252
- });
241
+ const next: Record<string, string | null> = {};
242
+ results.forEach((r, i) => {
243
+ if (r.status === 'fulfilled') {
244
+ next[r.value.id] = r.value.url;
245
+ created.push(r.value.url);
246
+ } else {
247
+ next[ids[i]] = null;
248
+ }
249
+ });
250
+ setUrls(next);
251
+ }
252
+ );
253
253
  return () => {
254
254
  active = false;
255
255
  created.forEach((url) => URL.revokeObjectURL(url));
@@ -269,17 +269,6 @@ const useImageUrls = (
269
269
  * scroll container. Free-floating, it was easy to lose track of — and scrolling the
270
270
  * sections took the search box away with them, which is exactly when you want it.
271
271
  */
272
- const Toolbar = styled.div`
273
- position: sticky;
274
- top: 0;
275
- z-index: 2;
276
- padding: 8px;
277
- border-radius: 8px;
278
- border: 1px solid rgba(255, 255, 255, 0.08);
279
- background: rgba(255, 255, 255, 0.04);
280
- backdrop-filter: blur(6px);
281
- `;
282
-
283
272
  /*
284
273
  * A screenshot tile: the image is the button, and the overflow menu is a sibling
285
274
  * layered over it — a <button> cannot legally contain another button, so the menu
@@ -371,19 +360,18 @@ const Screenshot = ({
371
360
  }) => (
372
361
  <ThumbTile>
373
362
  <Thumb type='button' title={att.filename} onClick={onOpen}>
374
- {url ? (
363
+ {url ?
375
364
  <img src={url} alt={att.filename} />
376
- ) : (
377
- <ReqoreSpan effect={{ opacity: 0.5, textSize: 'small' }}>
365
+ : <ReqoreSpan effect={{ opacity: 0.5, textSize: 'small' }}>
378
366
  {url === null ? att.filename : '…'}
379
367
  </ReqoreSpan>
380
- )}
368
+ }
381
369
  <ThumbCaption>{att.filename}</ThumbCaption>
382
370
  </Thumb>
383
371
  {/* One action doesn't earn a menu. With a single entry the overlay IS that
384
372
  action, so a tile and a file row offer the same thing the same way instead of
385
373
  hiding a download behind a caret on one surface and not the other. */}
386
- {menu?.length === 1 ? (
374
+ {menu?.length === 1 ?
387
375
  <ReqoreButton
388
376
  className='screenshot-menu'
389
377
  icon={menu[0].icon}
@@ -397,7 +385,7 @@ const Screenshot = ({
397
385
  menu[0].onClick?.(menu[0]);
398
386
  }}
399
387
  />
400
- ) : menu?.length ? (
388
+ : menu?.length ?
401
389
  <ReqoreDropdown
402
390
  className='screenshot-menu'
403
391
  icon='More2Line'
@@ -410,7 +398,7 @@ const Screenshot = ({
410
398
  items={menu}
411
399
  onClick={(event: React.MouseEvent<HTMLElement>) => event.stopPropagation()}
412
400
  />
413
- ) : null}
401
+ : null}
414
402
  </ThumbTile>
415
403
  );
416
404
 
@@ -468,8 +456,8 @@ export const TicketReferences = ({
468
456
  if (!onShowInChat) {
469
457
  return [];
470
458
  }
471
- return messageId
472
- ? [{ label: 'Show in chat', icon: 'Chat1Line', onClick: () => onShowInChat(messageId) }]
459
+ return messageId ?
460
+ [{ label: 'Show in chat', icon: 'Chat1Line', onClick: () => onShowInChat(messageId) }]
473
461
  : [
474
462
  {
475
463
  label: 'Show in chat',
@@ -488,17 +476,17 @@ export const TicketReferences = ({
488
476
 
489
477
  return [
490
478
  ...(primary ? [primary] : []),
491
- ...(menu.length
492
- ? [
493
- {
494
- icon: 'More2Line' as IReqoreIconName,
495
- tooltip: 'More actions',
496
- flat: true,
497
- minimal: true,
498
- actions: menu,
499
- },
500
- ]
501
- : []),
479
+ ...(menu.length ?
480
+ [
481
+ {
482
+ icon: 'More2Line' as IReqoreIconName,
483
+ tooltip: 'More actions',
484
+ flat: true,
485
+ minimal: true,
486
+ actions: menu,
487
+ },
488
+ ]
489
+ : []),
502
490
  ];
503
491
  };
504
492
 
@@ -598,10 +586,10 @@ export const TicketReferences = ({
598
586
  return allRefs
599
587
  .filter((r) => matchesText(r.interface_name, r.interface_kind))
600
588
  .sort((a, b) =>
601
- sort === 'name'
602
- ? a.interface_name.localeCompare(b.interface_name)
603
- : a.interface_kind.localeCompare(b.interface_kind) ||
604
- a.interface_name.localeCompare(b.interface_name)
589
+ sort === 'name' ?
590
+ a.interface_name.localeCompare(b.interface_name)
591
+ : a.interface_kind.localeCompare(b.interface_kind) ||
592
+ a.interface_name.localeCompare(b.interface_name)
605
593
  );
606
594
  }, [allRefs, filter, matchesText, sort]);
607
595
 
@@ -660,62 +648,53 @@ export const TicketReferences = ({
660
648
  const filterOptions = FILTER_ORDER.filter((key) => key === 'all' || counts[key] > 0);
661
649
  const nothingVisible = !visibleRefs.length && !visibleImages.length && !visibleFiles.length;
662
650
 
663
- const controlBar = showControls ? (
664
- <Toolbar>
665
- {/* the bar is a filter over the content, not content itself — at the default
666
- size the two dropdowns read as primary actions and crowd the search field */}
667
- <ReqoreControlGroup fluid wrap verticalAlign='center' gapSize='small' size='small'>
668
- <ReqoreInput
669
- icon='Search2Line'
670
- placeholder='Search files and references…'
651
+ const controlBar =
652
+ showControls ?
653
+ <SearchFilterBar
654
+ sticky
671
655
  value={search}
672
- onChange={(e) => setSearch(e.currentTarget.value)}
673
- onClearClick={() => setSearch('')}
674
- minimal
675
- fluid
676
- />
677
- <ReqoreDropdown
678
- icon={FILTER_META[filter].icon}
679
- label={FILTER_META[filter].label}
680
- minimal
681
- fixed
682
- flat
683
- items={filterOptions.map((key) => ({
684
- selected: filter === key,
685
- icon: FILTER_META[key].icon,
686
- label: key === 'all' ? FILTER_META.all.label : `${FILTER_META[key].label} (${counts[key]})`,
687
- onClick: () => setFilter(key),
688
- }))}
656
+ onChange={setSearch}
657
+ placeholder='Search files and references…'
658
+ filters={[
659
+ {
660
+ icon: FILTER_META[filter].icon,
661
+ label: FILTER_META[filter].label,
662
+ items: filterOptions.map((key) => ({
663
+ selected: filter === key,
664
+ icon: FILTER_META[key].icon,
665
+ label:
666
+ key === 'all' ?
667
+ FILTER_META.all.label
668
+ : `${FILTER_META[key].label} (${counts[key]})`,
669
+ onClick: () => setFilter(key),
670
+ })),
671
+ },
672
+ {
673
+ icon: 'ArrowUpDownLine',
674
+ label: SORT_META[sort].label,
675
+ items: SORT_ORDER.map((key) => ({
676
+ selected: sort === key,
677
+ icon: SORT_META[key].icon,
678
+ label: SORT_META[key].label,
679
+ onClick: () => setSort(key),
680
+ })),
681
+ },
682
+ ]}
689
683
  />
690
- <ReqoreDropdown
691
- icon='ArrowUpDownLine'
692
- label={SORT_META[sort].label}
693
- minimal
694
- fixed
695
- flat
696
- items={SORT_ORDER.map((key) => ({
697
- selected: sort === key,
698
- icon: SORT_META[key].icon,
699
- label: SORT_META[key].label,
700
- onClick: () => setSort(key),
701
- }))}
702
- />
703
- </ReqoreControlGroup>
704
- </Toolbar>
705
- ) : null;
684
+ : null;
706
685
 
707
686
  return (
708
687
  <ReqoreControlGroup vertical fluid gapSize='big'>
709
688
  {controlBar}
710
689
 
711
- {nothingVisible ? (
690
+ {nothingVisible ?
712
691
  <ReqoreEmptyState
713
692
  icon='Search2Line'
714
693
  title='No matching references or files'
715
694
  description={
716
- query
717
- ? `Nothing here matches “${search.trim()}”.`
718
- : 'Nothing matches the current filter.'
695
+ query ?
696
+ `Nothing here matches “${search.trim()}”.`
697
+ : 'Nothing matches the current filter.'
719
698
  }
720
699
  flat
721
700
  transparent
@@ -732,9 +711,9 @@ export const TicketReferences = ({
732
711
  </ReqoreButton>
733
712
  }
734
713
  />
735
- ) : null}
714
+ : null}
736
715
 
737
- {visibleImages.length ? (
716
+ {visibleImages.length ?
738
717
  <ReqorePanel
739
718
  label='Screenshots'
740
719
  icon='ImageLine'
@@ -752,29 +731,29 @@ export const TicketReferences = ({
752
731
  att={att}
753
732
  url={urls[att.attachment_id]}
754
733
  onOpen={() =>
755
- urls[att.attachment_id]
756
- ? setLightbox(i)
757
- : onDownloadAttachment?.(att.attachment_id, att.filename)
734
+ urls[att.attachment_id] ?
735
+ setLightbox(i)
736
+ : onDownloadAttachment?.(att.attachment_id, att.filename)
758
737
  }
759
738
  menu={[
760
- ...(onDownloadAttachment
761
- ? [
762
- {
763
- label: 'Download',
764
- icon: 'DownloadLine' as IReqoreIconName,
765
- onClick: () => onDownloadAttachment(att.attachment_id, att.filename),
766
- },
767
- ]
768
- : []),
739
+ ...(onDownloadAttachment ?
740
+ [
741
+ {
742
+ label: 'Download',
743
+ icon: 'DownloadLine' as IReqoreIconName,
744
+ onClick: () => onDownloadAttachment(att.attachment_id, att.filename),
745
+ },
746
+ ]
747
+ : []),
769
748
  ...showInChatItem(att.message_id),
770
749
  ]}
771
750
  />
772
751
  ))}
773
752
  </ReqoreColumns>
774
753
  </ReqorePanel>
775
- ) : null}
754
+ : null}
776
755
 
777
- {visibleRefs.length ? (
756
+ {visibleRefs.length ?
778
757
  <ReqorePanel
779
758
  label='Linked interfaces'
780
759
  icon='LinksLine'
@@ -808,18 +787,18 @@ export const TicketReferences = ({
808
787
  }
809
788
  flat
810
789
  tooltip={
811
- onInterfaceClick
812
- ? `Open ${reference.interface_kind} ${reference.interface_name}`
813
- : undefined
790
+ onInterfaceClick ?
791
+ `Open ${reference.interface_kind} ${reference.interface_name}`
792
+ : undefined
814
793
  }
815
794
  actions={rowActions(
816
- onInterfaceClick
817
- ? {
818
- icon: 'ArrowRightUpLine',
819
- tooltip: 'Open',
820
- onClick: () => onInterfaceClick(reference),
821
- }
822
- : undefined,
795
+ onInterfaceClick ?
796
+ {
797
+ icon: 'ArrowRightUpLine',
798
+ tooltip: 'Open',
799
+ onClick: () => onInterfaceClick(reference),
800
+ }
801
+ : undefined,
823
802
  reference.message_id
824
803
  )}
825
804
  />
@@ -827,9 +806,9 @@ export const TicketReferences = ({
827
806
  })}
828
807
  </ReqoreControlGroup>
829
808
  </ReqorePanel>
830
- ) : null}
809
+ : null}
831
810
 
832
- {visibleFiles.length ? (
811
+ {visibleFiles.length ?
833
812
  <ReqorePanel
834
813
  label='Files'
835
814
  icon='FileList3Line'
@@ -840,16 +819,16 @@ export const TicketReferences = ({
840
819
  labelSize={4}
841
820
  collapsible
842
821
  actions={
843
- visibleFiles.length > 1 && onDownloadAttachment
844
- ? [
845
- {
846
- icon: 'DownloadCloud2Line',
847
- label: 'Download all',
848
- onClick: () =>
849
- visibleFiles.forEach((f) => onDownloadAttachment(f.attachment_id, f.filename)),
850
- },
851
- ]
852
- : []
822
+ visibleFiles.length > 1 && onDownloadAttachment ?
823
+ [
824
+ {
825
+ icon: 'DownloadCloud2Line',
826
+ label: 'Download all',
827
+ onClick: () =>
828
+ visibleFiles.forEach((f) => onDownloadAttachment(f.attachment_id, f.filename)),
829
+ },
830
+ ]
831
+ : []
853
832
  }
854
833
  >
855
834
  {/* matches the interface rows above — the two lists read as one surface */}
@@ -876,13 +855,13 @@ export const TicketReferences = ({
876
855
  metadata={<ReqoreTextEffect effect={META_EFFECT}>{provenance}</ReqoreTextEffect>}
877
856
  flat
878
857
  actions={rowActions(
879
- onDownloadAttachment
880
- ? {
881
- icon: 'DownloadLine',
882
- tooltip: 'Download',
883
- onClick: () => onDownloadAttachment(f.attachment_id, f.filename),
884
- }
885
- : undefined,
858
+ onDownloadAttachment ?
859
+ {
860
+ icon: 'DownloadLine',
861
+ tooltip: 'Download',
862
+ onClick: () => onDownloadAttachment(f.attachment_id, f.filename),
863
+ }
864
+ : undefined,
886
865
  f.message_id
887
866
  )}
888
867
  />
@@ -890,9 +869,9 @@ export const TicketReferences = ({
890
869
  })}
891
870
  </ReqoreControlGroup>
892
871
  </ReqorePanel>
893
- ) : null}
872
+ : null}
894
873
 
895
- {lightbox !== null && fetchAttachmentUrl ? (
874
+ {lightbox !== null && fetchAttachmentUrl ?
896
875
  <ImageLightbox
897
876
  images={visibleImages}
898
877
  index={lightbox}
@@ -901,7 +880,7 @@ export const TicketReferences = ({
901
880
  fetchAttachmentUrl={fetchAttachmentUrl}
902
881
  onDownload={onDownloadAttachment}
903
882
  />
904
- ) : null}
883
+ : null}
905
884
  </ReqoreControlGroup>
906
885
  );
907
886
  };
@@ -329,11 +329,11 @@ export const Loading: Story = {
329
329
  docs: {
330
330
  description: {
331
331
  story:
332
- "Renders the loading state as a conversation-shaped skeleton — aria-busy and labelled 'Loading conversation', carrying no message content — rather than a spinner.",
332
+ "The FIRST load — loading with nothing to show yet — renders a conversation-shaped skeleton: aria-busy, labelled 'Loading conversation', carrying no message content, rather than a spinner. `messages` is empty deliberately; that is what makes this a first load rather than a refetch (see RefetchKeepsTheConversation).",
333
333
  },
334
334
  },
335
335
  },
336
- args: { loading: true },
336
+ args: { loading: true, messages: [] },
337
337
  async play({ canvasElement }) {
338
338
  // the loading state is a conversation-shaped skeleton, not a spinner
339
339
  const skeleton = canvasElement.querySelector('[aria-busy="true"]');
@@ -344,6 +344,28 @@ export const Loading: Story = {
344
344
  },
345
345
  };
346
346
 
347
+ export const RefetchKeepsTheConversation: Story = {
348
+ parameters: {
349
+ docs: {
350
+ description: {
351
+ story:
352
+ 'Loading with messages already on screen is a REFETCH, not a first load, and must leave the conversation alone. Every send triggers one — and where the thread is live, so does the other party sending — so blanking it here replaces a conversation the reader is in the middle of with pulsing placeholders, at the worst possible moment. The new message simply appears when the refetch resolves.',
353
+ },
354
+ },
355
+ },
356
+ args: { loading: true },
357
+ async play({ canvasElement }) {
358
+ const canvas = within(canvasElement);
359
+ // no skeleton: the thread is already showing something worth keeping
360
+ await expect(canvasElement.querySelector('[aria-busy="true"]')).toBeNull();
361
+ // ...and the existing messages are still on screen. Asserted on message bodies
362
+ // rather than author labels, which vary with `viewerRole`; getAllByText because a
363
+ // body can legitimately appear in more than one node.
364
+ await expect(canvas.getAllByText(/nightly/).length).toBeGreaterThan(0);
365
+ await expect(canvas.getAllByText(/datasource pool/).length).toBeGreaterThan(0);
366
+ },
367
+ };
368
+
347
369
  export const Empty: Story = {
348
370
  parameters: {
349
371
  docs: {
@@ -610,7 +610,12 @@ export const TicketThread = ({
610
610
  ? openingRefs
611
611
  : message.referenced_interfaces;
612
612
 
613
- if (loading) {
613
+ // Only the FIRST load gets the skeleton. A refetch while messages are already on
614
+ // screen must leave them there: every send triggers one (and, where the thread is
615
+ // live, so does the other party sending), and blanking a conversation you are
616
+ // reading — replacing it with pulsing placeholders — is the single worst moment to
617
+ // do it. The new message simply appears when the refetch resolves.
618
+ if (loading && !messages.length) {
614
619
  return <ConversationSkeleton />;
615
620
  }
616
621
 
@@ -647,8 +652,23 @@ export const TicketThread = ({
647
652
  avatar={{ icon: authorIcon(message) }}
648
653
  label={authorLabel(message, viewerRole)}
649
654
  timestamp={message.created ? <ReqoreTimeAgo time={message.created} /> : undefined}
650
- intent={message.internal ? 'warning' : undefined}
651
- customTheme={message.internal ? undefined : ACCENT}
655
+ // Own messages read as info-blue, the other party's as the purple
656
+ // accent — so the two sides of the conversation are told apart by
657
+ // colour as well as by side, and "which of these did I write?" is
658
+ // answerable at a glance. Deliberately keyed off `viewerRole`, so it
659
+ // stays correct on the staff surface too (staff replies are the blue
660
+ // ones there). Internal notes keep their warning intent and are not
661
+ // re-tinted — they are neither side of the conversation.
662
+ intent={
663
+ message.internal
664
+ ? 'warning'
665
+ : message.author_type === viewerRole
666
+ ? 'info'
667
+ : undefined
668
+ }
669
+ customTheme={
670
+ message.internal || message.author_type === viewerRole ? undefined : ACCENT
671
+ }
652
672
  flat={false}
653
673
  minimal
654
674
  >
package/src/index.tsx CHANGED
@@ -35,7 +35,7 @@ export type {
35
35
  ICurrentUser,
36
36
  ICurrentUserStore,
37
37
  } from './stores/currentUser/currentUser';
38
- export { query } from './utils/fetch';
38
+ export { fetchConfig, query } from './utils/fetch';
39
39
  export * from './utils/websocket';
40
40
  export { ReqraftLspClient } from './utils/lspClient';
41
41
  export type { IReqraftLspClientOptions } from './utils/lspClient';
@@ -60,3 +60,5 @@ export * from './components/dpqlEditor';
60
60
  export * from './components/qonsoleSmartInput';
61
61
  export * from './components/supportTicket';
62
62
  export * from './components/ticketThread';
63
+ export * from './components/ticketReferences';
64
+ export * from './components/searchFilterBar';