@sybilion/uilib 1.3.43 → 1.3.46

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 (56) hide show
  1. package/assets/logo.svg +1 -1
  2. package/dist/esm/components/ui/Chart/Chart.styl.js +1 -1
  3. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +7 -1
  4. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +7 -5
  5. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +1 -1
  6. package/dist/esm/components/ui/Chat/ChatPrompt/ChatPromptComposer.js +8 -4
  7. package/dist/esm/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.js +67 -0
  8. package/dist/esm/components/ui/Chat/ChatPrompt/useChatPromptEditor.js +24 -17
  9. package/dist/esm/components/ui/Logo/Logo.styl.js +1 -1
  10. package/dist/esm/components/ui/Logo/logo.svg.js +1 -1
  11. package/dist/esm/components/ui/Page/PageFooter/PageFooter.js +3 -2
  12. package/dist/esm/components/ui/Page/PageHeader/PageHeader.js +1 -1
  13. package/dist/esm/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.js +23 -1
  14. package/dist/esm/components/widgets/DriverCard/DriverCard.js +1 -1
  15. package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +1 -0
  16. package/dist/esm/components/widgets/PerformanceChart/PerformanceTable.js +1 -0
  17. package/dist/esm/index.js +1 -0
  18. package/dist/esm/tiptap/slash-mention/SlashSuggestionList.styl.js +2 -2
  19. package/dist/esm/tiptap/slash-mention/createSlashMentionExtension.js +45 -28
  20. package/dist/esm/types/src/components/ui/Chat/Chat.d.ts +1 -2
  21. package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +1 -1
  22. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +3 -1
  23. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.d.ts +3 -3
  24. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.types.d.ts +2 -0
  25. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.d.ts +35 -0
  26. package/dist/esm/types/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.d.ts +0 -2
  27. package/dist/esm/types/src/components/ui/Chat/index.d.ts +3 -0
  28. package/dist/esm/types/src/components/ui/Page/PageFooter/PageFooter.d.ts +1 -2
  29. package/dist/esm/types/src/docs/pages/ChatSheetPage.d.ts +1 -0
  30. package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +7 -2
  31. package/package.json +1 -1
  32. package/src/components/ui/Chart/Chart.styl +2 -1
  33. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +8 -1
  34. package/src/components/ui/Chat/Chat.types.ts +1 -1
  35. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +26 -1
  36. package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +86 -65
  37. package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.tsx +31 -13
  38. package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.types.ts +11 -0
  39. package/src/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.ts +122 -0
  40. package/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.ts +34 -21
  41. package/src/components/ui/Chat/index.ts +9 -0
  42. package/src/components/ui/Logo/Logo.styl +1 -0
  43. package/src/components/ui/Logo/logo.svg +1 -1
  44. package/src/components/ui/Page/PageFooter/PageFooter.tsx +2 -3
  45. package/src/components/ui/Page/PageHeader/PageHeader.tsx +1 -1
  46. package/src/components/ui/TextWithDeferTooltip/TextWithDeferTooltip.tsx +35 -4
  47. package/src/components/widgets/DriverCard/DriverCard.tsx +5 -1
  48. package/src/docs/DocsShell.tsx +1 -6
  49. package/src/docs/pages/ChatSheetPage.tsx +61 -0
  50. package/src/docs/pages/ChatSlashCommandsPage.tsx +46 -120
  51. package/src/docs/pages/TooltipPage.tsx +0 -31
  52. package/src/docs/registry.ts +6 -0
  53. package/src/tiptap/slash-mention/SlashSuggestionList.styl +4 -0
  54. package/src/tiptap/slash-mention/SlashSuggestionList.styl.d.ts +1 -0
  55. package/src/tiptap/slash-mention/createSlashMentionExtension.ts +46 -27
  56. package/src/tiptap/slash-mention/types.ts +7 -2
