@sybilion/uilib 1.3.68 → 1.3.71

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 (24) hide show
  1. package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.styl.js +1 -1
  2. package/dist/esm/components/ui/Chat/ChatPrompt/useChatPromptEditor.js +4 -1
  3. package/dist/esm/components/ui/Renamer/Renamer.js +4 -3
  4. package/dist/esm/components/ui/Renamer/Renamer.styl.js +2 -2
  5. package/dist/esm/components/ui/WorkspaceAppSwitcher/WorkspaceAppSwitcher.js +16 -5
  6. package/dist/esm/components/ui/WorkspaceAppSwitcher/WorkspaceAppSwitcher.styl.js +1 -1
  7. package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.js +1 -1
  8. package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.styl.js +2 -2
  9. package/dist/esm/types/src/components/ui/WorkspaceAppSwitcher/workspaceApp.types.d.ts +5 -1
  10. package/dist/esm/types/src/docs/pages/ChatComposerPrefillPage.d.ts +1 -0
  11. package/package.json +1 -1
  12. package/src/components/ui/Chat/ChatMessage/ChatMessage.styl +1 -1
  13. package/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.ts +4 -1
  14. package/src/components/ui/Renamer/Renamer.styl +21 -5
  15. package/src/components/ui/Renamer/Renamer.styl.d.ts +1 -0
  16. package/src/components/ui/Renamer/Renamer.tsx +5 -4
  17. package/src/components/ui/WorkspaceAppSwitcher/WorkspaceAppSwitcher.styl +8 -0
  18. package/src/components/ui/WorkspaceAppSwitcher/WorkspaceAppSwitcher.tsx +27 -4
  19. package/src/components/ui/WorkspaceAppSwitcher/workspaceApp.types.ts +6 -1
  20. package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.styl +9 -0
  21. package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.styl.d.ts +1 -0
  22. package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.tsx +2 -4
  23. package/src/docs/pages/ChatComposerPrefillPage.tsx +202 -0
  24. package/src/docs/registry.ts +6 -0
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".ChatMessage_root__6rnsF{background:var(--bg-secondary);display:flex;flex-direction:column;gap:var(--p-1);padding:var(--p-4);padding-bottom:var(--p-1)}.ChatMessage_text__Y1XNR{color:var(--text-secondary);font-size:var(--text-sm);max-width:100%;min-width:0;overflow-wrap:anywhere;-webkit-user-select:text;-moz-user-select:text;user-select:text;width:-moz-fit-content;width:fit-content;word-break:break-word}.ChatMessage_role-assistant__wketE+.ChatMessage_role-assistant__wketE,.ChatMessage_role-system__g13OP+.ChatMessage_role-system__g13OP,.ChatMessage_role-user__u4JPV+.ChatMessage_role-user__u4JPV{padding-top:var(--p-1)}.ChatMessage_role-user__u4JPV{align-items:flex-end;max-width:100%;min-width:0}.ChatMessage_role-user__u4JPV .ChatMessage_userColumn__cQM6-{align-items:flex-end;display:flex;flex-direction:column;gap:var(--p-2);max-width:100%;min-width:0}.ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-slate-100);border-radius:var(--p-4);border-bottom-right-radius:0;box-sizing:border-box;overflow:hidden;padding:var(--p-3) var(--p-4);white-space:pre-wrap}.dark .ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-gray-800)}.ChatMessage_role-system__g13OP{align-items:center}.ChatMessage_role-system__g13OP .ChatMessage_text__Y1XNR{color:var(--muted-foreground);width:100%}.ChatMessage_role-assistant__wketE .ChatMessage_text__Y1XNR{width:100%}.ChatMessage_role-assistant__wketE h3{line-height:2.4}.ChatMessage_role-assistant__wketE h4{font-size:1.1em;font-weight:600;line-height:2.2}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq{display:inline-block;margin-left:4px;margin-right:6px}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq:before{color:var(--text-secondary);content:\"•\";display:inline-block}.ChatMessage_role-assistant__wketE .ChatMessage_scrollHorizontal__Rms9n{max-width:100%}.ChatMessage_role-assistant__wketE table{border:1px solid var(--border);border-collapse:collapse;border-radius:var(--p-2);border-spacing:0;margin:var(--p-4) 0;overflow:hidden}.ChatMessage_role-assistant__wketE table td,.ChatMessage_role-assistant__wketE table th{border:1px solid var(--border);min-width:100px;padding:var(--p-1)}.ChatMessage_role-assistant__wketE table th{text-align:left}.ChatMessage_role-assistant__wketE ol,.ChatMessage_role-assistant__wketE ul{padding-left:var(--p-4)}.ChatMessage_role-assistant__wketE ul{list-style-type:disc}.ChatMessage_role-assistant__wketE ol{list-style-type:decimal}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{align-items:center;border:1px dashed var(--sb-slate-300);border-radius:8px;color:var(--foreground);display:inline-flex;font-size:var(--text-xs);gap:6px;margin:1px;max-width:100%;padding:2px 6px 2px 4px;text-decoration:none;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-slate-50);border-color:var(--sb-slate-400);border-style:solid}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{border-color:var(--sb-gray-600)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-gray-900)}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLinkLabel__PMU7e{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ChatMessage_role-assistant__wketE .ChatMessage_quickReplyWrap__1UFyD{display:inline-block;margin:var(--p-1) var(--p-1) var(--p-1) 0;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_downloadButtons__RygM-{display:flex;gap:var(--p-2);margin-top:var(--p-4)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{align-items:center;background-color:var(--background);border-radius:var(--p-3);box-shadow:0 0 0 1px var(--border);cursor:pointer;display:flex;gap:var(--p-4);margin-top:var(--p-3);padding:var(--p-3);padding-right:var(--p-4);transition:all .15s;width:-moz-fit-content;width:fit-content}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-50);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{background-color:var(--sb-gray-900);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-800)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardIcon__jkxDJ{align-items:center;border-radius:var(--p-2);display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardContent__PTPwz{display:flex;flex:1;flex-direction:column;min-width:0}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardTitle__K1wqr{font-size:var(--text-base);font-weight:600;line-height:1.4}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardSubtitle__fVeF2{color:var(--muted-foreground);font-size:var(--text-sm);line-height:1.4}";
3
+ var css_248z = ".ChatMessage_root__6rnsF{background:var(--bg-secondary);display:flex;flex-direction:column;gap:var(--p-1);padding:var(--p-4);padding-bottom:var(--p-1)}.ChatMessage_text__Y1XNR{color:var(--text-secondary);font-size:var(--text-sm);max-width:100%;min-width:0;overflow-wrap:anywhere;-webkit-user-select:text;-moz-user-select:text;user-select:text;width:-moz-fit-content;width:fit-content;word-break:break-word}.ChatMessage_role-assistant__wketE+.ChatMessage_role-assistant__wketE,.ChatMessage_role-system__g13OP+.ChatMessage_role-system__g13OP,.ChatMessage_role-user__u4JPV+.ChatMessage_role-user__u4JPV{padding-top:var(--p-1)}.ChatMessage_role-user__u4JPV{align-items:flex-end;max-width:100%;min-width:0}.ChatMessage_role-user__u4JPV .ChatMessage_userColumn__cQM6-{align-items:flex-end;display:flex;flex-direction:column;gap:var(--p-2);max-width:100%;min-width:0}.ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-slate-100);border-radius:var(--p-4);border-bottom-right-radius:0;box-sizing:border-box;overflow:hidden;padding:var(--p-3) var(--p-4);white-space:pre-wrap}.dark .ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-gray-900)}.ChatMessage_role-system__g13OP{align-items:center}.ChatMessage_role-system__g13OP .ChatMessage_text__Y1XNR{color:var(--muted-foreground);width:100%}.ChatMessage_role-assistant__wketE .ChatMessage_text__Y1XNR{width:100%}.ChatMessage_role-assistant__wketE h3{line-height:2.4}.ChatMessage_role-assistant__wketE h4{font-size:1.1em;font-weight:600;line-height:2.2}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq{display:inline-block;margin-left:4px;margin-right:6px}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq:before{color:var(--text-secondary);content:\"•\";display:inline-block}.ChatMessage_role-assistant__wketE .ChatMessage_scrollHorizontal__Rms9n{max-width:100%}.ChatMessage_role-assistant__wketE table{border:1px solid var(--border);border-collapse:collapse;border-radius:var(--p-2);border-spacing:0;margin:var(--p-4) 0;overflow:hidden}.ChatMessage_role-assistant__wketE table td,.ChatMessage_role-assistant__wketE table th{border:1px solid var(--border);min-width:100px;padding:var(--p-1)}.ChatMessage_role-assistant__wketE table th{text-align:left}.ChatMessage_role-assistant__wketE ol,.ChatMessage_role-assistant__wketE ul{padding-left:var(--p-4)}.ChatMessage_role-assistant__wketE ul{list-style-type:disc}.ChatMessage_role-assistant__wketE ol{list-style-type:decimal}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{align-items:center;border:1px dashed var(--sb-slate-300);border-radius:8px;color:var(--foreground);display:inline-flex;font-size:var(--text-xs);gap:6px;margin:1px;max-width:100%;padding:2px 6px 2px 4px;text-decoration:none;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-slate-50);border-color:var(--sb-slate-400);border-style:solid}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{border-color:var(--sb-gray-600)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-gray-900)}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLinkLabel__PMU7e{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ChatMessage_role-assistant__wketE .ChatMessage_quickReplyWrap__1UFyD{display:inline-block;margin:var(--p-1) var(--p-1) var(--p-1) 0;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_downloadButtons__RygM-{display:flex;gap:var(--p-2);margin-top:var(--p-4)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{align-items:center;background-color:var(--background);border-radius:var(--p-3);box-shadow:0 0 0 1px var(--border);cursor:pointer;display:flex;gap:var(--p-4);margin-top:var(--p-3);padding:var(--p-3);padding-right:var(--p-4);transition:all .15s;width:-moz-fit-content;width:fit-content}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-50);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{background-color:var(--sb-gray-900);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-800)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardIcon__jkxDJ{align-items:center;border-radius:var(--p-2);display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardContent__PTPwz{display:flex;flex:1;flex-direction:column;min-width:0}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardTitle__K1wqr{font-size:var(--text-base);font-weight:600;line-height:1.4}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardSubtitle__fVeF2{color:var(--muted-foreground);font-size:var(--text-sm);line-height:1.4}";
4
4
  var S = {"root":"ChatMessage_root__6rnsF","text":"ChatMessage_text__Y1XNR","role-user":"ChatMessage_role-user__u4JPV","role-assistant":"ChatMessage_role-assistant__wketE","role-system":"ChatMessage_role-system__g13OP","userColumn":"ChatMessage_userColumn__cQM6-","bullet":"ChatMessage_bullet__6vAhq","scrollHorizontal":"ChatMessage_scrollHorizontal__Rms9n","datasetAppLink":"ChatMessage_datasetAppLink__Pxy-T","datasetAppLinkLabel":"ChatMessage_datasetAppLinkLabel__PMU7e","quickReplyWrap":"ChatMessage_quickReplyWrap__1UFyD","downloadButtons":"ChatMessage_downloadButtons__RygM-","downloadCard":"ChatMessage_downloadCard__NsNRa","downloadCardIcon":"ChatMessage_downloadCardIcon__jkxDJ","downloadCardContent":"ChatMessage_downloadCardContent__PTPwz","downloadCardTitle":"ChatMessage_downloadCardTitle__K1wqr","downloadCardSubtitle":"ChatMessage_downloadCardSubtitle__fVeF2"};