package/assets/logo.svg CHANGED
@@ -1,3 +1,3 @@
1
1
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
2
- <path fill="#C259FF" d="M13.43 24.023h-2.836v-2.836h2.836zm-2.835-2.844H5.63v-2.74h4.965Zm10.527-2.74h-2.726v2.74h-4.965v-2.74h4.952v-4.965h2.74zm-15.5 0h-2.74v-4.965h2.74ZM2.87 13.462H.034v-2.836H2.87Zm21.096 0H21.13v-2.836h2.836zM5.617 10.635h-2.74V5.669h2.74zm15.505 0h-2.74V5.669h2.74zM10.588 2.927v2.74H5.623v-2.74Zm2.842 0h4.96v2.74h-4.966v-2.74h-2.83V.09h2.836z" style="stroke-width:.13252" />
2
+ <path fill="currentColor" d="M13.43 24.023h-2.836v-2.836h2.836zm-2.835-2.844H5.63v-2.74h4.965Zm10.527-2.74h-2.726v2.74h-4.965v-2.74h4.952v-4.965h2.74zm-15.5 0h-2.74v-4.965h2.74ZM2.87 13.462H.034v-2.836H2.87Zm21.096 0H21.13v-2.836h2.836zM5.617 10.635h-2.74V5.669h2.74zm15.505 0h-2.74V5.669h2.74zM10.588 2.927v2.74H5.623v-2.74Zm2.842 0h4.96v2.74h-4.966v-2.74h-2.83V.09h2.836z" style="stroke-width:.13252" />
3
3
  </svg>
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.Chart_chartContainer__--q1l{aspect-ratio:16/9;display:flex;font-size:.75rem;justify-content:center;line-height:1rem;max-width:100%;touch-action:none;width:100%}.Chart_chartContainer__--q1l .recharts-cartesian-axis-tick text{fill:var(--muted-foreground)}.Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--border)}.dark .Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--sb-slate-900)}.Chart_chartContainer__--q1l .recharts-curve.recharts-tooltip-cursor,.Chart_chartContainer__--q1l .recharts-polar-grid [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-radial-bar-background-sector,.Chart_chartContainer__--q1l .recharts-rectangle.recharts-tooltip-cursor{fill:var(--muted)}.Chart_chartContainer__--q1l .recharts-reference-line [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-dot[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-layer,.Chart_chartContainer__--q1l .recharts-sector{outline:none}.Chart_chartContainer__--q1l .recharts-sector[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-surface{outline:none}.Chart_chartContainer__--q1l .recharts-wrapper{position:relative}.Chart_chartContainer__--q1l .recharts-surface{position:relative;z-index:1}.Chart_chartContainer__--q1l .recharts-tooltip-wrapper{box-sizing:border-box;max-width:90%;z-index:3!important}.Chart_chartContainer__--q1l .recharts-active-dot{z-index:3}.Chart_chartGrid__t52WF{stroke-width:.6}.Chart_tooltipContainer__6tc0q{align-items:start;box-sizing:border-box;display:grid;max-width:min(500px,90vw);min-width:0;overflow-wrap:break-word;width:100%;word-break:break-word;grid-gap:.375rem;backdrop-filter:blur(10px);background-color:color-mix(in srgb,var(--background) 50%,transparent);border:1px solid var(--border)/.5;border-radius:.5rem;box-shadow:0 10px 10px -5px rgba(0,0,0,.2),0 0 1px 0 var(--muted-foreground);font-size:.75rem;gap:.375rem;line-height:1rem;opacity:0;padding:.375rem .625rem;transition:opacity .5s ease-out}.dark .Chart_tooltipContainer__6tc0q{box-shadow:0 0 1px 0 var(--sb-slate-700),0 10px 10px -5px rgba(0,0,0,.7)}.Chart_chartContainer__--q1l:hover .Chart_tooltipContainer__6tc0q{opacity:1;transition-duration:.5s}.Chart_tooltipItem__j8I9T{align-items:stretch;display:flex;flex-wrap:wrap;gap:.5rem;width:100%}.Chart_tooltipItem__j8I9T>svg{color:var(--muted-foreground);height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp{background-color:var(--color-bg);border-color:var(--color-border);border-radius:2px;border-width:1px;flex-shrink:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dot__MWcmW{height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-line__MO3ul{width:.25rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN{background-color:transparent;border-style:dashed;border-width:1.5px;width:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN.Chart_nested__7EWWk{margin-bottom:.125rem;margin-top:.125rem}.Chart_tooltipContent__M3R-W{display:flex;flex:1 1 0%;justify-content:space-between;line-height:1;min-width:0}.Chart_tooltipLabel__zMpjZ{display:grid;min-width:0;grid-gap:.375rem;gap:.375rem}.Chart_tooltipLabelText__45osJ{display:-webkit-box;-webkit-line-clamp:3;max-width:100%;min-width:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.Chart_tooltipValue__vTQxU{color:var(--foreground);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:500;margin-left:var(--p-3);white-space:nowrap}.Chart_legendContainer__u1J3U{align-items:center;display:flex;gap:1rem;justify-content:center}.Chart_legendItem__0CSyC{align-items:center;display:flex;gap:.375rem}.Chart_legendItem__0CSyC>svg{color:var(--muted-foreground);height:.75rem;width:.75rem}.Chart_legendIndicator__erzzP{border-radius:2px;flex-shrink:0;height:.5rem;width:.5rem}.chart-line-blinking path{animation:chart-line-blink 1s ease-in-out infinite;animation-direction:alternate}@keyframes Chart_chart-line-blink__4EI-g{0%{opacity:.5}to{opacity:1}}";
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.Chart_chartContainer__--q1l{aspect-ratio:16/9;display:flex;font-size:.75rem;justify-content:center;line-height:1rem;max-width:100%;touch-action:none;width:100%}.Chart_chartContainer__--q1l .recharts-cartesian-axis-tick text{fill:var(--muted-foreground)}.Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--border)}.dark .Chart_chartContainer__--q1l .recharts-cartesian-grid line[stroke=\"#ccc\"]{stroke:var(--sb-slate-900)}.Chart_chartContainer__--q1l .recharts-curve.recharts-tooltip-cursor,.Chart_chartContainer__--q1l .recharts-polar-grid [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-radial-bar-background-sector,.Chart_chartContainer__--q1l .recharts-rectangle.recharts-tooltip-cursor{fill:var(--muted)}.Chart_chartContainer__--q1l .recharts-reference-line [stroke=\"#ccc\"]{stroke:var(--border)}.Chart_chartContainer__--q1l .recharts-dot[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-layer,.Chart_chartContainer__--q1l .recharts-sector{outline:none}.Chart_chartContainer__--q1l .recharts-sector[stroke=\"#fff\"]{stroke:transparent}.Chart_chartContainer__--q1l .recharts-surface{outline:none}.Chart_chartContainer__--q1l .recharts-wrapper{position:relative}.Chart_chartContainer__--q1l .recharts-surface{position:relative;z-index:1}.Chart_chartContainer__--q1l .recharts-tooltip-wrapper{box-sizing:border-box;max-width:90%;z-index:3!important}.Chart_chartContainer__--q1l .recharts-active-dot{z-index:3}.Chart_chartGrid__t52WF{stroke-width:.6}.Chart_tooltipContainer__6tc0q{align-items:start;box-sizing:border-box;display:grid;max-width:min(500px,90vw);min-width:0;overflow-wrap:break-word;width:100%;word-break:break-word;grid-gap:.375rem;backdrop-filter:blur(10px);background-color:color-mix(in srgb,var(--background) 70%,transparent);border:1px solid var(--border)/.5;border-radius:.5rem;box-shadow:0 10px 10px -5px rgba(0,0,0,.2),0 0 1px 0 var(--muted-foreground);font-size:.75rem;gap:.375rem;line-height:1rem;opacity:0;padding:.375rem .625rem;transition:opacity .5s ease-out}.dark .Chart_tooltipContainer__6tc0q{background-color:color-mix(in srgb,var(--background) 50%,transparent);box-shadow:0 0 1px 0 var(--sb-slate-700),0 10px 10px -5px rgba(0,0,0,.7)}.Chart_chartContainer__--q1l:hover .Chart_tooltipContainer__6tc0q{opacity:1;transition-duration:.5s}.Chart_tooltipItem__j8I9T{align-items:stretch;display:flex;flex-wrap:wrap;gap:.5rem;width:100%}.Chart_tooltipItem__j8I9T>svg{color:var(--muted-foreground);height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp{background-color:var(--color-bg);border-color:var(--color-border);border-radius:2px;border-width:1px;flex-shrink:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dot__MWcmW{height:.625rem;width:.625rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-line__MO3ul{width:.25rem}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN{background-color:transparent;border-style:dashed;border-width:1.5px;width:0}.Chart_tooltipIndicator__Z-JWp.Chart_indicator-dashed__2LqIN.Chart_nested__7EWWk{margin-bottom:.125rem;margin-top:.125rem}.Chart_tooltipContent__M3R-W{display:flex;flex:1 1 0%;justify-content:space-between;line-height:1;min-width:0}.Chart_tooltipLabel__zMpjZ{display:grid;min-width:0;grid-gap:.375rem;gap:.375rem}.Chart_tooltipLabelText__45osJ{display:-webkit-box;-webkit-line-clamp:3;max-width:100%;min-width:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.Chart_tooltipValue__vTQxU{color:var(--foreground);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:\"tnum\";font-variant-numeric:tabular-nums;font-weight:500;margin-left:var(--p-3);white-space:nowrap}.Chart_legendContainer__u1J3U{align-items:center;display:flex;gap:1rem;justify-content:center}.Chart_legendItem__0CSyC{align-items:center;display:flex;gap:.375rem}.Chart_legendItem__0CSyC>svg{color:var(--muted-foreground);height:.75rem;width:.75rem}.Chart_legendIndicator__erzzP{border-radius:2px;flex-shrink:0;height:.5rem;width:.5rem}.chart-line-blinking path{animation:chart-line-blink 1s ease-in-out infinite;animation-direction:alternate}@keyframes Chart_chart-line-blink__4EI-g{0%{opacity:.5}to{opacity:1}}";
4
4
  var S = {"chartContainer":"Chart_chartContainer__--q1l","chartGrid":"Chart_chartGrid__t52WF","tooltipContainer":"Chart_tooltipContainer__6tc0q","tooltipItem":"Chart_tooltipItem__j8I9T","tooltipIndicator":"Chart_tooltipIndicator__Z-JWp","indicator-dot":"Chart_indicator-dot__MWcmW","indicator-line":"Chart_indicator-line__MO3ul","indicator-dashed":"Chart_indicator-dashed__2LqIN","nested":"Chart_nested__7EWWk","tooltipContent":"Chart_tooltipContent__M3R-W","tooltipLabel":"Chart_tooltipLabel__zMpjZ","tooltipLabelText":"Chart_tooltipLabelText__45osJ","tooltipValue":"Chart_tooltipValue__vTQxU","legendContainer":"Chart_legendContainer__u1J3U","legendItem":"Chart_legendItem__0CSyC","legendIndicator":"Chart_legendIndicator__erzzP","chart-line-blink":"Chart_chart-line-blink__4EI-g"};
5
5
  styleInject(css_248z);
6
6
 
@@ -42,7 +42,13 @@ function ChartAreaInteractive({ className, chartContainerClassName, legendClassN
42
42
  const timeFilteredChartData = useMemo(() => {
43
43
  const raw = selectedAnalysisId ?? selectedForecast?.id ?? null;
44
44
  const anchorId = raw == null ? null : typeof raw === 'number' ? raw : Number(raw);
45
- const opts = anchorId != null && Number.isFinite(anchorId)
45
+ const anchorForecastLoaded = anchorId != null &&
46
+ Number.isFinite(anchorId) &&
47
+ chartData.some(row => {
48
+ const value = row[`forecast_${anchorId}`];
49
+ return typeof value === 'number' && Number.isFinite(value);
50
+ });
51
+ const opts = anchorId != null && Number.isFinite(anchorId) && anchorForecastLoaded
46
52
  ? { endDateAnchorAnalysisId: anchorId }
47
53
  : undefined;
48
54
  return filterDataForTimeRange(chartData, timeRange, opts);
@@ -1,15 +1,16 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import cn from 'classnames';
3
- import { useRef, useCallback } from 'react';
3
+ import { forwardRef, useRef, useImperativeHandle, useCallback } from 'react';
4
4
  import S from './ChatPrompt.styl.js';
5
5
  import { ChatPromptAttachments } from './ChatPromptAttachments.js';
6
6
  import { ChatPromptComposer } from './ChatPromptComposer.js';
7
+ import { createChatPromptComposerHandle } from './chatPromptComposerInsert.js';
7
8
  import { useChatPromptEditor } from './useChatPromptEditor.js';
8
9
 
9
- function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, onSlashItemCommand, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
10
+ const ChatPrompt = forwardRef(function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, onSlashItemCommand, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }, ref) {
10
11
  const attachmentsCount = attachments.length;
11
12
  const emitSubmitRef = useRef(() => { });
12
- const { editor, trimmedMessage, resetAfterSend, handleComposerKeyDown } = useChatPromptEditor({
13
+ const { editor, trimmedMessage, resetAfterSend } = useChatPromptEditor({
13
14
  disabled,
14
15
  placeholder,
15
16
  slashCommandItems,
@@ -18,6 +19,7 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
18
19
  attachmentsCount,
19
20
  onEnterSubmit: () => emitSubmitRef.current(),
20
21
  });
22
+ useImperativeHandle(ref, () => (editor ? createChatPromptComposerHandle(editor) : null), [editor]);
21
23
  const emitSubmitAndClear = useCallback(() => {
22
24
  if (!editor)
23
25
  return;
@@ -42,7 +44,7 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
42
44
  if (!editor) {
43
45
  return null;
44
46
  }
45
- return (jsxs("form", { onSubmit: handleSubmitForm, className: cn(S.root, className), children: [jsx(ChatPromptAttachments, { attachments: attachments, onRemove: index => onRemoveAttachment?.(index), disabled: disabled }), jsx(ChatPromptComposer, { editor: editor, disabled: disabled, trimmedMessage: trimmedMessage, attachments: attachments, attachmentAccept: attachmentAccept, onAttachmentFiles: onAttachmentFiles, onComposerKeyDown: handleComposerKeyDown }), footer] }));
46
- }
47
+ return (jsxs("form", { onSubmit: handleSubmitForm, className: cn(S.root, className), children: [jsx(ChatPromptAttachments, { attachments: attachments, onRemove: index => onRemoveAttachment?.(index), disabled: disabled }), jsx(ChatPromptComposer, { editor: editor, disabled: disabled, trimmedMessage: trimmedMessage, attachments: attachments, attachmentAccept: attachmentAccept, onAttachmentFiles: onAttachmentFiles }), footer] }));
48
+ });
47
49
 
48
50
  export { ChatPrompt };
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.ChatPrompt_fileInput__xdgPn{display:none}.ChatPrompt_attachButton__gi-qF{align-self:flex-end;flex-shrink:0}.ChatPrompt_editorWrap__Q7gat{align-self:stretch;flex:1;min-width:0}.ChatPrompt_editorMount__Phh4D{background:transparent;border:none;border-radius:0!important;box-shadow:none!important;display:flex;flex:1;flex-direction:column;max-height:200px;min-height:40px;min-width:0;padding:0!important}.ChatPrompt_editorMount__Phh4D:focus-within{box-shadow:none!important}.ChatPrompt_editorMount__Phh4D .ProseMirror{border:none!important;box-shadow:none!important;flex:1;margin:0;max-height:200px!important;min-height:40px!important;outline:none!important;overflow-x:hidden!important;overflow-y:auto!important;padding:var(--p-2) 0 0!important;resize:none!important;white-space:pre-wrap;word-break:break-word}.ChatPrompt_editorMount__Phh4D .ProseMirror p.is-empty:before{color:var(--muted-foreground);content:attr(data-placeholder);float:left;height:0;pointer-events:none}.ChatPrompt_submitColumn__0rY1R{align-items:center;display:flex;flex-direction:column;flex-shrink:0;justify-content:flex-end}.ChatPrompt_submitColumn__0rY1R>button:focus{box-shadow:0 0 0 2px var(--brand-color-500)!important}.ChatPrompt_submitColumn__0rY1R>button:first-child{border:none;position:relative;transition:all .2s}.ChatPrompt_submitColumn__0rY1R>button:first-child:focus{transform:scale(1.2)}.ChatPrompt_submitColumn__0rY1R>button:first-child:before{bottom:-100%;content:\"\";left:-100%;position:absolute;right:-100%;top:-100%}.ChatPrompt_attachments__KG-fG{display:flex;flex-wrap:wrap;gap:var(--p-2);margin-bottom:var(--p-2)}.ChatPrompt_attachmentItem__QJk7J{flex:1 1 300px;max-width:300px;min-width:0}";
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.ChatPrompt_fileInput__xdgPn{display:none}.ChatPrompt_attachButton__gi-qF{align-self:flex-end;flex-shrink:0}.ChatPrompt_editorWrap__Q7gat{align-self:stretch;flex:1;min-width:0}.ChatPrompt_editorMount__Phh4D{background:transparent;border:none;border-radius:0!important;box-shadow:none!important;display:flex;flex:1;flex-direction:column;max-height:200px;min-height:40px;min-width:0;padding:0!important}.ChatPrompt_editorMount__Phh4D:focus-within{box-shadow:none!important}.ChatPrompt_editorMount__Phh4D .ProseMirror{border:none!important;box-shadow:none!important;flex:1;margin:0;max-height:200px!important;min-height:40px!important;outline:none!important;overflow-x:hidden!important;overflow-y:auto!important;padding:var(--p-2) 0 0!important;resize:none!important;white-space:pre-wrap;word-break:break-word}.ChatPrompt_editorMount__Phh4D .ProseMirror p.is-empty.is-editor-empty:before{color:var(--muted-foreground);content:attr(data-placeholder);float:left;height:0;pointer-events:none}.ChatPrompt_editorMount__Phh4D .ProseMirror .chat-prompt-command-chip,.ChatPrompt_editorMount__Phh4D .ProseMirror .slash-mention{align-items:center;background:var(--muted);border-radius:var(--radius-sm);display:inline-flex;font-size:.875em;line-height:1.4;padding:0 var(--p-1);-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:baseline;white-space:nowrap}.ChatPrompt_editorMount__Phh4D .ProseMirror .chat-prompt-command-chip{color:var(--foreground)}.ChatPrompt_submitColumn__0rY1R{align-items:center;display:flex;flex-direction:column;flex-shrink:0;justify-content:flex-end}.ChatPrompt_submitColumn__0rY1R>button:focus{box-shadow:0 0 0 2px var(--brand-color-500)!important}.ChatPrompt_submitColumn__0rY1R>button:first-child{border:none;position:relative;transition:all .2s}.ChatPrompt_submitColumn__0rY1R>button:first-child:focus{transform:scale(1.2)}.ChatPrompt_submitColumn__0rY1R>button:first-child:before{bottom:-100%;content:\"\";left:-100%;position:absolute;right:-100%;top:-100%}.ChatPrompt_attachments__KG-fG{display:flex;flex-wrap:wrap;gap:var(--p-2);margin-bottom:var(--p-2)}.ChatPrompt_attachmentItem__QJk7J{flex:1 1 300px;max-width:300px;min-width:0}";
4
4
  var S = {"root":"ChatPrompt_root__5G5bq","composer":"ChatPrompt_composer__H3c3N","fileInput":"ChatPrompt_fileInput__xdgPn","attachButton":"ChatPrompt_attachButton__gi-qF","editorWrap":"ChatPrompt_editorWrap__Q7gat","editorMount":"ChatPrompt_editorMount__Phh4D","submitColumn":"ChatPrompt_submitColumn__0rY1R","attachments":"ChatPrompt_attachments__KG-fG","attachmentItem":"ChatPrompt_attachmentItem__QJk7J"};
5
5
  styleInject(css_248z);
6
6
 
@@ -1,13 +1,17 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useRef, useCallback } from 'react';
2
+ import { forwardRef, useRef, useImperativeHandle, useCallback } from 'react';
3
3
  import { EditorContent } from '@tiptap/react';
4
4
  import { PaperclipIcon, SendHorizontalIcon } from 'lucide-react';
5
5
  import { Button } from '../../Button/Button.js';
6
6
  import S from './ChatPrompt.styl.js';
7
+ import { createChatPromptComposerHandle } from './chatPromptComposerInsert.js';
7
8
 
8
- function ChatPromptComposer({ editor, disabled, trimmedMessage, attachments, attachmentAccept, onAttachmentFiles, onComposerKeyDown, }) {
9
+ const ChatPromptComposer = forwardRef(function ChatPromptComposer({ editor, disabled, trimmedMessage, attachments, attachmentAccept, onAttachmentFiles, }, ref) {
9
10
  const fileInputRef = useRef(null);
10
11
  const showAttachButton = Boolean(attachmentAccept && onAttachmentFiles);
12
+ useImperativeHandle(ref, () => createChatPromptComposerHandle(editor), [
13
+ editor,
14
+ ]);
11
15
  const handleFileInputChange = useCallback((e) => {
12
16
  const files = Array.from(e.target.files ?? []);
13
17
  e.target.value = '';
@@ -19,7 +23,7 @@ function ChatPromptComposer({ editor, disabled, trimmedMessage, attachments, att
19
23
  return (jsxs("div", { className: S.composer, children: [showAttachButton ? (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", accept: attachmentAccept, multiple: true, className: S.fileInput, disabled: disabled, onChange: handleFileInputChange }), jsx(Button, { type: "button", variant: "ghost", icon: true, size: "sm", className: S.attachButton, "aria-label": "Attach file", disabled: disabled, onClick: e => {
20
24
  e.preventDefault();
21
25
  fileInputRef.current?.click();
22
- }, children: jsx(PaperclipIcon, { size: 16 }) })] })) : null, jsx("div", { className: S.editorWrap, onKeyDown: onComposerKeyDown, children: jsx(EditorContent, { editor: editor, className: S.editorMount }) }), jsx("div", { className: S.submitColumn, children: jsx(Button, { type: "submit", size: "sm", disabled: disabled || !canSubmit, onMouseDown: e => e.preventDefault(), children: jsx(SendHorizontalIcon, { size: 16 }) }) })] }));
23
- }
26
+ }, children: jsx(PaperclipIcon, { size: 16 }) })] })) : null, jsx("div", { className: S.editorWrap, children: jsx(EditorContent, { editor: editor, className: S.editorMount }) }), jsx("div", { className: S.submitColumn, children: jsx(Button, { type: "submit", size: "sm", disabled: disabled || !canSubmit, onMouseDown: e => e.preventDefault(), children: jsx(SendHorizontalIcon, { size: 16 }) }) })] }));
27
+ });
24
28
 
25
29
  export { ChatPromptComposer };
@@ -0,0 +1,67 @@
1
+ /** Global class for inline command chips inserted via `insertAtCaret`. */
2
+ const CHAT_PROMPT_COMMAND_CHIP_CLASS = 'chat-prompt-command-chip';
3
+ /** Range of the unfinished token immediately before `pos` (back to space or line start). */
4
+ function getChatPromptTokenRangeBeforePos(editor, pos) {
5
+ const $pos = editor.state.doc.resolve(pos);
6
+ const textBefore = $pos.parent.textBetween(0, $pos.parentOffset, undefined, '\ufffc');
7
+ const token = textBefore.match(/[^\s]*$/)?.[0] ?? '';
8
+ return { from: pos - token.length, to: pos };
9
+ }
10
+ function escapeHtml(text) {
11
+ return text
12
+ .replaceAll('&', '&amp;')
13
+ .replaceAll('<', '&lt;')
14
+ .replaceAll('>', '&gt;')
15
+ .replaceAll('"', '&quot;');
16
+ }
17
+ /** Build inline chip HTML for `insertAtCaret` (uses global `CHAT_PROMPT_COMMAND_CHIP_CLASS`). */
18
+ function chatPromptChipHtml(text, className = CHAT_PROMPT_COMMAND_CHIP_CLASS) {
19
+ const cls = className ? ` class="${className}"` : '';
20
+ return `<span${cls} data-chat-prompt-chip="true">${escapeHtml(text)}</span>`;
21
+ }
22
+ function insertChatPromptContentAtCaret(editor, content, options) {
23
+ if (editor.isDestroyed)
24
+ return;
25
+ const { replaceRange, replaceTriggerToken = true, trailingSpace = true, } = options ?? {};
26
+ const { from: selFrom, to: selTo } = editor.state.selection;
27
+ let from;
28
+ let to;
29
+ if (replaceRange) {
30
+ from = replaceRange.from;
31
+ to = replaceRange.to;
32
+ }
33
+ else if (replaceTriggerToken) {
34
+ ({ from, to } = getChatPromptTokenRangeBeforePos(editor, selFrom));
35
+ }
36
+ else {
37
+ from = selFrom;
38
+ to = selTo;
39
+ }
40
+ const chain = editor.chain().focus().deleteRange({ from, to });
41
+ if (typeof content === 'string') {
42
+ chain.insertContentAt(from, trailingSpace ? `${content} ` : content);
43
+ }
44
+ else {
45
+ chain.insertContentAt(from, content);
46
+ if (trailingSpace) {
47
+ chain.insertContent(' ');
48
+ }
49
+ }
50
+ chain.run();
51
+ queueMicrotask(() => {
52
+ editor.view.dom.ownerDocument?.defaultView
53
+ ?.getSelection?.()
54
+ ?.collapseToEnd();
55
+ });
56
+ }
57
+ function createChatPromptComposerHandle(editor) {
58
+ return {
59
+ insertAtCaret: (content, options) => insertChatPromptContentAtCaret(editor, content, options),
60
+ focus: () => {
61
+ editor.commands.focus();
62
+ },
63
+ getEditor: () => editor,
64
+ };
65
+ }
66
+
67
+ export { CHAT_PROMPT_COMMAND_CHIP_CLASS, chatPromptChipHtml, createChatPromptComposerHandle, getChatPromptTokenRangeBeforePos, insertChatPromptContentAtCaret };
@@ -44,7 +44,6 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
44
44
  Placeholder.configure({
45
45
  placeholder: placeholderText,
46
46
  showOnlyWhenEditable: true,
47
- showOnlyCurrent: false,
48
47
  }),
49
48
  ];
50
49
  if (slashItemsStable.length > 0) {
@@ -70,6 +69,28 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
70
69
  });
71
70
  }, []);
72
71
  const trimmedMessage = plainDraft.trim();
72
+ const trimmedMessageRef = useRef(trimmedMessage);
73
+ trimmedMessageRef.current = trimmedMessage;
74
+ const attachmentsCountRef = useRef(attachmentsCount);
75
+ attachmentsCountRef.current = attachmentsCount;
76
+ const onEnterSubmitRef = useRef(onEnterSubmit);
77
+ onEnterSubmitRef.current = onEnterSubmit;
78
+ const handleEditorKeyDown = useCallback((_view, event) => {
79
+ if (!(event.key === 'Enter' &&
80
+ !event.shiftKey &&
81
+ !event.metaKey &&
82
+ !event.ctrlKey)) {
83
+ return false;
84
+ }
85
+ if (slashOpenRef.current)
86
+ return false;
87
+ if (!trimmedMessageRef.current && attachmentsCountRef.current === 0) {
88
+ return false;
89
+ }
90
+ event.preventDefault();
91
+ onEnterSubmitRef.current();
92
+ return true;
93
+ }, []);
73
94
  const editor = useEditor({
74
95
  extensions,
75
96
  content: CHAT_PROMPT_EMPTY_DOC,
@@ -80,6 +101,7 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
80
101
  spellcheck: 'true',
81
102
  'aria-label': ariaLabelComposer,
82
103
  },
104
+ handleKeyDown: handleEditorKeyDown,
83
105
  },