5
5
  styleInject(css_248z);
6
6
 
@@ -143,8 +143,11 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlash
143
143
  const dom = chatPromptSafeEditorDom(editor);
144
144
  if (dom)
145
145
  syncChatPromptComposerHeight(dom, t);
146
+ if (!disabled && !editor.isDestroyed) {
147
+ editor.chain().focus('end').run();
148
+ }
146
149
  });
147
- }, [editor, prefillMessage]);
150
+ }, [editor, prefillMessage, disabled]);
148
151
  useLayoutEffect(() => {
149
152
  if (!editor)
150
153
  return;
@@ -45,17 +45,18 @@ function Renamer({ value = '', onInput, onChange, children, className, placehold
45
45
  setName(value);
46
46
  setIsRenaming(true);
47
47
  }, [value]);
48
- const sizeMap = {
48
+ const inputSizeMap = {
49
49
  sm: 'sm',
50
50
  md: 'md',
51
- lg: 'lg',
51
+ // lg typography comes from Renamer `.size-lg`; avoid Input size-lg (text-base).
52
+ lg: 'md',
52
53
  };
53
54
  const buttonSizeMap = {
54
55
  sm: 'sm',
55
56
  md: 'md',
56
57
  lg: 'lg',
57
58
  };
58
- return (jsx("div", { className: cn(S.root, !value && S.empty, className), onKeyDown: onKeyDown, children: isRenaming ? (jsx(Input, { className: S.input, autoFocus: !isMobile, variant: "clean", size: sizeMap[size], placeholder: placeholder, value: name, onChange: handleInputChange, onBlur: handleBlur, onKeyDown: onKeyDown })) : (jsxs(Fragment, { children: [jsx("span", { className: S.value, children: children }), jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { className: S.button, variant: "ghost", onPointerDown: e => {
59
+ return (jsx("div", { className: cn(S.root, S[`size-${size}`], !value && S.empty, className), onKeyDown: onKeyDown, children: isRenaming ? (jsx(Input, { className: S.input, autoFocus: !isMobile, variant: "clean", size: inputSizeMap[size], placeholder: placeholder, value: name, onChange: handleInputChange, onBlur: handleBlur, onKeyDown: onKeyDown })) : (jsxs(Fragment, { children: [jsx("span", { className: S.value, children: children }), jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { className: S.button, variant: "ghost", onPointerDown: e => {
59
60
  e.preventDefault();
60
61
  e.stopPropagation();
61
62
  }, onClick: e => {
@@ -1,7 +1,7 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Renamer_root__26OvT{align-items:center;display:flex}.Renamer_value__G8b1D{padding-right:var(--p-2)}.Renamer_input__KRnGn label{border-radius:var(--p-2)!important;font-size:18px!important}.Renamer_empty__A7xx1 .Renamer_input__KRnGn label{background-color:var(--accent)/.1}.Renamer_button__jKMLc{opacity:.5;padding-left:0;transition:opacity .2s ease-out}.Renamer_root__26OvT:hover .Renamer_button__jKMLc{opacity:1}";
4
- var S = {"root":"Renamer_root__26OvT","value":"Renamer_value__G8b1D","input":"Renamer_input__KRnGn","empty":"Renamer_empty__A7xx1","button":"Renamer_button__jKMLc"};
3
+ var css_248z = ".Renamer_root__26OvT{align-items:center;display:flex}.Renamer_value__G8b1D{padding-right:var(--p-2)}.Renamer_size-lg__dfVhW.Renamer_root__26OvT .Renamer_input__KRnGn,.Renamer_size-lg__dfVhW.Renamer_root__26OvT .Renamer_value__G8b1D{color:var(--foreground);font-family:var(--font-family-heading);font-size:var(--text-2xl);font-weight:300;line-height:var(--text-2xl)}.Renamer_size-lg__dfVhW.Renamer_root__26OvT .Renamer_input__KRnGn{border-radius:var(--p-4);box-shadow:0 0 0 1px var(--border);height:auto;min-height:var(--text-2xl);padding:4px 12px 4px 0}.Renamer_empty__A7xx1.Renamer_size-lg__dfVhW.Renamer_root__26OvT .Renamer_input__KRnGn{background-color:var(--accent)/.1}.Renamer_button__jKMLc{opacity:.5;padding-left:0;transition:opacity .2s ease-out}.Renamer_root__26OvT:hover .Renamer_button__jKMLc{opacity:1}";
4
+ var S = {"root":"Renamer_root__26OvT","value":"Renamer_value__G8b1D","size-lg":"Renamer_size-lg__dfVhW","input":"Renamer_input__KRnGn","empty":"Renamer_empty__A7xx1","button":"Renamer_button__jKMLc"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { S as default };
@@ -12,12 +12,23 @@ import S from './WorkspaceAppSwitcher.styl.js';
12
12
  function entryKey(entry) {
13
13
  return entry.id;
14
14
  }
15
- function IconTile({ iconKey, accentMuted, accent, }) {
16
- const IconComponent = WORKSPACE_APP_ICONS[iconKey];
15
+ function renderIconContent(icon, iconKey) {
16
+ if (icon != null) {
17
+ return (jsx("span", { className: S.icon, "aria-hidden": true, children: icon }));
18
+ }
19
+ if (iconKey != null) {
20
+ const IconComponent = WORKSPACE_APP_ICONS[iconKey];
21
+ if (IconComponent) {
22
+ return jsx(IconComponent, { className: S.icon, "aria-hidden": true });
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ function IconTile({ icon, iconKey, accentMuted, accent, }) {
17
28
  return (jsx("span", { className: S.iconTile, style: {
18
29
  '--bg-color': accentMuted,
19
30
  '--fg-color': accent,
20
- }, children: jsx(IconComponent, { className: S.icon, "aria-hidden": true }) }));
31
+ }, children: renderIconContent(icon, iconKey) }));
21
32
  }
22
33
  function useResolvedApps(appsStorageKey, defaultApps) {
23
34
  const [apps, setApps] = useState(() => {
@@ -51,11 +62,11 @@ function WorkspaceAppSwitcher({ pathname, onNavigate, authenticated = true, defa
51
62
  if (!displayApp) {
52
63
  return null;
53
64
  }
54
- return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button, { variant: "ghost", className: S.trigger, "aria-label": "Select workspace app", children: [jsx(IconTile, { iconKey: displayApp.iconKey, accentMuted: displayApp.accentMuted, accent: displayApp.accent }), jsxs("span", { className: S.textCol, children: [jsx("span", { className: S.name, children: displayApp.displayName }), jsx("span", { className: S.sub, children: displayApp.subtitle })] }), jsx(ChevronDown, { className: S.chevron, size: 12, "aria-hidden": true })] }) }), jsx(DropdownMenuContent, { className: S.menuContent, align: "start", sideOffset: 8, elevation: "md", children: apps.map(entry => {
65
+ return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button, { variant: "ghost", className: S.trigger, "aria-label": "Select workspace app", children: [jsx(IconTile, { icon: displayApp.icon, iconKey: displayApp.iconKey, accentMuted: displayApp.accentMuted, accent: displayApp.accent }), jsxs("span", { className: S.textCol, children: [jsx("span", { className: S.name, children: displayApp.displayName }), jsx("span", { className: S.sub, children: displayApp.subtitle })] }), jsx(ChevronDown, { className: S.chevron, size: 12, "aria-hidden": true })] }) }), jsx(DropdownMenuContent, { className: S.menuContent, align: "start", sideOffset: 8, elevation: "md", children: apps.map(entry => {
55
66
  const active = current != null ? entryKey(entry) === entryKey(current) : false;
56
67
  return (jsxs(DropdownMenuItem, { className: cn(S.item, active && S.itemActive), onSelect: () => {
57
68
  onNavigate(entry.href);
58
- }, children: [jsx(IconTile, { iconKey: entry.iconKey, accentMuted: entry.accentMuted, accent: entry.accent }), jsxs("span", { className: S.textCol, children: [jsx("span", { className: S.name, children: entry.displayName }), jsx("span", { className: S.sub, children: entry.subtitle })] })] }, entry.id));
69
+ }, children: [jsx(IconTile, { icon: entry.icon, iconKey: entry.iconKey, accentMuted: entry.accentMuted, accent: entry.accent }), jsxs("span", { className: S.textCol, children: [jsx("span", { className: S.name, children: entry.displayName }), jsx("span", { className: S.sub, children: entry.subtitle })] })] }, entry.id));
59
70
  }) })] }));
60
71
  }
61
72
 
@@ -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)}}.WorkspaceAppSwitcher_trigger__s6qYT{align-items:center;background:transparent;border:none;border-radius:12px;color:inherit;cursor:pointer;display:flex;font:inherit;gap:var(--p-2);height:auto;margin-left:var(--p-3)!important;max-width:320px;padding:var(--p-1)!important;padding-right:var(--p-3)!important;text-align:left}.WorkspaceAppSwitcher_trigger__s6qYT:hover{background-color:var(--muted)}@media (max-width:768px){.WorkspaceAppSwitcher_trigger__s6qYT{gap:0;margin-left:0!important;max-width:none;padding-right:var(--p-1)!important}.WorkspaceAppSwitcher_trigger__s6qYT .WorkspaceAppSwitcher_chevron__7kAqO,.WorkspaceAppSwitcher_trigger__s6qYT .WorkspaceAppSwitcher_textCol__K1gfI{display:none}}.WorkspaceAppSwitcher_iconTile__tVDr8{align-items:center;border-radius:10px;color:var(--fg-color);display:flex;flex-shrink:0;height:40px;justify-content:center;position:relative;width:40px}.WorkspaceAppSwitcher_iconTile__tVDr8:after,.WorkspaceAppSwitcher_iconTile__tVDr8:before{border-radius:inherit;content:\"\";display:block;height:100%;position:absolute;width:100%}.WorkspaceAppSwitcher_iconTile__tVDr8:before{background-color:var(--background)}.WorkspaceAppSwitcher_iconTile__tVDr8:after{background-color:var(--bg-color)}.WorkspaceAppSwitcher_icon__Jgw14{color:var(--fg-color)!important;height:22px!important;width:22px!important;z-index:1}.WorkspaceAppSwitcher_textCol__K1gfI{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.WorkspaceAppSwitcher_name__ewMYP{color:var(--foreground);font-size:var(--text-sm);font-weight:600}.WorkspaceAppSwitcher_name__ewMYP,.WorkspaceAppSwitcher_sub__b7w1p{line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.WorkspaceAppSwitcher_sub__b7w1p{color:var(--muted-foreground);font-size:var(--text-xs)}.WorkspaceAppSwitcher_menuContent__4-UNY{max-width:360px;min-width:280px}.WorkspaceAppSwitcher_item__nnufY{align-items:center;cursor:pointer;display:flex;gap:var(--p-3);outline:none;padding:var(--p-3)}.WorkspaceAppSwitcher_itemActive__3mPlO{background-color:var(--muted)}";
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.WorkspaceAppSwitcher_trigger__s6qYT{align-items:center;background:transparent;border:none;border-radius:12px;color:inherit;cursor:pointer;display:flex;font:inherit;gap:var(--p-2);height:auto;margin-left:var(--p-3)!important;max-width:320px;padding:var(--p-1)!important;padding-right:var(--p-3)!important;text-align:left}.WorkspaceAppSwitcher_trigger__s6qYT:hover{background-color:var(--muted)}@media (max-width:768px){.WorkspaceAppSwitcher_trigger__s6qYT{gap:0;margin-left:0!important;max-width:none;padding-right:var(--p-1)!important}.WorkspaceAppSwitcher_trigger__s6qYT .WorkspaceAppSwitcher_chevron__7kAqO,.WorkspaceAppSwitcher_trigger__s6qYT .WorkspaceAppSwitcher_textCol__K1gfI{display:none}}.WorkspaceAppSwitcher_iconTile__tVDr8{align-items:center;border-radius:10px;color:var(--fg-color);display:flex;flex-shrink:0;height:40px;justify-content:center;position:relative;width:40px}.WorkspaceAppSwitcher_iconTile__tVDr8:after,.WorkspaceAppSwitcher_iconTile__tVDr8:before{border-radius:inherit;content:\"\";display:block;height:100%;position:absolute;width:100%}.WorkspaceAppSwitcher_iconTile__tVDr8:before{background-color:var(--background)}.WorkspaceAppSwitcher_iconTile__tVDr8:after{background-color:var(--bg-color)}.WorkspaceAppSwitcher_icon__Jgw14{align-items:center;display:flex;justify-content:center;z-index:1}.WorkspaceAppSwitcher_icon__Jgw14,.WorkspaceAppSwitcher_icon__Jgw14 svg{color:var(--fg-color)!important;height:22px!important;width:22px!important}.WorkspaceAppSwitcher_textCol__K1gfI{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.WorkspaceAppSwitcher_name__ewMYP{color:var(--foreground);font-size:var(--text-sm);font-weight:600}.WorkspaceAppSwitcher_name__ewMYP,.WorkspaceAppSwitcher_sub__b7w1p{line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.WorkspaceAppSwitcher_sub__b7w1p{color:var(--muted-foreground);font-size:var(--text-xs)}.WorkspaceAppSwitcher_menuContent__4-UNY{max-width:360px;min-width:280px}.WorkspaceAppSwitcher_item__nnufY{align-items:center;cursor:pointer;display:flex;gap:var(--p-3);outline:none;padding:var(--p-3)}.WorkspaceAppSwitcher_itemActive__3mPlO{background-color:var(--muted)}";
4
4
  var S = {"trigger":"WorkspaceAppSwitcher_trigger__s6qYT","textCol":"WorkspaceAppSwitcher_textCol__K1gfI","chevron":"WorkspaceAppSwitcher_chevron__7kAqO","iconTile":"WorkspaceAppSwitcher_iconTile__tVDr8","icon":"WorkspaceAppSwitcher_icon__Jgw14","name":"WorkspaceAppSwitcher_name__ewMYP","sub":"WorkspaceAppSwitcher_sub__b7w1p","menuContent":"WorkspaceAppSwitcher_menuContent__4-UNY","item":"WorkspaceAppSwitcher_item__nnufY","itemActive":"WorkspaceAppSwitcher_itemActive__3mPlO"};
5
5
  styleInject(css_248z);
6
6
 
@@ -12,7 +12,7 @@ import 'lucide-react';
12
12
  import S from './SybilionAppHeader.styl.js';
13
13
 
14
14
  function SybilionAppHeader({ pageHeaderId, actionsAnchorId = PAGE_HEADER_ACTIONS_ID, actionsAnchorClassName, actionsStart, actionsEnd, pathname, onNavigate, authenticated, defaultApps, appsStorageKey, logo, logoAreaClassName, welcomeBannerOffset, ...navUserHeaderProps }) {
15
- return (jsxs(AppHeaderPortal, { pageHeaderId: pageHeaderId, children: [jsx("div", { className: cn(S.logoArea, welcomeBannerOffset && S.logoAreaWithBanner, logoAreaClassName), children: jsx(Link, { to: "/", className: S.logoLink, children: logo ?? jsx(Logo, { size: "md", "aria-hidden": true }) }) }), jsx(WorkspaceAppSwitcher, { pathname: pathname, onNavigate: onNavigate, authenticated: authenticated, defaultApps: defaultApps, appsStorageKey: appsStorageKey }), jsx(Gap, {}), jsxs("div", { id: actionsAnchorId, className: cn(S.actionsAnchor, actionsAnchorClassName), children: [actionsStart, jsx(NavUserHeader, { ...navUserHeaderProps }), actionsEnd] })] }));
15
+ return (jsxs(AppHeaderPortal, { pageHeaderId: pageHeaderId, children: [jsx("div", { className: cn(S.logoArea, welcomeBannerOffset && S.logoAreaWithBanner, logoAreaClassName), children: jsx(Link, { to: "/", className: S.logoLink, children: logo ?? jsx(Logo, { size: "md", "aria-hidden": true }) }) }), jsx(WorkspaceAppSwitcher, { pathname: pathname, onNavigate: onNavigate, authenticated: authenticated, defaultApps: defaultApps, appsStorageKey: appsStorageKey }), jsx(Gap, {}), jsxs("div", { className: cn(S.actionsAnchor, actionsAnchorClassName), children: [jsx("div", { id: actionsAnchorId, className: S.pageActionsPortal }), actionsStart, jsx(NavUserHeader, { ...navUserHeaderProps }), actionsEnd] })] }));
16
16
  }
17
17
 
18
18
  export { SybilionAppHeader };
@@ -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)}}.SybilionAppHeader_actionsAnchor__ress2{align-items:center;display:flex;flex-shrink:0;gap:var(--p-4)}.SybilionAppHeader_logoArea__3HAhG{align-items:center;display:flex;gap:var(--p-2);left:40px;position:absolute;top:22px;z-index:10}@media (max-width:768px){.SybilionAppHeader_logoArea__3HAhG{left:32px}}@media (min-width:768px){[data-slot=sidebar-wrapper][data-state=expanded] .SybilionAppHeader_logoArea__3HAhG{position:fixed}}.SybilionAppHeader_logoAreaWithBanner__7Iy78{top:22px;top:calc(22px + var(--welcome-banner-height, 0px))}@media (min-width:768px){[data-slot=sidebar-wrapper][data-state=collapsed] .SybilionAppHeader_logoAreaWithBanner__7Iy78{top:22px}}.SybilionAppHeader_logoLink__bH-KX{align-items:center;color:var(--color-foreground);display:flex;font-size:1.5rem;font-weight:400;gap:.5rem;text-decoration:none;white-space:nowrap;width:-moz-fit-content;width:fit-content}.SybilionAppHeader_logoLink__bH-KX svg{display:inline-flex;flex-shrink:0;height:32px;transition:transform .1s ease-in-out;width:auto}.SybilionAppHeader_logoLink__bH-KX:hover svg{transform:scale(1.05)}";
4
- var S = {"actionsAnchor":"SybilionAppHeader_actionsAnchor__ress2","logoArea":"SybilionAppHeader_logoArea__3HAhG","logoAreaWithBanner":"SybilionAppHeader_logoAreaWithBanner__7Iy78","logoLink":"SybilionAppHeader_logoLink__bH-KX"};
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.SybilionAppHeader_actionsAnchor__ress2,.SybilionAppHeader_pageActionsPortal__9C5ww{align-items:center;display:flex;flex-shrink:0;gap:var(--p-4)}.SybilionAppHeader_pageActionsPortal__9C5ww:empty{display:none}.SybilionAppHeader_logoArea__3HAhG{align-items:center;display:flex;gap:var(--p-2);left:40px;position:absolute;top:22px;z-index:10}@media (max-width:768px){.SybilionAppHeader_logoArea__3HAhG{left:32px}}@media (min-width:768px){[data-slot=sidebar-wrapper][data-state=expanded] .SybilionAppHeader_logoArea__3HAhG{position:fixed}}.SybilionAppHeader_logoAreaWithBanner__7Iy78{top:22px;top:calc(22px + var(--welcome-banner-height, 0px))}@media (min-width:768px){[data-slot=sidebar-wrapper][data-state=collapsed] .SybilionAppHeader_logoAreaWithBanner__7Iy78{top:22px}}.SybilionAppHeader_logoLink__bH-KX{align-items:center;color:var(--color-foreground);display:flex;font-size:1.5rem;font-weight:400;gap:.5rem;text-decoration:none;white-space:nowrap;width:-moz-fit-content;width:fit-content}.SybilionAppHeader_logoLink__bH-KX svg{display:inline-flex;flex-shrink:0;height:32px;transition:transform .1s ease-in-out;width:auto}.SybilionAppHeader_logoLink__bH-KX:hover svg{transform:scale(1.05)}";
4
+ var S = {"actionsAnchor":"SybilionAppHeader_actionsAnchor__ress2","pageActionsPortal":"SybilionAppHeader_pageActionsPortal__9C5ww","logoArea":"SybilionAppHeader_logoArea__3HAhG","logoAreaWithBanner":"SybilionAppHeader_logoAreaWithBanner__7Iy78","logoLink":"SybilionAppHeader_logoLink__bH-KX"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { S as default };
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { WorkspaceAppIconKey } from './workspaceAppIcons';
2
3
  /** Path segment for slug apps: pathname matches `/apps/{id}` */
3
4
  export declare const WORKSPACE_APP_SLUG_BASE_PATH = "/apps";
@@ -7,7 +8,10 @@ export type WorkspaceAppEntry = {
7
8
  id: string;
8
9
  displayName: string;
9
10
  subtitle: string;
10
- iconKey: WorkspaceAppIconKey;
11
+ /** Custom icon for display; not persisted to localStorage. */
12
+ icon?: ReactNode;
13
+ /** Built-in icon lookup when `icon` is omitted (required for localStorage entries). */
14
+ iconKey?: WorkspaceAppIconKey;
11
15
  accent: string;
12
16
  accentMuted: string;
13
17
  href: string;
@@ -0,0 +1 @@
1
+ export default function ChatComposerPrefillPage(): import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.68",
3
+ "version": "1.3.71",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -48,7 +48,7 @@
48
48
  box-sizing border-box
49
49
 
50
50
  :global(.dark) &
51
- background-color var(--sb-gray-800)
51
+ background-color var(--sb-gray-900)
52
52
 
53
53
  .role-system
54
54
  align-items center
@@ -208,8 +208,11 @@ export function useChatPromptEditor({
208
208
  queueMicrotask(() => {
209
209
  const dom = chatPromptSafeEditorDom(editor);
210
210
  if (dom) syncChatPromptComposerHeight(dom, t);
211
+ if (!disabled && !editor.isDestroyed) {
212
+ editor.chain().focus('end').run();
213
+ }
211
214
  });
212
- }, [editor, prefillMessage]);
215
+ }, [editor, prefillMessage, disabled]);
213
216
 
214
217
  useLayoutEffect(() => {
215
218
  if (!editor) return;
@@ -5,12 +5,28 @@
5
5
  .value
6
6
  padding-right var(--p-2)
7
7
 
8
- .input
9
- label
10
- font-size 18px !important
11
- border-radius var(--p-2) !important
8
+ // Matches PageHeader .title h1 typography (page title context).
9
+ .size-lg.root
10
+ .value
11
+ font-family var(--font-family-heading)
12
+ font-weight 300
13
+ font-size var(--text-2xl)
14
+ line-height var(--text-2xl)
15
+ color var(--foreground)
12
16
 
13
- .empty .input label
17
+ .input
18
+ font-family var(--font-family-heading)
19
+ font-weight 300
20
+ font-size var(--text-2xl)
21
+ line-height var(--text-2xl)
22
+ color var(--foreground)
23
+ height auto
24
+ min-height var(--text-2xl)
25
+ padding 4px 12px 4px 0
26
+ border-radius var(--p-4)
27
+ box-shadow 0 0 0 1px var(--border)
28
+
29
+ .empty.size-lg.root .input
14
30
  background-color var(--accent) / 0.1
15
31
 
16
32
  .button
@@ -5,6 +5,7 @@ interface CssExports {
5
5
  'empty': string;
6
6
  'input': string;
7
7
  'root': string;
8
+ 'size-lg': string;
8
9
  'value': string;
9
10
  }
10
11
  export const cssExports: CssExports;
@@ -74,10 +74,11 @@ export function Renamer({
74
74
  setIsRenaming(true);
75
75
  }, [value]);
76
76
 
77
- const sizeMap = {
77
+ const inputSizeMap = {
78
78
  sm: 'sm' as const,
79
79
  md: 'md' as const,
80
- lg: 'lg' as const,
80
+ // lg typography comes from Renamer `.size-lg`; avoid Input size-lg (text-base).
81
+ lg: 'md' as const,
81
82
  };
82
83
 
83
84
  const buttonSizeMap = {
@@ -88,7 +89,7 @@ export function Renamer({
88
89
 
89
90
  return (
90
91
  <div
91
- className={cn(S.root, !value && S.empty, className)}
92
+ className={cn(S.root, S[`size-${size}`], !value && S.empty, className)}
92
93
  onKeyDown={onKeyDown}
93
94
  >
94
95
  {isRenaming ? (
@@ -96,7 +97,7 @@ export function Renamer({
96
97
  className={S.input}
97
98
  autoFocus={!isMobile}
98
99
  variant="clean"
99
- size={sizeMap[size]}
100
+ size={inputSizeMap[size]}
100
101
  placeholder={placeholder}
101
102
  value={name}
102
103
  onChange={handleInputChange}
@@ -59,10 +59,18 @@
59
59
 
60
60
  .icon
61
61
  z-index 1
62
+ display flex
63
+ align-items center
64
+ justify-content center
62
65
  width 22px !important
63
66
  height 22px !important
64
67
  color var(--fg-color) !important
65
68
 
69
+ :global(svg)
70
+ width 22px !important
71
+ height 22px !important
72
+ color var(--fg-color) !important
73
+
66
74
  .textCol
67
75
  display flex
68
76
  flex-direction column
@@ -1,5 +1,5 @@
1
1
  import cn from 'classnames';
2
- import type { CSSProperties } from 'react';
2
+ import type { CSSProperties, ReactNode } from 'react';
3
3
  import { useEffect, useState } from 'react';
4
4
 
5
5
  import { Button } from '#uilib/components/ui/Button/Button';
@@ -32,16 +32,37 @@ function entryKey(entry: WorkspaceAppEntry): string {
32
32
  return entry.id;
33
33
  }
34
34
 
35
+ function renderIconContent(
36
+ icon: ReactNode | undefined,
37
+ iconKey: WorkspaceAppEntry['iconKey'],
38
+ ): ReactNode {
39
+ if (icon != null) {
40
+ return (
41
+ <span className={S.icon} aria-hidden>
42
+ {icon}
43
+ </span>
44
+ );
45
+ }
46
+ if (iconKey != null) {
47
+ const IconComponent = WORKSPACE_APP_ICONS[iconKey];
48
+ if (IconComponent) {
49
+ return <IconComponent className={S.icon} aria-hidden />;
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
35
55
  function IconTile({
56
+ icon,
36
57
  iconKey,
37
58
  accentMuted,
38
59
  accent,
39
60
  }: {
40
- iconKey: WorkspaceAppEntry['iconKey'];
61
+ icon?: ReactNode;
62
+ iconKey?: WorkspaceAppEntry['iconKey'];
41
63
  accentMuted: string;
42
64
  accent: string;
43
65
  }) {
44
- const IconComponent = WORKSPACE_APP_ICONS[iconKey];
45
66
  return (
46
67
  <span
47
68
  className={S.iconTile}
@@ -52,7 +73,7 @@ function IconTile({
52
73
  } as CSSProperties
53
74
  }
54
75
  >
55
- <IconComponent className={S.icon} aria-hidden />
76
+ {renderIconContent(icon, iconKey)}
56
77
  </span>
57
78
  );
58
79
  }
@@ -116,6 +137,7 @@ export function WorkspaceAppSwitcher({
116
137
  aria-label="Select workspace app"
117
138
  >
118
139
  <IconTile
140
+ icon={displayApp.icon}
119
141
  iconKey={displayApp.iconKey}
120
142
  accentMuted={displayApp.accentMuted}
121
143
  accent={displayApp.accent}
@@ -146,6 +168,7 @@ export function WorkspaceAppSwitcher({
146
168
  }}
147
169
  >
148
170
  <IconTile
171
+ icon={entry.icon}
149
172
  iconKey={entry.iconKey}
150
173
  accentMuted={entry.accentMuted}
151
174
  accent={entry.accent}
@@ -1,3 +1,5 @@
1
+ import type { ReactNode } from 'react';
2
+
1
3
  import type { WorkspaceAppIconKey } from './workspaceAppIcons';
2
4
 
3
5
  /** Path segment for slug apps: pathname matches `/apps/{id}` */
@@ -9,7 +11,10 @@ export type WorkspaceAppEntry = {
9
11
  id: string;
10
12
  displayName: string;
11
13
  subtitle: string;
12
- iconKey: WorkspaceAppIconKey;
14
+ /** Custom icon for display; not persisted to localStorage. */
15
+ icon?: ReactNode;
16
+ /** Built-in icon lookup when `icon` is omitted (required for localStorage entries). */
17
+ iconKey?: WorkspaceAppIconKey;
13
18
  accent: string;
14
19
  accentMuted: string;
15
20
  href: string;
@@ -6,6 +6,15 @@
6
6
  gap var(--p-4)
7
7
  flex-shrink 0
8
8
 
9
+ .pageActionsPortal
10
+ display flex
11
+ align-items center
12
+ gap var(--p-4)
13
+ flex-shrink 0
14
+
15
+ &:empty
16
+ display none
17
+
9
18
  .logoArea
10
19
  position absolute
11
20
  top 22px
@@ -5,6 +5,7 @@ interface CssExports {
5
5
  'logoArea': string;
6
6
  'logoAreaWithBanner': string;
7
7
  'logoLink': string;
8
+ 'pageActionsPortal': string;
8
9
  }
9
10
  export const cssExports: CssExports;
10
11
  export default cssExports;
@@ -69,10 +69,8 @@ export function SybilionAppHeader({
69
69
  appsStorageKey={appsStorageKey}
70
70
  />
71
71
  <Gap />
72
- <div
73
- id={actionsAnchorId}
74
- className={cn(S.actionsAnchor, actionsAnchorClassName)}
75
- >
72
+ <div className={cn(S.actionsAnchor, actionsAnchorClassName)}>
73
+ <div id={actionsAnchorId} className={S.pageActionsPortal} />
76
74
  {actionsStart}
77
75
  <NavUserHeader {...navUserHeaderProps} />
78
76
  {actionsEnd}
@@ -0,0 +1,202 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+
3
+ import { Button } from '#uilib/components/ui/Button';
4
+ import {
5
+ ChatChrome,
6
+ type ChatSheetActions,
7
+ type Message,
8
+ MessageRole,
9
+ } from '#uilib/components/ui/Chat';
10
+ import { PageContentSection } from '#uilib/components/ui/Page';
11
+ import { ScrollRef } from '@homecode/ui';
12
+ import { MessageSquare } from 'lucide-react';
13
+
14
+ import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
15
+ import { DOCS_CHAT_USER_KEY } from '../docsConstants';
16
+ import { DocsHeaderActions } from '../docsHeaderActions';
17
+
18
+ const NO_QUICK_REPLY_KEYS: ReadonlySet<string> = new Set();
19
+
20
+ const SAMPLE_PREFILL =
21
+ 'Summarize revenue trends for Q4 and highlight outliers.';
22
+
23
+ const ASSISTANT_REPLY_TEXT =
24
+ 'Prefill demo reply — composer should stay editable after send.';
25
+
26
+ function makeMessage(role: MessageRole, text: string): Message {
27
+ return {
28
+ id: crypto.randomUUID(),
29
+ role,
30
+ text,
31
+ timestamp: Date.now(),
32
+ };
33
+ }
34
+
35
+ const DOCS_CHAT_PREFILL_SCOPE_ID = `${DOCS_CHAT_USER_KEY}-docs-composer-prefill`;
36
+
37
+ export default function ChatComposerPrefillPage() {
38
+ const [messages, setMessages] = useState<Message[]>([]);
39
+ const [isLoading, setIsLoading] = useState(false);
40
+ const [inlinePrefill, setInlinePrefill] = useState<string | null>(null);
41
+ const replyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
42
+ const scrollRef = useRef<ScrollRef>(null);
43
+ const sheetActionsRef = useRef<ChatSheetActions | null>(null);
44
+
45
+ useEffect(() => {
46
+ return () => {
47
+ if (replyTimeoutRef.current != null) {
48
+ clearTimeout(replyTimeoutRef.current);
49
+ }
50
+ };
51
+ }, []);
52
+
53
+ const isEmpty = messages.length === 0 && !isLoading;
54
+ const isLastMessageFromUser =
55
+ messages.length > 0 &&
56
+ messages[messages.length - 1]?.role === MessageRole.USER;
57
+
58
+ const onSubmit = useCallback(
59
+ (raw: string) => {
60
+ const text = raw.trim();
61
+ if (!text || isLoading) return;
62
+
63
+ setMessages(prev => [...prev, makeMessage(MessageRole.USER, text)]);
64
+ setIsLoading(true);
65
+ setInlinePrefill(null);
66
+
67
+ if (replyTimeoutRef.current != null) {
68
+ clearTimeout(replyTimeoutRef.current);
69
+ }
70
+ replyTimeoutRef.current = setTimeout(() => {
71
+ replyTimeoutRef.current = null;
72
+ setMessages(prev => [
73
+ ...prev,
74
+ makeMessage(MessageRole.ASSISTANT, ASSISTANT_REPLY_TEXT),
75
+ ]);
76
+ setIsLoading(false);
77
+ }, 900);
78
+ },
79
+ [isLoading],
80
+ );
81
+
82
+ const prefillInlineComposer = useCallback(() => {
83
+ setInlinePrefill(SAMPLE_PREFILL);
84
+ }, []);
85
+
86
+ const openSheetWithPrefill = useCallback(() => {
87
+ sheetActionsRef.current?.openNewChatWithPrefill(SAMPLE_PREFILL);
88
+ }, []);
89
+
90
+ const promptDeepLink = `/docs/chat-composer-prefill?prompt=${encodeURIComponent(SAMPLE_PREFILL)}`;
91
+
92
+ return (
93
+ <>
94
+ <AppPageHeader
95
+ breadcrumbs={[{ label: 'Chat' }, { label: 'Composer prefill' }]}
96
+ title="Chat — composer prefill & focus"
97
+ subheader="Prefill the TipTap composer via prefillMessage, ChatSheetActions.openNewChatWithPrefill, or a ?prompt= deep link. Cursor lands at end; message is not auto-sent."
98
+ actions={
99
+ <DocsHeaderActions
100
+ actionsRef={sheetActionsRef}
101
+ scopeId={DOCS_CHAT_PREFILL_SCOPE_ID}
102
+ triggerLabel={<MessageSquare size={20} />}
103
+ triggerAriaLabel="AI Assistant"
104
+ emptyState={{
105
+ title: 'Start a conversation',
106
+ description:
107
+ 'Use the buttons below or ?prompt= to pre-fill this composer.',
108
+ }}
109
+ />
110
+ }
111
+ />
112
+ <PageContentSection>
113
+ <p style={{ marginBottom: 12, fontSize: 14, lineHeight: 1.5 }}>
114
+ After prefill, the composer receives focus at the end of the text so
115
+ users can edit before sending. Wired through{' '}
116
+ <code style={{ fontSize: 13 }}>useChatPromptEditor</code> (
117
+ <code style={{ fontSize: 13 }}>prefillMessage</code>) and{' '}
118
+ <code style={{ fontSize: 13 }}>useChatPanelChromeModel</code> (
119
+ <code style={{ fontSize: 13 }}>promptPrefill</code> /{' '}
120
+ <code style={{ fontSize: 13 }}>openNewChatWithPrefill</code>).
121
+ </p>
122
+ <p
123
+ style={{
124
+ marginBottom: 16,
125
+ display: 'flex',
126
+ flexWrap: 'wrap',
127
+ gap: 8,
128
+ }}
129
+ >
130
+ <Button
131
+ type="button"
132
+ variant="outline"
133
+ size="sm"
134
+ onClick={prefillInlineComposer}
135
+ >
136
+ Prefill inline composer
137
+ </Button>
138
+ <Button
139
+ type="button"
140
+ variant="outline"
141
+ size="sm"
142
+ onClick={openSheetWithPrefill}
143
+ >
144
+ Open portal chat with prefill
145
+ </Button>
146
+ <Button type="button" variant="outline" size="sm" asChild>
147
+ <a href={promptDeepLink}>Try ?prompt= deep link</a>
148
+ </Button>
149
+ </p>
150
+ <h3 style={{ marginBottom: 8, fontSize: 14, fontWeight: 600 }}>
151
+ What to verify
152
+ </h3>
153
+ <ul
154
+ style={{
155
+ margin: '0 0 16px',
156
+ paddingLeft: 20,
157
+ fontSize: 14,
158
+ lineHeight: 1.6,
159
+ }}
160
+ >
161
+ <li>Composer shows sample text; caret at end (not start)</li>
162
+ <li>User can type or edit before Enter / submit</li>
163
+ <li>Prefill does not send until user submits</li>
164
+ <li>
165
+ Portal path: new session opens, panel visible, same focus behavior
166
+ </li>
167
+ <li>
168
+ Deep link strips <code style={{ fontSize: 13 }}>?prompt=</code> and
169
+ opens chat with prefill once
170
+ </li>
171
+ </ul>
172
+ <ChatChrome
173
+ showResizeHandle={false}
174
+ resizeHandle={undefined}
175
+ onClose={undefined}
176
+ isEmpty={isEmpty}
177
+ renderPresets={() => null}
178
+ messages={messages}
179
+ onQuickReply={() => {}}
180
+ suppressedQuickReplyKeys={NO_QUICK_REPLY_KEYS}
181
+ isLoading={isLoading}
182
+ scriptContinueLabel={undefined}
183
+ onScriptContinue={undefined}
184
+ showSyntheticBranchButtons={false}
185
+ unusedBranchKeys={[]}
186
+ showInlinePresets={false}
187
+ isLastMessageFromUser={isLastMessageFromUser}
188
+ scrollRef={scrollRef}
189
+ effectiveScopeId="docs-chat-composer-prefill-inline"
190
+ onPromptSubmit={onSubmit}
191
+ onChatDeleted={() => {}}
192
+ promptPrefill={inlinePrefill}
193
+ emptyState={{
194
+ title: 'Inline prefill demo',
195
+ description:
196
+ 'Click “Prefill inline composer” or type below. Portal and ?prompt= use the header ChatSheet.',
197
+ }}
198
+ />
199
+ </PageContentSection>
200
+ </>
201
+ );
202
+ }
@@ -139,6 +139,12 @@ export const DOC_REGISTRY: DocEntry[] = [
139
139
  section: 'Chat',
140
140
  load: () => import('./pages/ChatAttachmentsDropzonePage'),
141
141
  },
142
+ {
143
+ slug: 'chat-composer-prefill',
144
+ title: 'Chat composer prefill',
145
+ section: 'Chat',
146
+ load: () => import('./pages/ChatComposerPrefillPage'),
147
+ },
142
148
  {
143
149
  slug: 'checkbox',
144
150
  title: 'Checkbox',