84
106
  onTransaction: ({ editor: ed }) => {
85
107
  const dom = chatPromptSafeEditorDom(ed);
@@ -89,7 +111,7 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
89
111
  setPlainDraft(ed.getText());
90
112
  },
91
113
  onCreate: bindEditorDom,
92
- }, [extensions, bindEditorDom, ariaLabelComposer]);
114
+ }, [extensions, bindEditorDom, ariaLabelComposer, handleEditorKeyDown]);
93
115
  useEffect(() => {
94
116
  if (!editor)
95
117
  return;
@@ -131,8 +153,6 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
131
153
  return;
132
154
  syncChatPromptComposerHeight(dom, plainDraft);
133
155
  }, [editor, plainDraft]);
134
- const onEnterSubmitRef = useRef(onEnterSubmit);
135
- onEnterSubmitRef.current = onEnterSubmit;
136
156
  const resetAfterSend = useCallback(() => {
137
157
  if (!editor)
138
158
  return;
@@ -144,23 +164,10 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
144
164
  });
145
165
  setPlainDraft('');
146
166
  }, [editor]);
147
- const handleComposerKeyDown = useCallback((e) => {
148
- if (!(e.key === 'Enter' && !e.shiftKey && !e.metaKey && !e.ctrlKey))
149
- return;
150
- if (!editorDomRef.current?.contains(e.target))
151
- return;
152
- if (slashOpenRef.current)
153
- return;
154
- if (!trimmedMessage && attachmentsCount === 0)
155
- return;
156
- e.preventDefault();
157
- onEnterSubmitRef.current();
158
- }, [attachmentsCount, trimmedMessage]);
159
167
  return {
160
168
  editor,
161
169
  trimmedMessage,
162
170
  resetAfterSend,
163
- handleComposerKeyDown,
164
171
  };
165
172
  }
166
173
 
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Logo_root__-IFVw{align-items:center;color:var(--color-foreground);display:flex;gap:8px}.Logo_icon__cE1BI{display:block;flex-shrink:0;height:24px;width:auto}.Logo_text__YpU3U{font-family:var(--font-family-heading);line-height:48px}.Logo_sm__p25D- .Logo_icon__cE1BI{height:18px}.Logo_sm__p25D- .Logo_text__YpU3U{font-size:var(--text-lg);line-height:36px}.Logo_md__1Y9mG .Logo_icon__cE1BI{height:24px}.Logo_md__1Y9mG .Logo_text__YpU3U{font-size:var(--text-2xl);line-height:48px}.Logo_lg__V9Yy2 .Logo_icon__cE1BI{height:32px}.Logo_lg__V9Yy2 .Logo_text__YpU3U{font-size:var(--text-3xl);line-height:56px}";
3
+ var css_248z = ".Logo_root__-IFVw{align-items:center;color:var(--color-foreground);display:flex;gap:8px}.Logo_icon__cE1BI{color:var(--brand-color);display:block;flex-shrink:0;height:24px;width:auto}.Logo_text__YpU3U{font-family:var(--font-family-heading);line-height:48px}.Logo_sm__p25D- .Logo_icon__cE1BI{height:18px}.Logo_sm__p25D- .Logo_text__YpU3U{font-size:var(--text-lg);line-height:36px}.Logo_md__1Y9mG .Logo_icon__cE1BI{height:24px}.Logo_md__1Y9mG .Logo_text__YpU3U{font-size:var(--text-2xl);line-height:48px}.Logo_lg__V9Yy2 .Logo_icon__cE1BI{height:32px}.Logo_lg__V9Yy2 .Logo_text__YpU3U{font-size:var(--text-3xl);line-height:56px}";
4
4
  var S = {"root":"Logo_root__-IFVw","icon":"Logo_icon__cE1BI","text":"Logo_text__YpU3U","sm":"Logo_sm__p25D-","md":"Logo_md__1Y9mG","lg":"Logo_lg__V9Yy2"};
5
5
  styleInject(css_248z);
6
6
 
@@ -7,7 +7,7 @@ var SvgLogo = function SvgLogo(props) {
7
7
  viewBox: "0 0 24 24",
8
8
  fill: "none"
9
9
  }, props), /*#__PURE__*/React.createElement("path", {
10
- fill: "#C259FF",
10
+ fill: "currentColor",
11
11
  d: "M13.43 24.023h-2.836v-2.836h2.836zm-2.835-2.844H5.63v-2.74h4.965Zm10.527-2.74h-2.726v2.74h-4.965v-2.74h4.952v-4.965h2.74zm-15.5 0h-2.74v-4.965h2.74ZM2.87 13.462H.034v-2.836H2.87Zm21.096 0H21.13v-2.836h2.836zM5.617 10.635h-2.74V5.669h2.74zm15.505 0h-2.74V5.669h2.74zM10.588 2.927v2.74H5.623v-2.74Zm2.842 0h4.96v2.74h-4.966v-2.74h-2.83V.09h2.836z",
12
12
  style: {
13
13
  strokeWidth: 0.13252
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Link } from 'react-router-dom';
3
+ import { Logo } from '../../Logo/Logo.js';
3
4
  import { GlobeIcon, MailIcon } from 'lucide-react';
4
5
  import S from './PageFooter.styl.js';
5
6
 
@@ -8,7 +9,7 @@ function PageFooterLinkEl({ item }) {
8
9
  const openInNewTab = /^https?:\/\//i.test(url);
9
10
  return (jsx("a", { href: url, className: S.link, ...(ariaLabel !== undefined ? { 'aria-label': ariaLabel } : {}), ...(openInNewTab ? { target: '_blank', rel: 'noreferrer' } : {}), children: label }));
10
11
  }
11
- function PageFooter({ children, versionLink, versionLabel, homeTo = '/', brandText = 'Sybilion', logo, websiteHref = 'https://sybilion.com', mailHref = 'mailto:support@sybilion.com', copyrightText = '© 2026 Sybilion. All rights reserved.', links: linksProp, }) {
12
+ function PageFooter({ children, versionLink, versionLabel, homeTo = '/', brandText = 'Sybilion', websiteHref = 'https://sybilion.com', mailHref = 'mailto:support@sybilion.com', copyrightText = '© 2026 Sybilion. All rights reserved.', links: linksProp, }) {
12
13
  const resolvedLinks = linksProp !== undefined
13
14
  ? linksProp
14
15
  : [
@@ -25,7 +26,7 @@ function PageFooter({ children, versionLink, versionLabel, homeTo = '/', brandTe
25
26
  ariaLabel: 'Contact support via email',
26
27
  },
27
28
  ];
28
- return (jsx("footer", { className: S.root, children: jsxs("div", { className: S.line, children: [jsxs("div", { className: S.logo, children: [jsxs(Link, { to: homeTo, children: [logo, "\u00A0", brandText] }), versionLink !== '' && (jsx("div", { className: S.version, children: jsx(Link, { to: versionLink, className: S.versionLink, children: versionLabel }) }))] }), children, jsxs("div", { className: S.meta, children: [jsx("div", { className: S.copyright, children: copyrightText }), resolvedLinks.length > 0 && (jsx("nav", { className: S.links, "aria-label": "Footer links", children: resolvedLinks.map(item => (jsx(PageFooterLinkEl, { item: item }, item.id))) }))] })] }) }));
29
+ return (jsx("footer", { className: S.root, children: jsxs("div", { className: S.line, children: [jsxs("div", { className: S.logo, children: [jsxs(Link, { to: homeTo, children: [jsx(Logo, {}), "\u00A0", brandText] }), versionLink !== '' && (jsx("div", { className: S.version, children: jsx(Link, { to: versionLink, className: S.versionLink, children: versionLabel }) }))] }), children, jsxs("div", { className: S.meta, children: [jsx("div", { className: S.copyright, children: copyrightText }), resolvedLinks.length > 0 && (jsx("nav", { className: S.links, "aria-label": "Footer links", children: resolvedLinks.map(item => (jsx(PageFooterLinkEl, { item: item }, item.id))) }))] })] }) }));
29
30
  }
30
31
 
31
32
  export { PageFooter };
@@ -8,7 +8,7 @@ import S from './PageHeader.styl.js';
8
8
 
9
9
  function PageHeader({ breadcrumbs, breadcrumbClientLogo, breadcrumbCompanyName, breadcrumbSidebarTrigger = true, title, subheader, actions, }) {
10
10
  const { isScrolled } = useContext(PageContext);
11
- return (jsx("div", { className: cn(S.root, actions && S.hasActions, isScrolled && S.scrolled), children: jsxs("div", { className: S.inner, children: [jsx(Breadcrumbs, { className: S.breadcrumbs, items: breadcrumbs ?? [], clientLogo: breadcrumbClientLogo, companyName: breadcrumbCompanyName, sidebarTrigger: breadcrumbSidebarTrigger, children: isScrolled && (jsxs("div", { className: S.titleDupe, children: [jsx(BreadCrumbsSeparator, { size: 14 }), title] })) }), jsxs("div", { className: S.main, children: [jsxs("div", { className: S.title, children: [jsx("h1", { children: title }), subheader && (jsx(TextWithDeferTooltip, { className: S.subheader, children: subheader }))] }), actions && jsx("div", { className: S.actions, children: actions })] })] }) }));
11
+ return (jsx("div", { className: cn(S.root, actions && S.hasActions, isScrolled && S.scrolled), children: jsxs("div", { className: S.inner, children: [jsx(Breadcrumbs, { className: S.breadcrumbs, items: breadcrumbs ?? [], clientLogo: breadcrumbClientLogo, companyName: breadcrumbCompanyName, sidebarTrigger: breadcrumbSidebarTrigger, children: isScrolled && (jsxs("div", { className: S.titleDupe, children: [jsx(BreadCrumbsSeparator, { size: 14 }), title] })) }), jsxs("div", { className: S.main, children: [jsxs("div", { className: S.title, children: [jsx("h1", { children: title }), subheader && (jsx(TextWithDeferTooltip, { className: S.subheader, overTrigger: true, children: subheader }))] }), actions && jsx("div", { className: S.actions, children: actions })] })] }) }));
12
12
  }
13
13
 
14
14
  export { PageHeader };
@@ -4,19 +4,41 @@ import { Tooltip, TooltipTrigger, TooltipContent } from '../Tooltip/Tooltip.js';
4
4
 
5
5
  function TextWithDeferTooltip({ className, children, width, maxWidth, side = 'bottom', overTrigger = false, ...props }) {
6
6
  const [withTooltip, setWithTooltip] = useState(false);
7
+ const [tooltipStyles, setTooltipStyles] = useState({});
7
8
  const ref = useRef(null);
9
+ const cachedFontSizeRef = useRef(null);
8
10
  const handleMouseEnter = () => {
9
11
  if (!ref.current)
10
12
  return;
11
13
  const isOverflowingHorizontally = ref.current.scrollWidth - ref.current.clientWidth > 3;
12
14
  const isOverflowingVertically = ref.current.scrollHeight - ref.current.clientHeight > 3;
13
15
  if (isOverflowingHorizontally || isOverflowingVertically) {
16
+ const styles = {
17
+ fontSize: cachedFontSizeRef.current || undefined,
18
+ };
19
+ if (ref.current) {
20
+ const { width: rectWidth, left, top, } = ref.current.getBoundingClientRect();
21
+ styles.width = `${width ?? rectWidth}px`;
22
+ if (!cachedFontSizeRef.current) {
23
+ const { fontSize } = window.getComputedStyle(ref.current);
24
+ cachedFontSizeRef.current = fontSize;
25
+ styles.fontSize = fontSize;
26
+ }
27
+ if (overTrigger) {
28
+ styles.transform = `translate(${left}px, ${top}px) !important`;
29
+ }
30
+ }
31
+ setTooltipStyles(styles);
14
32
  setWithTooltip(true);
15
33
  }
16
34
  };
17
35
  const textElement = (jsx("div", { ref: ref, className: className, onMouseEnter: handleMouseEnter, ...props, children: children }));
18
36
  if (withTooltip) {
19
- return (jsxs(Tooltip, { open: withTooltip, onOpenChange: setWithTooltip, children: [jsx(TooltipTrigger, { asChild: true, children: textElement }), jsx(TooltipContent, { side: side, overTrigger: overTrigger, maxWidth: maxWidth, style: width !== undefined ? { width: `${width}px` } : undefined, children: children })] }));
37
+ const tooltipSide = overTrigger ? 'bottom' : side;
38
+ return (jsxs(Tooltip, { open: withTooltip, onOpenChange: setWithTooltip, children: [jsx(TooltipTrigger, { asChild: true, children: textElement }), jsx(TooltipContent, { side: tooltipSide, style: {
39
+ ...(maxWidth !== undefined && { maxWidth: `${maxWidth}px` }),
40
+ ...tooltipStyles,
41
+ }, overTrigger: overTrigger, children: children })] }));
20
42
  }
21
43
  return textElement;
22
44
  }
@@ -83,7 +83,7 @@ function DriverCard({ selectedDriver, isLoading, inQueue = false, driverSelector
83
83
  const directionText = direction > 0 ? 'Positive' : 'Negative';
84
84
  const DirectionIcon = direction > 0 ? TrendUpIcon : TrendDownIcon;
85
85
  const nameElem = (jsx("h4", { className: `${S.driverTitle} ${S.truncated}`, children: name }));
86
- return (jsx(Card, { className: S.root, paddingSize: "l", children: jsx(CardContent, { noScroll: true, children: jsxs("div", { className: S.cardContent, children: [jsx("div", { className: S.driverHeader, children: jsxs("div", { className: S.headerContent, children: [jsxs("div", { className: S.topHeader, children: [jsxs("p", { className: S.categoryInfo, children: [jsx("span", { className: S.categoryIcon, children: getCategoryIcon(category) }), jsx("span", { className: S.categoryText, children: category })] }), driverSelector] }), name.length > 60 ? (jsxs(Tooltip, { children: [jsx(LabelWithId, { id: id, label: jsx(TooltipTrigger, { asChild: true, children: nameElem }) }), jsx(TooltipContent, { side: "left", className: S.tooltipContent, children: jsx("div", { className: S.tooltipTitle, children: name }) })] })) : (jsx(LabelWithId, { id: id, label: nameElem })), jsx("p", { className: S.regionDisplay, children: regionDisplay })] }) }), jsx("div", { className: S.metricsSection, children: jsx("div", { className: S.importanceScore, children: importanceDisplay }) }), jsxs("div", { className: S.directionLagSection, children: [jsxs(Badge, { variant: direction > 0 ? 'green' : 'red', className: S.directionBadge, children: [jsx(DirectionIcon, { className: S.trendIcon }), directionText, " correlation"] }), jsxs("span", { className: S.lagInfo, children: ["Lag: ", lag] })] }), jsx(DriverPerformanceChart, { driver: selectedDriver }), jsx("p", { className: S.description, children: summary ?? '' })] }) }) }));
86
+ return (jsx(Card, { className: S.root, paddingSize: "l", children: jsx(CardContent, { noScroll: true, children: jsxs("div", { className: S.cardContent, children: [jsx("div", { className: S.driverHeader, children: jsxs("div", { className: S.headerContent, children: [jsxs("div", { className: S.topHeader, children: [jsxs("p", { className: S.categoryInfo, children: [jsx("span", { className: S.categoryIcon, children: getCategoryIcon(category) }), jsx("span", { className: S.categoryText, children: category })] }), driverSelector] }), name.length > 60 ? (jsxs(Tooltip, { children: [jsx(LabelWithId, { id: id, label: jsx(TooltipTrigger, { asChild: true, children: nameElem }) }), jsx(TooltipContent, { side: "left", className: S.tooltipContent, overTrigger: true, children: jsx("div", { className: S.tooltipTitle, children: name }) })] })) : (jsx(LabelWithId, { id: id, label: nameElem })), jsx("p", { className: S.regionDisplay, children: regionDisplay })] }) }), jsx("div", { className: S.metricsSection, children: jsx("div", { className: S.importanceScore, children: importanceDisplay }) }), jsxs("div", { className: S.directionLagSection, children: [jsxs(Badge, { variant: direction > 0 ? 'green' : 'red', className: S.directionBadge, children: [jsx(DirectionIcon, { className: S.trendIcon }), directionText, " correlation"] }), jsxs("span", { className: S.lagInfo, children: ["Lag: ", lag] })] }), jsx(DriverPerformanceChart, { driver: selectedDriver }), jsx("p", { className: S.description, children: summary ?? '' })] }) }) }));
87
87
  }
88
88
 
89
89
  export { DriverCard };
@@ -17,6 +17,7 @@ import '../../ui/Page/PageContent/PageContent.styl.js';
17
17
  import '@homecode/ui';
18
18
  import '../../ui/Page/PageScroll/PageScroll.styl.js';
19
19
  import { PageXScroll } from '../../ui/Page/PageXScroll/PageXScroll.js';
20
+ import '../../ui/Logo/Logo.styl.js';
20
21
  import '../../ui/Page/PageFooter/PageFooter.styl.js';
21
22
  import '@radix-ui/react-tabs';
22
23
  import '../../ui/Tabs/Tabs.styl.js';
@@ -16,6 +16,7 @@ import '../../ui/Page/PageContent/PageContent.styl.js';
16
16
  import '@homecode/ui';
17
17
  import '../../ui/Page/PageScroll/PageScroll.styl.js';
18
18
  import { PageXScroll } from '../../ui/Page/PageXScroll/PageXScroll.js';
19
+ import '../../ui/Logo/Logo.styl.js';
19
20
  import '../../ui/Page/PageFooter/PageFooter.styl.js';
20
21
  import '@radix-ui/react-tabs';
21
22
  import '../../ui/Tabs/Tabs.styl.js';
package/dist/esm/index.js CHANGED
@@ -31,6 +31,7 @@ export { ChatSheet } from './components/ui/Chat/ChatSheet/ChatSheet.js';
31
31
  export { useChatPanelChromeModel } from './components/ui/Chat/ChatSheet/useChatPanelChromeModel.js';
32
32
  export { ChatMessage } from './components/ui/Chat/ChatMessage/ChatMessage.js';
33
33
  export { ChatPrompt } from './components/ui/Chat/ChatPrompt/ChatPrompt.js';
34
+ export { CHAT_PROMPT_COMMAND_CHIP_CLASS, chatPromptChipHtml, createChatPromptComposerHandle, getChatPromptTokenRangeBeforePos, insertChatPromptContentAtCaret } from './components/ui/Chat/ChatPrompt/chatPromptComposerInsert.js';
34
35
  export { ChatPresets } from './components/ui/Chat/ChatPresets/ChatPresets.js';
35
36
  export { MessageRole } from './components/ui/Chat/Chat.types.js';
36
37
  export { CsvIcon } from './components/icons/CsvIcon/CsvIcon.js';
@@ -1,7 +1,7 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.SlashSuggestionList_root__9KCb1{background:var(--popover,var(--card));border:1px solid var(--border);border-radius:8px;border-radius:var(--radius-lg,8px);box-shadow:0 8px 24px rgba(0,0,0,.12);color:var(--foreground);display:flex;flex-direction:column;font-size:var(--text-sm);list-style:none;margin:0;max-height:40vh;max-width:90vw;min-width:220px;overflow-y:auto;padding:var(--p-1)}.dark .SlashSuggestionList_root__9KCb1{box-shadow:0 8px 24px rgba(0,0,0,.5)}.SlashSuggestionList_item__9JBeY{align-items:flex-start;background:transparent;border:none;border-radius:6px;border-radius:var(--radius-md,6px);color:inherit;cursor:pointer;display:flex;flex-direction:column;margin:0;padding:var(--p-1) var(--p-2);text-align:left;width:100%}.SlashSuggestionList_itemHighlighted__cStZr,.SlashSuggestionList_item__9JBeY:hover{background:var(--muted)}.SlashSuggestionList_itemLabel__OMaov{font-weight:600}.SlashSuggestionList_itemDesc__fAdcA{color:var(--muted-foreground);font-size:var(--text-xs)}";
4
- var S = {"root":"SlashSuggestionList_root__9KCb1","item":"SlashSuggestionList_item__9JBeY","itemHighlighted":"SlashSuggestionList_itemHighlighted__cStZr","itemLabel":"SlashSuggestionList_itemLabel__OMaov","itemDesc":"SlashSuggestionList_itemDesc__fAdcA"};
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.SlashSuggestionList_root__9KCb1{background:var(--popover,var(--card));border:1px solid var(--border);border-radius:8px;border-radius:var(--radius-lg,8px);box-shadow:0 8px 24px rgba(0,0,0,.12);color:var(--foreground);display:flex;flex-direction:column;font-size:var(--text-sm);list-style:none;margin:0;max-height:40vh;max-width:90vw;min-width:220px;overflow-y:auto;padding:var(--p-1)}.dark .SlashSuggestionList_root__9KCb1{box-shadow:0 8px 24px rgba(0,0,0,.5)}.SlashSuggestionList_item__9JBeY{align-items:flex-start;background:transparent;border:none;border-radius:6px;border-radius:var(--radius-md,6px);color:inherit;cursor:pointer;display:flex;flex-direction:column;margin:0;padding:var(--p-1) var(--p-2);text-align:left;width:100%}.SlashSuggestionList_itemHighlighted__cStZr,.SlashSuggestionList_item__9JBeY:hover{background:var(--muted)}.SlashSuggestionList_itemLabel__OMaov{font-weight:600}.SlashSuggestionList_itemDesc__fAdcA{color:var(--muted-foreground);font-size:var(--text-xs)}.SlashSuggestionList_mention__aT45p{border-radius:.3em;padding:0 .3em}";
4
+ var S = {"root":"SlashSuggestionList_root__9KCb1","item":"SlashSuggestionList_item__9JBeY","itemHighlighted":"SlashSuggestionList_itemHighlighted__cStZr","itemLabel":"SlashSuggestionList_itemLabel__OMaov","itemDesc":"SlashSuggestionList_itemDesc__fAdcA","mention":"SlashSuggestionList_mention__aT45p"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { S as default };
@@ -2,8 +2,34 @@ import { mergeAttributes } from '@tiptap/core';
2
2
  import Mention from '@tiptap/extension-mention';
3
3
  import { ReactRenderer } from '@tiptap/react';
4
4
  import { SlashSuggestionList } from './SlashSuggestionList.js';
5
+ import S from './SlashSuggestionList.styl.js';
5
6
  import { filterSlashItems } from './defaultChatSlashItems.js';
6
7
 
8
+ const SlashMention = Mention.extend({
9
+ addAttributes() {
10
+ return {
11
+ ...this.parent?.(),
12
+ className: {
13
+ default: null,
14
+ parseHTML: element => element.getAttribute('data-slash-class'),
15
+ renderHTML: attributes => {
16
+ if (!attributes.className)
17
+ return {};
18
+ return { 'data-slash-class': attributes.className };
19
+ },
20
+ },
21
+ color: {
22
+ default: null,
23
+ parseHTML: element => element.getAttribute('data-slash-color'),
24
+ renderHTML: attributes => {
25
+ if (!attributes.color)
26
+ return {};
27
+ return { 'data-slash-color': attributes.color };
28
+ },
29
+ },
30
+ };
31
+ },
32
+ });
7
33
  const SUGGESTION_GAP_PX = 4;
8
34
  function placeSlashSuggestionPopup(popupElement, clientRect, placement) {
9
35
  if (!clientRect)
@@ -68,27 +94,10 @@ function slashMentionSuggestionRender(uiRef, placement = 'below') {
68
94
  onKeyDown: ({ event }) => uiRef.current?.onKeyboardEvent(event) ?? false,
69
95
  };
70
96
  }
71
- function clearSlashTriggerEditor(editor, range) {
72
- if (editor.isDestroyed)
73
- return;
74
- try {
75
- editor.chain().focus().deleteRange(range).clearContent().run();
76
- }
77
- catch {
78
- // Editor view may be tearing down during suggestion exit.
79
- }
80
- }
81
- function collapseEditorSelectionEnd(editor) {
82
- if (editor.isDestroyed)
83
- return;
84
- try {
85
- editor.view?.dom?.ownerDocument?.defaultView
86
- ?.getSelection?.()
87
- ?.collapseToEnd();
88
- }
89
- catch {
90
- // view.dom throws when editor is not mounted
91
- }
97
+ function slashMentionChipStyle(color) {
98
+ if (!color)
99
+ return undefined;
100
+ return `color: ${color}; background-color: color-mix(in srgb, ${color} 30%, transparent);`;
92
101
  }
93
102
  function insertDefaultMention(editor, range, props, slashChar) {
94
103
  const nodeAfter = editor.view.state.selection.$to.nodeAfter;
@@ -104,6 +113,8 @@ function insertDefaultMention(editor, range, props, slashChar) {
104
113
  id: props.id,
105
114
  label: props.label,
106
115
  mentionSuggestionChar: slashChar,
116
+ className: props.className ?? null,
117
+ color: props.color ?? null,
107
118
  },
108
119
  },
109
120
  { type: 'text', text: ' ' },
@@ -129,23 +140,31 @@ function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pl
129
140
  const uiRef = {
130
141
  current: null,
131
142
  };
132
- return Mention.configure({
143
+ return SlashMention.configure({
133
144
  renderText({ node }) {
134
145
  return `/${node.attrs.id}`;
135
146
  },
136
- renderHTML({ options, node, suggestion }) {
137
- const suggestionChar = suggestion?.char ?? slashChar;
147
+ renderHTML({ options, node }) {
138
148
  const id = typeof node.attrs.id === 'string'
139
149
  ? node.attrs.id
140
150
  : String(node.attrs.id ?? '');
151
+ const label = typeof node.attrs.label === 'string' && node.attrs.label.trim() !== ''
152
+ ? node.attrs.label
153
+ : id;
154
+ const color = typeof node.attrs.color === 'string' ? node.attrs.color : null;
155
+ const className = [S.mention, node.attrs.className]
156
+ .filter(Boolean)
157
+ .join(' ');
158
+ const chipStyle = slashMentionChipStyle(color);
141
159
  return [
142
160
  'span',
143
161
  mergeAttributes({
144
162
  'data-type': 'mention',
145
163
  'data-slash-command': id,
146
- class: 'slash-mention',
164
+ class: className,
165
+ ...(chipStyle ? { style: chipStyle } : {}),
147
166
  }, options.HTMLAttributes),
148
- `${suggestionChar}${id}`,
167
+ label,
149
168
  ];
150
169
  },
151
170
  suggestion: {
@@ -157,8 +176,6 @@ function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pl
157
176
  command: ({ editor, range, props }) => {
158
177
  const item = props;
159
178
  if (onItemCommand?.({ editor, range, item }) === true) {
160
- clearSlashTriggerEditor(editor, range);
161
- queueMicrotask(() => collapseEditorSelectionEnd(editor));
162
179
  return null;
163
180
  }
164
181
  insertDefaultMention(editor, range, item, slashChar);
@@ -2,10 +2,9 @@ import { ChatPresets } from '#uilib/components/ui/Chat/ChatPresets';
2
2
  import type { ChatProps } from './Chat.types';
3
3
  import { ChatEmptyState } from './ChatEmptyState/ChatEmptyState';
4
4
  import { ChatMessage } from './ChatMessage';
5
- import { ChatPrompt } from './ChatPrompt';
6
5
  export declare function Chat({ children, className, isEmpty, scopeId, onChatDeleted, ...props }: ChatProps): import("react/jsx-runtime").JSX.Element;
7
6
  export declare namespace Chat {
8
- var Prompt: typeof ChatPrompt;
7
+ var Prompt: import("react").ForwardRefExoticComponent<import("./Chat.types").ChatPromptProps & import("react").RefAttributes<import(".").ChatPromptComposerHandle>>;
9
8
  var Message: typeof ChatMessage;
10
9
  var Presets: typeof ChatPresets;
11
10
  var EmptyState: typeof ChatEmptyState;
@@ -84,7 +84,7 @@ export interface ChatPromptProps {
84
84
  onAttachmentFiles?: (files: File[]) => void;
85
85
  /** Slash menu (`/`); omit or pass empty to disable Mention trigger (no default items). */
86
86
  slashCommandItems?: SlashCommandItem[];
87
- /** Custom slash pick handler; return true to skip default mention insert. */
87
+ /** Custom slash pick handler; return true to skip default mention insert and insert via composer API. */
88
88
  onSlashItemCommand?: SlashOnItemCommand;
89
89
  }
90
90
  export interface ChatMessageProps {
@@ -1,2 +1,4 @@
1
1
  import type { ChatPromptProps } from '../Chat.types';
2
- export declare function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, onSlashItemCommand, attachments, onRemoveAttachment, disabled, attachmentAccept, onAttachmentFiles, }: ChatPromptProps): import("react/jsx-runtime").JSX.Element;
2
+ import { type ChatPromptComposerHandle } from './ChatPromptComposer';
3
+ export type { ChatPromptComposerHandle } from './ChatPromptComposer';
4
+ export declare const ChatPrompt: import("react").ForwardRefExoticComponent<ChatPromptProps & import("react").RefAttributes<ChatPromptComposerHandle>>;