@openhands/agent-canvas 1.0.0-alpha.6 → 1.0.0-alpha.7

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 (68) hide show
  1. package/README.md +41 -7
  2. package/bin/agent-canvas.mjs +9 -2
  3. package/build/assets/automation-detail-D7GEU0vR.js +1 -0
  4. package/build/assets/automations-list-CkVNsgzm.js +1 -0
  5. package/build/assets/conversation-COZAKz_K.js +1 -0
  6. package/build/assets/{conversation-D8scXOe7.js → conversation-DWcvnmds.js} +3 -1
  7. package/build/assets/conversation-panel-CZDStT0b.js +1 -0
  8. package/build/assets/conversation-websocket-context-DulnrIHh.js +3 -0
  9. package/build/assets/edit-automation-modal-C3bFxS2f.js +1 -0
  10. package/build/assets/git-control-bar-branch-button-Bm6rzSpo.js +27 -0
  11. package/build/assets/{home-D9fJfhQA.js → home-DR11ejqB.js} +1 -1
  12. package/build/assets/{manifest-6400820c.js → manifest-f041e61a.js} +1 -1
  13. package/build/assets/{messages-BfaEAG2q.js → messages-v-q35ObG.js} +1 -1
  14. package/build/assets/{root-6AdVEJBT.js → root-D2PVd51i.js} +1 -1
  15. package/build/assets/root-layout-B4QioBS6.js +2 -0
  16. package/build/assets/{shared-conversation-BfZNCsvo.js → shared-conversation-DQlzwdpo.js} +1 -1
  17. package/build/index.html +3 -3
  18. package/dist/components/features/backends/backend-selector.cjs +1 -1
  19. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  20. package/dist/components/features/backends/backend-selector.js +95 -95
  21. package/dist/components/features/backends/backend-selector.js.map +1 -1
  22. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  23. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  24. package/dist/components/features/chat/components/chat-input-actions.js +118 -118
  25. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  26. package/dist/components/features/chat/components/slash-command-menu.cjs +1 -1
  27. package/dist/components/features/chat/components/slash-command-menu.cjs.map +1 -1
  28. package/dist/components/features/chat/components/slash-command-menu.js +1 -1
  29. package/dist/components/features/chat/components/slash-command-menu.js.map +1 -1
  30. package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
  31. package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
  32. package/dist/components/features/sidebar/sidebar-rail-body.d.ts +1 -2
  33. package/dist/components/features/sidebar/sidebar-rail-body.js +104 -104
  34. package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
  35. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  36. package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
  37. package/dist/components/features/sidebar/sidebar.js +82 -83
  38. package/dist/components/features/sidebar/sidebar.js.map +1 -1
  39. package/dist/contexts/conversation-websocket-context.cjs +3 -3
  40. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  41. package/dist/contexts/conversation-websocket-context.js +36 -36
  42. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  43. package/dist/hooks/query/use-local-git-info.cjs +3 -1
  44. package/dist/hooks/query/use-local-git-info.cjs.map +1 -1
  45. package/dist/hooks/query/use-local-git-info.d.ts +2 -2
  46. package/dist/hooks/query/use-local-git-info.js +27 -24
  47. package/dist/hooks/query/use-local-git-info.js.map +1 -1
  48. package/dist/package.cjs +1 -1
  49. package/dist/package.cjs.map +1 -1
  50. package/dist/package.js +1 -1
  51. package/dist/package.js.map +1 -1
  52. package/dist/stores/error-message-store.cjs +1 -1
  53. package/dist/stores/error-message-store.cjs.map +1 -1
  54. package/dist/stores/error-message-store.d.ts +10 -1
  55. package/dist/stores/error-message-store.js +16 -3
  56. package/dist/stores/error-message-store.js.map +1 -1
  57. package/package.json +1 -1
  58. package/scripts/dev-static.mjs +6 -0
  59. package/scripts/dev-with-automation.mjs +6 -0
  60. package/scripts/static-server.mjs +85 -4
  61. package/build/assets/automation-detail-CQrtk33s.js +0 -1
  62. package/build/assets/automations-list-COmogz0S.js +0 -1
  63. package/build/assets/conversation-CeGMBOyB.js +0 -1
  64. package/build/assets/conversation-panel-DMz46ji-.js +0 -1
  65. package/build/assets/conversation-websocket-context-B0Gd3yiT.js +0 -3
  66. package/build/assets/edit-automation-modal-DnTHJrf1.js +0 -1
  67. package/build/assets/git-control-bar-branch-button-DhpPgadK.js +0 -27
  68. package/build/assets/root-layout-DvYGxAnr.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"chat-input-actions.js","names":[],"sources":["../../../../../src/components/features/chat/components/chat-input-actions.tsx"],"sourcesContent":["import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { useTranslation } from \"react-i18next\";\nimport { Cpu } from \"lucide-react\";\nimport { AgentStatus } from \"#/components/features/controls/agent-status\";\nimport { ChangeAgentButton } from \"../change-agent-button\";\nimport { ChatInputModel } from \"./chat-input-model\";\nimport { SwitchProfileButton } from \"../switch-profile-button\";\nimport { ChatAddFileButton } from \"../chat-add-file-button\";\nimport { ChatSendButton } from \"../chat-send-button\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport SettingsGearIcon from \"#/icons/settings-gear.svg?react\";\nimport CarretRightFillIcon from \"#/icons/carret-right-fill.svg?react\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport ThreeDotsVerticalIcon from \"#/icons/three-dots-vertical.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useUnifiedPauseConversation } from \"#/hooks/mutation/use-unified-stop-conversation\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { usePauseConversation } from \"#/hooks/mutation/use-pause-conversation\";\nimport { useResumeConversation } from \"#/hooks/mutation/use-resume-conversation\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useAcpModelContext } from \"#/hooks/use-acp-model-context\";\nimport { labelForAcpModel } from \"#/constants/acp-providers\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ToolsContextMenuIconText } from \"../../controls/tools-context-menu-icon-text\";\nimport { ContextMenuListItem } from \"../../context-menu/context-menu-list-item\";\nimport { ContextMenu } from \"#/ui/context-menu\";\nimport { Divider } from \"#/ui/divider\";\nimport { useClickOutsideElement } from \"#/hooks/use-click-outside-element\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlTransitionClassName } from \"#/utils/form-control-classes\";\n\ninterface ChatInputActionsProps {\n disabled: boolean;\n canSubmit?: boolean;\n onAddFileClick?: () => void;\n showButton?: boolean;\n buttonClassName?: string;\n handleSubmit?: () => void;\n}\n\nexport function ChatInputActions({\n disabled,\n canSubmit = true,\n onAddFileClick = () => {},\n showButton = true,\n buttonClassName = \"\",\n handleSubmit = () => {},\n}: ChatInputActionsProps) {\n const { t } = useTranslation(\"openhands\");\n const unifiedPauseMutation = useUnifiedPauseConversation();\n const pauseConversationMutation = usePauseConversation();\n const resumeConversationMutation = useResumeConversation();\n const { conversationId } = useOptionalConversationId();\n const { data: conversation } = useActiveConversation();\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n // Shared with ChatInputModel: routes the model affordance to ChatInputModel\n // (which knows how to show the ACP model) instead of SwitchProfileButton for\n // ACP conversations — and for the home screen when Settings → Agent already\n // selects an ACP agent, since the next conversation will inherit it.\n const { isAcpContext, destinationPath, destinationLabel } =\n useAcpModelContext();\n // Mirror ChatInputModel: ACP conversations show the provider's human label\n // (e.g. \"Claude Opus 4.7\") in the overflow model submenu, not the raw\n // ``acp_model`` id. OpenHands keeps the raw model string.\n const overflowModelLabel = isAcpContext\n ? (labelForAcpModel(conversation?.acp_server, conversation?.llm_model) ??\n conversation?.llm_model)\n : conversation?.llm_model;\n const webSocketStatus = useUnifiedWebSocketStatus();\n const { curAgentState } = useAgentState();\n const { conversationMode, setConversationMode } = useConversationStore();\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n const actionsRowRef = React.useRef<HTMLDivElement>(null);\n const rightSectionRef = React.useRef<HTMLDivElement>(null);\n const addFileRef = React.useRef<HTMLDivElement>(null);\n const codeRef = React.useRef<HTMLDivElement>(null);\n const modelRef = React.useRef<HTMLDivElement>(null);\n const overflowTriggerRef = React.useRef<HTMLButtonElement>(null);\n const [actionsRowWidth, setActionsRowWidth] = React.useState<number>(\n Number.POSITIVE_INFINITY,\n );\n const [rightSectionWidth, setRightSectionWidth] = React.useState(0);\n const [addFileWidth, setAddFileWidth] = React.useState(32);\n const [codeWidth, setCodeWidth] = React.useState(96);\n const [modelWidth, setModelWidth] = React.useState(120);\n const [isOverflowOpen, setIsOverflowOpen] = React.useState(false);\n const [activeSubmenu, setActiveSubmenu] = React.useState<\n \"agent\" | \"model\" | null\n >(null);\n const [overflowPortalStyle, setOverflowPortalStyle] =\n React.useState<React.CSSProperties>();\n\n React.useEffect(() => {\n const rowEl = actionsRowRef.current;\n const rightEl = rightSectionRef.current;\n const addEl = addFileRef.current;\n const codeEl = codeRef.current;\n const modelEl = modelRef.current;\n\n if (\n !rowEl ||\n !rightEl ||\n !addEl ||\n !modelEl ||\n (isCloud && !codeEl) ||\n typeof ResizeObserver === \"undefined\"\n ) {\n return;\n }\n\n const syncWidths = () => {\n const nextRowWidth = rowEl.getBoundingClientRect().width;\n const nextRightWidth = rightEl.getBoundingClientRect().width;\n const nextAddWidth = addEl.getBoundingClientRect().width;\n const nextModelWidth = modelEl.getBoundingClientRect().width;\n\n if (nextRowWidth > 0) setActionsRowWidth(nextRowWidth);\n if (nextRightWidth > 0) setRightSectionWidth(nextRightWidth);\n if (nextAddWidth > 0) setAddFileWidth(nextAddWidth);\n if (nextModelWidth > 0) setModelWidth(nextModelWidth);\n\n if (codeEl) {\n const nextCodeWidth = codeEl.getBoundingClientRect().width;\n if (nextCodeWidth > 0) setCodeWidth(nextCodeWidth);\n }\n };\n\n const observer = new ResizeObserver(() => {\n syncWidths();\n });\n\n observer.observe(rowEl);\n observer.observe(rightEl);\n observer.observe(addEl);\n observer.observe(modelEl);\n if (codeEl) {\n observer.observe(codeEl);\n }\n\n syncWidths();\n\n return () => observer.disconnect();\n }, [isCloud]);\n\n const handlePauseAgent = () => {\n if (!conversationId) return;\n pauseConversationMutation.mutate({ conversationId });\n };\n\n const handleResumeAgentClick = () => {\n if (!conversationId) return;\n resumeConversationMutation.mutate({ conversationId });\n };\n\n const isPausing =\n unifiedPauseMutation.isPending || pauseConversationMutation.isPending;\n\n const OVERFLOW_BUTTON_WIDTH = 28;\n const INLINE_GAP = 12;\n const ROOT_GAP = 8;\n\n const fitOptionalItems = React.useCallback(\n (availableWidth: number) => {\n let remaining = availableWidth;\n const next = {\n showCodeInline: false,\n showModelInline: false,\n };\n\n if (isCloud && remaining >= codeWidth) {\n next.showCodeInline = true;\n remaining -= codeWidth + INLINE_GAP;\n }\n\n if (remaining >= modelWidth) {\n next.showModelInline = true;\n }\n\n return next;\n },\n [isCloud, codeWidth, modelWidth],\n );\n\n const leftBaseWidth =\n actionsRowWidth - rightSectionWidth - ROOT_GAP - addFileWidth - INLINE_GAP;\n\n const fitWithoutOverflow = fitOptionalItems(leftBaseWidth);\n const allOptionalFit =\n (!isCloud || fitWithoutOverflow.showCodeInline) &&\n fitWithoutOverflow.showModelInline;\n\n const fitWithOverflow = allOptionalFit\n ? fitWithoutOverflow\n : fitOptionalItems(leftBaseWidth - OVERFLOW_BUTTON_WIDTH - INLINE_GAP);\n\n const showCodeInline = !isCloud ? false : fitWithOverflow.showCodeInline;\n const showModelInline = fitWithOverflow.showModelInline;\n const showAddFileInline = true;\n const showAgentStatusInline = actionsRowWidth >= 360;\n\n const hasOverflowItems =\n !showAddFileInline || (isCloud && !showCodeInline) || !showModelInline;\n\n React.useEffect(() => {\n if (!hasOverflowItems) {\n setIsOverflowOpen(false);\n setActiveSubmenu(null);\n }\n }, [hasOverflowItems]);\n\n const overflowMenuRef = useClickOutsideElement<HTMLUListElement>(() => {\n setIsOverflowOpen(false);\n setActiveSubmenu(null);\n });\n\n const isAgentSwitcherDisabled =\n curAgentState === AgentState.RUNNING ||\n isCreatingConversation ||\n webSocketStatus !== \"OPEN\";\n\n const closeOverflowMenus = () => {\n setActiveSubmenu(null);\n setIsOverflowOpen(false);\n };\n\n React.useLayoutEffect(() => {\n if (!isOverflowOpen || !overflowTriggerRef.current) {\n return;\n }\n\n const trigger = overflowTriggerRef.current;\n\n const updatePosition = () => {\n const rect = trigger.getBoundingClientRect();\n const GAP = 8;\n setOverflowPortalStyle({\n position: \"fixed\",\n top: rect.top - GAP,\n left: rect.left,\n transform: \"translateY(-100%)\",\n zIndex: 9999,\n });\n };\n\n updatePosition();\n window.addEventListener(\"resize\", updatePosition);\n window.addEventListener(\"scroll\", updatePosition, true);\n\n return () => {\n window.removeEventListener(\"resize\", updatePosition);\n window.removeEventListener(\"scroll\", updatePosition, true);\n };\n }, [isOverflowOpen]);\n\n const overflowMenu = (\n <ContextMenu\n ref={overflowMenuRef}\n testId=\"chat-input-overflow-menu\"\n position=\"top\"\n alignment=\"left\"\n className=\"!static !top-auto !bottom-auto !left-auto !right-auto !mt-0 overflow-visible min-w-[200px]\"\n >\n {isCloud && !showCodeInline && (\n <div className=\"relative group/overflow-agent\">\n <ContextMenuListItem\n testId=\"overflow-agent-button\"\n onClick={() =>\n setActiveSubmenu((current) =>\n current === \"agent\" ? null : \"agent\",\n )\n }\n isDisabled={isAgentSwitcherDisabled}\n >\n <ToolsContextMenuIconText\n icon={<CodePillIcon className=\"h-[11px] w-[11px]\" />}\n text={\n conversationMode === \"code\"\n ? t(I18nKey.COMMON$CODE)\n : t(I18nKey.COMMON$PLAN)\n }\n rightIcon={<CarretRightFillIcon width={10} height={10} />}\n />\n </ContextMenuListItem>\n {!isAgentSwitcherDisabled && (\n <div\n className={cn(\n \"absolute left-full top-[-4px] z-60 opacity-0 invisible pointer-events-none transition-all duration-200 ml-[1px]\",\n \"group-hover/overflow-agent:opacity-100 group-hover/overflow-agent:visible group-hover/overflow-agent:pointer-events-auto\",\n \"hover:opacity-100 hover:visible hover:pointer-events-auto\",\n activeSubmenu === \"agent\" &&\n \"opacity-100 visible pointer-events-auto\",\n )}\n >\n <ContextMenu\n testId=\"overflow-agent-submenu\"\n className=\"overflow-visible min-w-[195px] gap-0\"\n >\n <ContextMenuListItem\n testId=\"overflow-agent-code\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n closeOverflowMenus();\n }}\n >\n <ToolsContextMenuIconText\n icon={<CodePillIcon className=\"h-[11px] w-[11px]\" />}\n text={t(I18nKey.COMMON$CODE)}\n />\n </ContextMenuListItem>\n <ContextMenuListItem\n testId=\"overflow-agent-plan\"\n onClick={(event) => {\n handlePlanClick(event);\n closeOverflowMenus();\n }}\n >\n <ToolsContextMenuIconText\n icon={\n <LessonPlanIcon\n width={16}\n height={16}\n color=\"currentColor\"\n />\n }\n text={t(I18nKey.COMMON$PLAN)}\n />\n </ContextMenuListItem>\n </ContextMenu>\n </div>\n )}\n </div>\n )}\n {!showModelInline && (\n <div className=\"relative group/overflow-model\">\n <ContextMenuListItem\n testId=\"overflow-model-button\"\n onClick={() =>\n setActiveSubmenu((current) =>\n current === \"model\" ? null : \"model\",\n )\n }\n >\n <ToolsContextMenuIconText\n icon={<Cpu width={16} height={16} strokeWidth={2} aria-hidden />}\n text=\"Model\"\n rightIcon={<CarretRightFillIcon width={10} height={10} />}\n />\n </ContextMenuListItem>\n <div\n className={cn(\n \"absolute left-full top-[-4px] z-60 opacity-0 invisible pointer-events-none transition-all duration-200 ml-[1px]\",\n \"group-hover/overflow-model:opacity-100 group-hover/overflow-model:visible group-hover/overflow-model:pointer-events-auto\",\n \"hover:opacity-100 hover:visible hover:pointer-events-auto\",\n activeSubmenu === \"model\" &&\n \"opacity-100 visible pointer-events-auto\",\n )}\n >\n <ContextMenu\n testId=\"overflow-model-submenu\"\n className=\"overflow-visible min-w-[220px] max-w-[320px] gap-0\"\n >\n <li className=\"text-sm\">\n <div className=\"p-2 leading-5 text-[var(--oh-foreground)] break-all\">\n {overflowModelLabel}\n </div>\n </li>\n <Divider inset=\"menu\" />\n <li className=\"text-sm\">\n <NavigationLink\n to={destinationPath}\n onClick={closeOverflowMenus}\n className={cn(\n \"group flex h-[30px] items-center gap-2 rounded p-2 leading-5 text-[var(--oh-foreground)] hover:bg-[var(--oh-interactive-hover)]\",\n formControlTransitionClassName,\n )}\n >\n <SettingsGearIcon\n width={16}\n height={16}\n className={cn(\n \"shrink-0 text-[var(--oh-muted)] group-hover:text-[var(--oh-foreground)]\",\n formControlTransitionClassName,\n )}\n aria-hidden\n />\n <span>{destinationLabel}</span>\n </NavigationLink>\n </li>\n </ContextMenu>\n </div>\n </div>\n )}\n </ContextMenu>\n );\n\n return (\n <div\n ref={actionsRowRef}\n className=\"w-full min-w-0 flex items-center justify-between gap-2\"\n >\n <div className=\"flex min-w-0 items-center gap-1\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div ref={addFileRef} className={cn(!showAddFileInline && \"hidden\")}>\n <ChatAddFileButton\n disabled={disabled}\n handleFileIconClick={onAddFileClick}\n />\n </div>\n {isCloud && (\n <div ref={codeRef} className={cn(!showCodeInline && \"hidden\")}>\n <ChangeAgentButton />\n </div>\n )}\n <div ref={modelRef} className={cn(!showModelInline && \"hidden\")}>\n {isCloud || isAcpContext ? (\n <ChatInputModel />\n ) : (\n <SwitchProfileButton />\n )}\n </div>\n\n {hasOverflowItems && (\n <div className=\"relative shrink-0\">\n <button\n ref={overflowTriggerRef}\n type=\"button\"\n className={cn(\n \"flex size-6 items-center justify-center rounded-full text-[var(--oh-muted)]\",\n formControlTransitionClassName,\n \"hover:bg-white/10 hover:text-white cursor-pointer\",\n )}\n aria-label=\"More input actions\"\n aria-expanded={isOverflowOpen}\n aria-haspopup=\"menu\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n setIsOverflowOpen((open) => !open);\n }}\n >\n <ThreeDotsVerticalIcon\n width={16}\n height={16}\n color=\"currentColor\"\n />\n </button>\n\n {isOverflowOpen &&\n typeof document !== \"undefined\" &&\n overflowPortalStyle &&\n ReactDOM.createPortal(\n <div style={overflowPortalStyle}>{overflowMenu}</div>,\n document.body,\n )}\n </div>\n )}\n </div>\n </div>\n <div\n ref={rightSectionRef}\n className=\"ml-auto flex shrink-0 items-center gap-2\"\n >\n {showAgentStatusInline && conversationId && (\n <AgentStatus\n handleStop={handlePauseAgent}\n handleResumeAgent={handleResumeAgentClick}\n disabled={disabled}\n isPausing={isPausing}\n />\n )}\n {showButton && (\n <ChatSendButton\n buttonClassName={buttonClassName}\n handleSubmit={handleSubmit}\n disabled={disabled || !canSubmit}\n />\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat-input-actions.js","names":[],"sources":["../../../../../src/components/features/chat/components/chat-input-actions.tsx"],"sourcesContent":["import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { useTranslation } from \"react-i18next\";\nimport { Cpu } from \"lucide-react\";\nimport { AgentStatus } from \"#/components/features/controls/agent-status\";\nimport { ChangeAgentButton } from \"../change-agent-button\";\nimport { ChatInputModel } from \"./chat-input-model\";\nimport { SwitchProfileButton } from \"../switch-profile-button\";\nimport { ChatAddFileButton } from \"../chat-add-file-button\";\nimport { ChatSendButton } from \"../chat-send-button\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport SettingsGearIcon from \"#/icons/settings-gear.svg?react\";\nimport CarretRightFillIcon from \"#/icons/carret-right-fill.svg?react\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport ThreeDotsVerticalIcon from \"#/icons/three-dots-vertical.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useUnifiedPauseConversation } from \"#/hooks/mutation/use-unified-stop-conversation\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { usePauseConversation } from \"#/hooks/mutation/use-pause-conversation\";\nimport { useResumeConversation } from \"#/hooks/mutation/use-resume-conversation\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useAcpModelContext } from \"#/hooks/use-acp-model-context\";\nimport { labelForAcpModel } from \"#/constants/acp-providers\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ToolsContextMenuIconText } from \"../../controls/tools-context-menu-icon-text\";\nimport { ContextMenuListItem } from \"../../context-menu/context-menu-list-item\";\nimport { ContextMenu } from \"#/ui/context-menu\";\nimport { Divider } from \"#/ui/divider\";\nimport { useClickOutsideElement } from \"#/hooks/use-click-outside-element\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlTransitionClassName } from \"#/utils/form-control-classes\";\n\ninterface ChatInputActionsProps {\n disabled: boolean;\n canSubmit?: boolean;\n onAddFileClick?: () => void;\n showButton?: boolean;\n buttonClassName?: string;\n handleSubmit?: () => void;\n}\n\nexport function ChatInputActions({\n disabled,\n canSubmit = true,\n onAddFileClick = () => {},\n showButton = true,\n buttonClassName = \"\",\n handleSubmit = () => {},\n}: ChatInputActionsProps) {\n const { t } = useTranslation(\"openhands\");\n const unifiedPauseMutation = useUnifiedPauseConversation();\n const pauseConversationMutation = usePauseConversation();\n const resumeConversationMutation = useResumeConversation();\n const { conversationId } = useOptionalConversationId();\n const { data: conversation } = useActiveConversation();\n const { backend } = useActiveBackend();\n const isCloud = backend.kind === \"cloud\";\n // Shared with ChatInputModel: routes the model affordance to ChatInputModel\n // (which knows how to show the ACP model) instead of SwitchProfileButton for\n // ACP conversations — and for the home screen when Settings → Agent already\n // selects an ACP agent, since the next conversation will inherit it.\n const { isAcpContext, destinationPath, destinationLabel } =\n useAcpModelContext();\n // Mirror ChatInputModel: ACP conversations show the provider's human label\n // (e.g. \"Claude Opus 4.7\") in the overflow model submenu, not the raw\n // ``acp_model`` id. OpenHands keeps the raw model string.\n const overflowModelLabel = isAcpContext\n ? (labelForAcpModel(conversation?.acp_server, conversation?.llm_model) ??\n conversation?.llm_model)\n : conversation?.llm_model;\n // The change-agent button can never be enabled on the home page (no\n // conversation → no WebSocket → permanently disabled), so hide it there\n // entirely. It is still shown inside a conversation on cloud backends.\n const showChangeAgentButton = isCloud && Boolean(conversationId);\n const webSocketStatus = useUnifiedWebSocketStatus();\n const { curAgentState } = useAgentState();\n const { conversationMode, setConversationMode } = useConversationStore();\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n const actionsRowRef = React.useRef<HTMLDivElement>(null);\n const rightSectionRef = React.useRef<HTMLDivElement>(null);\n const addFileRef = React.useRef<HTMLDivElement>(null);\n const codeRef = React.useRef<HTMLDivElement>(null);\n const modelRef = React.useRef<HTMLDivElement>(null);\n const overflowTriggerRef = React.useRef<HTMLButtonElement>(null);\n const [actionsRowWidth, setActionsRowWidth] = React.useState<number>(\n Number.POSITIVE_INFINITY,\n );\n const [rightSectionWidth, setRightSectionWidth] = React.useState(0);\n const [addFileWidth, setAddFileWidth] = React.useState(32);\n const [codeWidth, setCodeWidth] = React.useState(96);\n const [modelWidth, setModelWidth] = React.useState(120);\n const [isOverflowOpen, setIsOverflowOpen] = React.useState(false);\n const [activeSubmenu, setActiveSubmenu] = React.useState<\n \"agent\" | \"model\" | null\n >(null);\n const [overflowPortalStyle, setOverflowPortalStyle] =\n React.useState<React.CSSProperties>();\n\n React.useEffect(() => {\n const rowEl = actionsRowRef.current;\n const rightEl = rightSectionRef.current;\n const addEl = addFileRef.current;\n const codeEl = codeRef.current;\n const modelEl = modelRef.current;\n\n if (\n !rowEl ||\n !rightEl ||\n !addEl ||\n !modelEl ||\n (showChangeAgentButton && !codeEl) ||\n typeof ResizeObserver === \"undefined\"\n ) {\n return;\n }\n\n const syncWidths = () => {\n const nextRowWidth = rowEl.getBoundingClientRect().width;\n const nextRightWidth = rightEl.getBoundingClientRect().width;\n const nextAddWidth = addEl.getBoundingClientRect().width;\n const nextModelWidth = modelEl.getBoundingClientRect().width;\n\n if (nextRowWidth > 0) setActionsRowWidth(nextRowWidth);\n if (nextRightWidth > 0) setRightSectionWidth(nextRightWidth);\n if (nextAddWidth > 0) setAddFileWidth(nextAddWidth);\n if (nextModelWidth > 0) setModelWidth(nextModelWidth);\n\n if (codeEl) {\n const nextCodeWidth = codeEl.getBoundingClientRect().width;\n if (nextCodeWidth > 0) setCodeWidth(nextCodeWidth);\n }\n };\n\n const observer = new ResizeObserver(() => {\n syncWidths();\n });\n\n observer.observe(rowEl);\n observer.observe(rightEl);\n observer.observe(addEl);\n observer.observe(modelEl);\n if (codeEl) {\n observer.observe(codeEl);\n }\n\n syncWidths();\n\n return () => observer.disconnect();\n }, [showChangeAgentButton]);\n\n const handlePauseAgent = () => {\n if (!conversationId) return;\n pauseConversationMutation.mutate({ conversationId });\n };\n\n const handleResumeAgentClick = () => {\n if (!conversationId) return;\n resumeConversationMutation.mutate({ conversationId });\n };\n\n const isPausing =\n unifiedPauseMutation.isPending || pauseConversationMutation.isPending;\n\n const OVERFLOW_BUTTON_WIDTH = 28;\n const INLINE_GAP = 12;\n const ROOT_GAP = 8;\n\n const fitOptionalItems = React.useCallback(\n (availableWidth: number) => {\n let remaining = availableWidth;\n const next = {\n showCodeInline: false,\n showModelInline: false,\n };\n\n if (showChangeAgentButton && remaining >= codeWidth) {\n next.showCodeInline = true;\n remaining -= codeWidth + INLINE_GAP;\n }\n\n if (remaining >= modelWidth) {\n next.showModelInline = true;\n }\n\n return next;\n },\n [showChangeAgentButton, codeWidth, modelWidth],\n );\n\n const leftBaseWidth =\n actionsRowWidth - rightSectionWidth - ROOT_GAP - addFileWidth - INLINE_GAP;\n\n const fitWithoutOverflow = fitOptionalItems(leftBaseWidth);\n const allOptionalFit =\n (!showChangeAgentButton || fitWithoutOverflow.showCodeInline) &&\n fitWithoutOverflow.showModelInline;\n\n const fitWithOverflow = allOptionalFit\n ? fitWithoutOverflow\n : fitOptionalItems(leftBaseWidth - OVERFLOW_BUTTON_WIDTH - INLINE_GAP);\n\n const showCodeInline = !showChangeAgentButton\n ? false\n : fitWithOverflow.showCodeInline;\n const showModelInline = fitWithOverflow.showModelInline;\n const showAddFileInline = true;\n const showAgentStatusInline = actionsRowWidth >= 360;\n\n const hasOverflowItems =\n !showAddFileInline ||\n (showChangeAgentButton && !showCodeInline) ||\n !showModelInline;\n\n React.useEffect(() => {\n if (!hasOverflowItems) {\n setIsOverflowOpen(false);\n setActiveSubmenu(null);\n }\n }, [hasOverflowItems]);\n\n const overflowMenuRef = useClickOutsideElement<HTMLUListElement>(() => {\n setIsOverflowOpen(false);\n setActiveSubmenu(null);\n });\n\n const isAgentSwitcherDisabled =\n curAgentState === AgentState.RUNNING ||\n isCreatingConversation ||\n webSocketStatus !== \"OPEN\";\n\n const closeOverflowMenus = () => {\n setActiveSubmenu(null);\n setIsOverflowOpen(false);\n };\n\n React.useLayoutEffect(() => {\n if (!isOverflowOpen || !overflowTriggerRef.current) {\n return;\n }\n\n const trigger = overflowTriggerRef.current;\n\n const updatePosition = () => {\n const rect = trigger.getBoundingClientRect();\n const GAP = 8;\n setOverflowPortalStyle({\n position: \"fixed\",\n top: rect.top - GAP,\n left: rect.left,\n transform: \"translateY(-100%)\",\n zIndex: 9999,\n });\n };\n\n updatePosition();\n window.addEventListener(\"resize\", updatePosition);\n window.addEventListener(\"scroll\", updatePosition, true);\n\n return () => {\n window.removeEventListener(\"resize\", updatePosition);\n window.removeEventListener(\"scroll\", updatePosition, true);\n };\n }, [isOverflowOpen]);\n\n const overflowMenu = (\n <ContextMenu\n ref={overflowMenuRef}\n testId=\"chat-input-overflow-menu\"\n position=\"top\"\n alignment=\"left\"\n className=\"!static !top-auto !bottom-auto !left-auto !right-auto !mt-0 overflow-visible min-w-[200px]\"\n >\n {showChangeAgentButton && !showCodeInline && (\n <div className=\"relative group/overflow-agent\">\n <ContextMenuListItem\n testId=\"overflow-agent-button\"\n onClick={() =>\n setActiveSubmenu((current) =>\n current === \"agent\" ? null : \"agent\",\n )\n }\n isDisabled={isAgentSwitcherDisabled}\n >\n <ToolsContextMenuIconText\n icon={<CodePillIcon className=\"h-[11px] w-[11px]\" />}\n text={\n conversationMode === \"code\"\n ? t(I18nKey.COMMON$CODE)\n : t(I18nKey.COMMON$PLAN)\n }\n rightIcon={<CarretRightFillIcon width={10} height={10} />}\n />\n </ContextMenuListItem>\n {!isAgentSwitcherDisabled && (\n <div\n className={cn(\n \"absolute left-full top-[-4px] z-60 opacity-0 invisible pointer-events-none transition-all duration-200 ml-[1px]\",\n \"group-hover/overflow-agent:opacity-100 group-hover/overflow-agent:visible group-hover/overflow-agent:pointer-events-auto\",\n \"hover:opacity-100 hover:visible hover:pointer-events-auto\",\n activeSubmenu === \"agent\" &&\n \"opacity-100 visible pointer-events-auto\",\n )}\n >\n <ContextMenu\n testId=\"overflow-agent-submenu\"\n className=\"overflow-visible min-w-[195px] gap-0\"\n >\n <ContextMenuListItem\n testId=\"overflow-agent-code\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n closeOverflowMenus();\n }}\n >\n <ToolsContextMenuIconText\n icon={<CodePillIcon className=\"h-[11px] w-[11px]\" />}\n text={t(I18nKey.COMMON$CODE)}\n />\n </ContextMenuListItem>\n <ContextMenuListItem\n testId=\"overflow-agent-plan\"\n onClick={(event) => {\n handlePlanClick(event);\n closeOverflowMenus();\n }}\n >\n <ToolsContextMenuIconText\n icon={\n <LessonPlanIcon\n width={16}\n height={16}\n color=\"currentColor\"\n />\n }\n text={t(I18nKey.COMMON$PLAN)}\n />\n </ContextMenuListItem>\n </ContextMenu>\n </div>\n )}\n </div>\n )}\n {!showModelInline && (\n <div className=\"relative group/overflow-model\">\n <ContextMenuListItem\n testId=\"overflow-model-button\"\n onClick={() =>\n setActiveSubmenu((current) =>\n current === \"model\" ? null : \"model\",\n )\n }\n >\n <ToolsContextMenuIconText\n icon={<Cpu width={16} height={16} strokeWidth={2} aria-hidden />}\n text=\"Model\"\n rightIcon={<CarretRightFillIcon width={10} height={10} />}\n />\n </ContextMenuListItem>\n <div\n className={cn(\n \"absolute left-full top-[-4px] z-60 opacity-0 invisible pointer-events-none transition-all duration-200 ml-[1px]\",\n \"group-hover/overflow-model:opacity-100 group-hover/overflow-model:visible group-hover/overflow-model:pointer-events-auto\",\n \"hover:opacity-100 hover:visible hover:pointer-events-auto\",\n activeSubmenu === \"model\" &&\n \"opacity-100 visible pointer-events-auto\",\n )}\n >\n <ContextMenu\n testId=\"overflow-model-submenu\"\n className=\"overflow-visible min-w-[220px] max-w-[320px] gap-0\"\n >\n <li className=\"text-sm\">\n <div className=\"p-2 leading-5 text-[var(--oh-foreground)] break-all\">\n {overflowModelLabel}\n </div>\n </li>\n <Divider inset=\"menu\" />\n <li className=\"text-sm\">\n <NavigationLink\n to={destinationPath}\n onClick={closeOverflowMenus}\n className={cn(\n \"group flex h-[30px] items-center gap-2 rounded p-2 leading-5 text-[var(--oh-foreground)] hover:bg-[var(--oh-interactive-hover)]\",\n formControlTransitionClassName,\n )}\n >\n <SettingsGearIcon\n width={16}\n height={16}\n className={cn(\n \"shrink-0 text-[var(--oh-muted)] group-hover:text-[var(--oh-foreground)]\",\n formControlTransitionClassName,\n )}\n aria-hidden\n />\n <span>{destinationLabel}</span>\n </NavigationLink>\n </li>\n </ContextMenu>\n </div>\n </div>\n )}\n </ContextMenu>\n );\n\n return (\n <div\n ref={actionsRowRef}\n className=\"w-full min-w-0 flex items-center justify-between gap-2\"\n >\n <div className=\"flex min-w-0 items-center gap-1\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div ref={addFileRef} className={cn(!showAddFileInline && \"hidden\")}>\n <ChatAddFileButton\n disabled={disabled}\n handleFileIconClick={onAddFileClick}\n />\n </div>\n {showChangeAgentButton && (\n <div ref={codeRef} className={cn(!showCodeInline && \"hidden\")}>\n <ChangeAgentButton />\n </div>\n )}\n <div ref={modelRef} className={cn(!showModelInline && \"hidden\")}>\n {isCloud || isAcpContext ? (\n <ChatInputModel />\n ) : (\n <SwitchProfileButton />\n )}\n </div>\n\n {hasOverflowItems && (\n <div className=\"relative shrink-0\">\n <button\n ref={overflowTriggerRef}\n type=\"button\"\n className={cn(\n \"flex size-6 items-center justify-center rounded-full text-[var(--oh-muted)]\",\n formControlTransitionClassName,\n \"hover:bg-white/10 hover:text-white cursor-pointer\",\n )}\n aria-label=\"More input actions\"\n aria-expanded={isOverflowOpen}\n aria-haspopup=\"menu\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n setIsOverflowOpen((open) => !open);\n }}\n >\n <ThreeDotsVerticalIcon\n width={16}\n height={16}\n color=\"currentColor\"\n />\n </button>\n\n {isOverflowOpen &&\n typeof document !== \"undefined\" &&\n overflowPortalStyle &&\n ReactDOM.createPortal(\n <div style={overflowPortalStyle}>{overflowMenu}</div>,\n document.body,\n )}\n </div>\n )}\n </div>\n </div>\n <div\n ref={rightSectionRef}\n className=\"ml-auto flex shrink-0 items-center gap-2\"\n >\n {showAgentStatusInline && conversationId && (\n <AgentStatus\n handleStop={handlePauseAgent}\n handleResumeAgent={handleResumeAgentClick}\n disabled={disabled}\n isPausing={isPausing}\n />\n )}\n {showButton && (\n <ChatSendButton\n buttonClassName={buttonClassName}\n handleSubmit={handleSubmit}\n disabled={disabled || !canSubmit}\n />\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  const e=require(`../../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../../utils/utils.cjs`),r=require(`../../../../ui/typography.cjs`);let i=require(`react`);i=e.__toESM(i,1);let a=require(`react/jsx-runtime`);function o(e){return e.replace(/!\[([^\]]*)\]\([^)]*\)/g,`$1`).replace(/\[([^\]]*)\]\([^)]*\)/g,`$1`).replace(/\*{3}(.+?)\*{3}/g,`$1`).replace(/\*{2}(.+?)\*{2}/g,`$1`).replace(/\*(.+?)\*/g,`$1`).replace(/_{3}(.+?)_{3}/g,`$1`).replace(/_{2}(.+?)_{2}/g,`$1`).replace(/_(.+?)_/g,`$1`).replace(/`(.+?)`/g,`$1`).replace(/~~(.+?)~~/g,`$1`)}function s(e){let t=e,n=e.match(/^---\s*\n([\s\S]*?)\n---/);if(n){let r=n[1].match(/^description:\s*(.+)$/m);if(r){let e=r[1].trim();return(e.startsWith(`"`)&&e.endsWith(`"`)||e.startsWith(`'`)&&e.endsWith(`'`))&&(e=e.slice(1,-1)),o(e)}t=e.slice(n[0].length)}let r=t.split(`
2
- `).map(e=>e.trim()).find(e=>e.length>0&&!e.startsWith(`#`)&&e!==`---`);if(!r)return null;let i=o(r);return i.match(/^[^.!?\n]*[.!?]/)?.[0]||i}function c({item:e,isSelected:t,onSelect:c,ref:l}){let u=(0,i.useMemo)(()=>`content`in e.skill&&e.skill.content?s(e.skill.content):`description`in e.skill&&e.skill.description?o(e.skill.description):null,[e.skill]);return(0,a.jsxs)(`button`,{role:`option`,"aria-selected":t,ref:l,type:`button`,className:n.cn(`w-full px-3 py-2.5 text-left transition-colors`,t?`bg-tertiary`:`hover:bg-[var(--oh-surface-raised)]`),onMouseDown:t=>{t.preventDefault(),c(e)},children:[(0,a.jsx)(r.Text,{className:`font-normal`,children:e.command}),u&&(0,a.jsx)(r.Text,{className:`text-xs text-[var(--oh-muted)] mt-0.5 truncate block`,children:u})]})}function l({items:e,selectedIndex:n,onSelect:r}){let{t:o}=t.useTranslation(`openhands`),s=(0,i.useRef)([]);return(0,i.useEffect)(()=>{s.current=s.current.slice(0,e.length)},[e.length]),(0,i.useEffect)(()=>{let e=s.current[n];e&&e.scrollIntoView({block:`nearest`})},[n]),e.length===0?null:(0,a.jsxs)(`div`,{role:`listbox`,"aria-label":o(`CHAT_INTERFACE$COMMANDS`),className:`absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50`,"data-testid":`slash-command-menu`,children:[(0,a.jsx)(`div`,{className:`px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]`,children:o(`CHAT_INTERFACE$COMMANDS`)}),e.map((e,t)=>(0,a.jsx)(c,{item:e,isSelected:t===n,onSelect:r,ref:e=>{s.current[t]=e}},e.command))]})}exports.SlashCommandMenu=l;
2
+ `).map(e=>e.trim()).find(e=>e.length>0&&!e.startsWith(`#`)&&e!==`---`);if(!r)return null;let i=o(r);return i.match(/^[^.!?\n]*[.!?]/)?.[0]||i}function c({item:e,isSelected:t,onSelect:c,ref:l}){let u=(0,i.useMemo)(()=>`description`in e.skill&&e.skill.description?o(e.skill.description):`content`in e.skill&&e.skill.content?s(e.skill.content):null,[e.skill]);return(0,a.jsxs)(`button`,{role:`option`,"aria-selected":t,ref:l,type:`button`,className:n.cn(`w-full px-3 py-2.5 text-left transition-colors`,t?`bg-tertiary`:`hover:bg-[var(--oh-surface-raised)]`),onMouseDown:t=>{t.preventDefault(),c(e)},children:[(0,a.jsx)(r.Text,{className:`font-normal`,children:e.command}),u&&(0,a.jsx)(r.Text,{className:`text-xs text-[var(--oh-muted)] mt-0.5 truncate block`,children:u})]})}function l({items:e,selectedIndex:n,onSelect:r}){let{t:o}=t.useTranslation(`openhands`),s=(0,i.useRef)([]);return(0,i.useEffect)(()=>{s.current=s.current.slice(0,e.length)},[e.length]),(0,i.useEffect)(()=>{let e=s.current[n];e&&e.scrollIntoView({block:`nearest`})},[n]),e.length===0?null:(0,a.jsxs)(`div`,{role:`listbox`,"aria-label":o(`CHAT_INTERFACE$COMMANDS`),className:`absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50`,"data-testid":`slash-command-menu`,children:[(0,a.jsx)(`div`,{className:`px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]`,children:o(`CHAT_INTERFACE$COMMANDS`)}),e.map((e,t)=>(0,a.jsx)(c,{item:e,isSelected:t===n,onSelect:r,ref:e=>{s.current[t]=e}},e.command))]})}exports.SlashCommandMenu=l;
3
3
  //# sourceMappingURL=slash-command-menu.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"slash-command-menu.cjs","names":[],"sources":["../../../../../src/components/features/chat/components/slash-command-menu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"#/utils/utils\";\nimport { Text } from \"#/ui/typography\";\nimport { SlashCommandItem } from \"#/hooks/chat/use-slash-command\";\n\n/**\n * Strip common inline Markdown syntax so descriptions render as plain text.\n * Handles: bold, italic, inline code, links, and images.\n */\nexport function stripMarkdown(text: string): string {\n return (\n text\n // Images: ![alt](url) → alt\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Bold/italic: ***text***, **text**, *text*, ___text___, __text__, _text_\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_{3}(.+?)_{3}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Inline code: `text` → text\n .replace(/`(.+?)`/g, \"$1\")\n // Strikethrough: ~~text~~ → text\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Extract a short description from skill content.\n * Tries YAML frontmatter \"description:\" first, then falls back\n * to the first meaningful line after headers and frontmatter.\n * Returns plain text with Markdown formatting stripped.\n */\nexport function getSkillDescription(content: string): string | null {\n let body = content;\n\n // Try to extract description from YAML frontmatter\n const frontmatterMatch = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/);\n if (frontmatterMatch) {\n const descMatch = frontmatterMatch[1].match(/^description:\\s*(.+)$/m);\n if (descMatch) {\n let desc = descMatch[1].trim();\n // Strip surrounding quotes from YAML values\n if (\n (desc.startsWith('\"') && desc.endsWith('\"')) ||\n (desc.startsWith(\"'\") && desc.endsWith(\"'\"))\n ) {\n desc = desc.slice(1, -1);\n }\n return stripMarkdown(desc);\n }\n // Skip frontmatter for body parsing\n body = content.slice(frontmatterMatch[0].length);\n }\n\n // Fall back to first meaningful line (skip headers, empty lines, frontmatter delimiters)\n const meaningful = body\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0 && !line.startsWith(\"#\") && line !== \"---\");\n\n if (!meaningful) return null;\n\n // Strip Markdown first so URLs inside links don't confuse sentence detection\n const stripped = stripMarkdown(meaningful);\n const sentence = stripped.match(/^[^.!?\\n]*[.!?]/);\n return sentence?.[0] || stripped;\n}\n\ninterface SlashCommandMenuItemProps {\n item: SlashCommandItem;\n isSelected: boolean;\n onSelect: (item: SlashCommandItem) => void;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nfunction SlashCommandMenuItem({\n item,\n isSelected,\n onSelect,\n ref,\n}: SlashCommandMenuItemProps) {\n const description = useMemo(() => {\n if (\"content\" in item.skill && item.skill.content) {\n return getSkillDescription(item.skill.content);\n }\n if (\"description\" in item.skill && item.skill.description) {\n return stripMarkdown(item.skill.description);\n }\n return null;\n }, [item.skill]);\n\n return (\n <button\n role=\"option\"\n aria-selected={isSelected}\n ref={ref}\n type=\"button\"\n className={cn(\n \"w-full px-3 py-2.5 text-left transition-colors\",\n isSelected ? \"bg-tertiary\" : \"hover:bg-[var(--oh-surface-raised)]\",\n )}\n onMouseDown={(e) => {\n // Use mouseDown instead of click to fire before input blur\n e.preventDefault();\n onSelect(item);\n }}\n >\n <Text className=\"font-normal\">{item.command}</Text>\n {description && (\n <Text className=\"text-xs text-[var(--oh-muted)] mt-0.5 truncate block\">\n {description}\n </Text>\n )}\n </button>\n );\n}\n\ninterface SlashCommandMenuProps {\n items: SlashCommandItem[];\n selectedIndex: number;\n onSelect: (item: SlashCommandItem) => void;\n}\n\nexport function SlashCommandMenu({\n items,\n selectedIndex,\n onSelect,\n}: SlashCommandMenuProps) {\n const { t } = useTranslation(\"openhands\");\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n // Keep refs array in sync with items length\n useEffect(() => {\n itemRefs.current = itemRefs.current.slice(0, items.length);\n }, [items.length]);\n\n // Scroll selected item into view\n useEffect(() => {\n const selectedItem = itemRefs.current[selectedIndex];\n if (selectedItem) {\n selectedItem.scrollIntoView({ block: \"nearest\" });\n }\n }, [selectedIndex]);\n\n if (items.length === 0) return null;\n\n return (\n <div\n role=\"listbox\"\n aria-label={t(\"CHAT_INTERFACE$COMMANDS\")}\n className=\"absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50\"\n data-testid=\"slash-command-menu\"\n >\n <div className=\"px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]\">\n {t(\"CHAT_INTERFACE$COMMANDS\")}\n </div>\n {items.map((item, index) => (\n <SlashCommandMenuItem\n key={item.command}\n item={item}\n isSelected={index === selectedIndex}\n onSelect={onSelect}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":"4SAUA,SAAgB,EAAc,EAAsB,CAClD,OACE,EAEG,QAAQ,0BAA2B,KAAK,CAExC,QAAQ,yBAA0B,KAAK,CAEvC,QAAQ,mBAAoB,KAAK,CACjC,QAAQ,mBAAoB,KAAK,CACjC,QAAQ,aAAc,KAAK,CAC3B,QAAQ,iBAAkB,KAAK,CAC/B,QAAQ,iBAAkB,KAAK,CAC/B,QAAQ,WAAY,KAAK,CAEzB,QAAQ,WAAY,KAAK,CAEzB,QAAQ,aAAc,KAAK,CAUlC,SAAgB,EAAoB,EAAgC,CAClE,IAAI,EAAO,EAGL,EAAmB,EAAQ,MAAM,2BAA2B,CAClE,GAAI,EAAkB,CACpB,IAAM,EAAY,EAAiB,GAAG,MAAM,yBAAyB,CACrE,GAAI,EAAW,CACb,IAAI,EAAO,EAAU,GAAG,MAAM,CAQ9B,OALG,EAAK,WAAW,IAAI,EAAI,EAAK,SAAS,IAAI,EAC1C,EAAK,WAAW,IAAI,EAAI,EAAK,SAAS,IAAI,IAE3C,EAAO,EAAK,MAAM,EAAG,GAAG,EAEnB,EAAc,EAAK,CAG5B,EAAO,EAAQ,MAAM,EAAiB,GAAG,OAAO,CAIlD,IAAM,EAAa,EAChB,MAAM;EAAK,CACX,IAAK,GAAS,EAAK,MAAM,CAAC,CAC1B,KAAM,GAAS,EAAK,OAAS,GAAK,CAAC,EAAK,WAAW,IAAI,EAAI,IAAS,MAAM,CAE7E,GAAI,CAAC,EAAY,OAAO,KAGxB,IAAM,EAAW,EAAc,EAAW,CAE1C,OADiB,EAAS,MAAM,kBACzB,GAAW,IAAM,EAU1B,SAAS,EAAqB,CAC5B,OACA,aACA,WACA,OAC4B,CAC5B,IAAM,GAAA,EAAA,EAAA,aACA,YAAa,EAAK,OAAS,EAAK,MAAM,QACjC,EAAoB,EAAK,MAAM,QAAQ,CAE5C,gBAAiB,EAAK,OAAS,EAAK,MAAM,YACrC,EAAc,EAAK,MAAM,YAAY,CAEvC,KACN,CAAC,EAAK,MAAM,CAAC,CAEhB,OACE,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,gBAAe,EACV,MACL,KAAK,SACL,UAAW,EAAA,GACT,iDACA,EAAa,cAAgB,sCAC9B,CACD,YAAc,GAAM,CAElB,EAAE,gBAAgB,CAClB,EAAS,EAAK,WAZlB,EAeE,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,UAAU,uBAAe,EAAK,QAAe,CAAA,CAClD,IACC,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,UAAU,gEACb,EACI,CAAA,CAEF,GAUb,SAAgB,EAAiB,CAC/B,QACA,gBACA,YACwB,CACxB,GAAM,CAAE,KAAM,EAAA,eAAe,YAAY,CACnC,GAAA,EAAA,EAAA,QAAgD,EAAE,CAAC,CAiBzD,OAdA,EAAA,EAAA,eAAgB,CACd,EAAS,QAAU,EAAS,QAAQ,MAAM,EAAG,EAAM,OAAO,EACzD,CAAC,EAAM,OAAO,CAAC,EAGlB,EAAA,EAAA,eAAgB,CACd,IAAM,EAAe,EAAS,QAAQ,GAClC,GACF,EAAa,eAAe,CAAE,MAAO,UAAW,CAAC,EAElD,CAAC,EAAc,CAAC,CAEf,EAAM,SAAW,EAAU,MAG7B,EAAA,EAAA,MAAC,MAAD,CACE,KAAK,UACL,aAAY,EAAE,0BAA0B,CACxC,UAAU,kLACV,cAAY,8BAJd,EAME,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,8FACZ,EAAE,0BAA0B,CACzB,CAAA,CACL,EAAM,KAAK,EAAM,KAChB,EAAA,EAAA,KAAC,EAAD,CAEQ,OACN,WAAY,IAAU,EACZ,WACV,IAAM,GAAO,CACX,EAAS,QAAQ,GAAS,GAE5B,CAPK,EAAK,QAOV,CACF,CACE"}
1
+ {"version":3,"file":"slash-command-menu.cjs","names":[],"sources":["../../../../../src/components/features/chat/components/slash-command-menu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"#/utils/utils\";\nimport { Text } from \"#/ui/typography\";\nimport { SlashCommandItem } from \"#/hooks/chat/use-slash-command\";\n\n/**\n * Strip common inline Markdown syntax so descriptions render as plain text.\n * Handles: bold, italic, inline code, links, and images.\n */\nexport function stripMarkdown(text: string): string {\n return (\n text\n // Images: ![alt](url) → alt\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Bold/italic: ***text***, **text**, *text*, ___text___, __text__, _text_\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_{3}(.+?)_{3}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Inline code: `text` → text\n .replace(/`(.+?)`/g, \"$1\")\n // Strikethrough: ~~text~~ → text\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Extract a short description from skill content.\n * Tries YAML frontmatter \"description:\" first, then falls back\n * to the first meaningful line after headers and frontmatter.\n * Returns plain text with Markdown formatting stripped.\n */\nexport function getSkillDescription(content: string): string | null {\n let body = content;\n\n // Try to extract description from YAML frontmatter\n const frontmatterMatch = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/);\n if (frontmatterMatch) {\n const descMatch = frontmatterMatch[1].match(/^description:\\s*(.+)$/m);\n if (descMatch) {\n let desc = descMatch[1].trim();\n // Strip surrounding quotes from YAML values\n if (\n (desc.startsWith('\"') && desc.endsWith('\"')) ||\n (desc.startsWith(\"'\") && desc.endsWith(\"'\"))\n ) {\n desc = desc.slice(1, -1);\n }\n return stripMarkdown(desc);\n }\n // Skip frontmatter for body parsing\n body = content.slice(frontmatterMatch[0].length);\n }\n\n // Fall back to first meaningful line (skip headers, empty lines, frontmatter delimiters)\n const meaningful = body\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0 && !line.startsWith(\"#\") && line !== \"---\");\n\n if (!meaningful) return null;\n\n // Strip Markdown first so URLs inside links don't confuse sentence detection\n const stripped = stripMarkdown(meaningful);\n const sentence = stripped.match(/^[^.!?\\n]*[.!?]/);\n return sentence?.[0] || stripped;\n}\n\ninterface SlashCommandMenuItemProps {\n item: SlashCommandItem;\n isSelected: boolean;\n onSelect: (item: SlashCommandItem) => void;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nfunction SlashCommandMenuItem({\n item,\n isSelected,\n onSelect,\n ref,\n}: SlashCommandMenuItemProps) {\n const description = useMemo(() => {\n if (\"description\" in item.skill && item.skill.description) {\n return stripMarkdown(item.skill.description);\n }\n if (\"content\" in item.skill && item.skill.content) {\n return getSkillDescription(item.skill.content);\n }\n return null;\n }, [item.skill]);\n\n return (\n <button\n role=\"option\"\n aria-selected={isSelected}\n ref={ref}\n type=\"button\"\n className={cn(\n \"w-full px-3 py-2.5 text-left transition-colors\",\n isSelected ? \"bg-tertiary\" : \"hover:bg-[var(--oh-surface-raised)]\",\n )}\n onMouseDown={(e) => {\n // Use mouseDown instead of click to fire before input blur\n e.preventDefault();\n onSelect(item);\n }}\n >\n <Text className=\"font-normal\">{item.command}</Text>\n {description && (\n <Text className=\"text-xs text-[var(--oh-muted)] mt-0.5 truncate block\">\n {description}\n </Text>\n )}\n </button>\n );\n}\n\ninterface SlashCommandMenuProps {\n items: SlashCommandItem[];\n selectedIndex: number;\n onSelect: (item: SlashCommandItem) => void;\n}\n\nexport function SlashCommandMenu({\n items,\n selectedIndex,\n onSelect,\n}: SlashCommandMenuProps) {\n const { t } = useTranslation(\"openhands\");\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n // Keep refs array in sync with items length\n useEffect(() => {\n itemRefs.current = itemRefs.current.slice(0, items.length);\n }, [items.length]);\n\n // Scroll selected item into view\n useEffect(() => {\n const selectedItem = itemRefs.current[selectedIndex];\n if (selectedItem) {\n selectedItem.scrollIntoView({ block: \"nearest\" });\n }\n }, [selectedIndex]);\n\n if (items.length === 0) return null;\n\n return (\n <div\n role=\"listbox\"\n aria-label={t(\"CHAT_INTERFACE$COMMANDS\")}\n className=\"absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50\"\n data-testid=\"slash-command-menu\"\n >\n <div className=\"px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]\">\n {t(\"CHAT_INTERFACE$COMMANDS\")}\n </div>\n {items.map((item, index) => (\n <SlashCommandMenuItem\n key={item.command}\n item={item}\n isSelected={index === selectedIndex}\n onSelect={onSelect}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":"4SAUA,SAAgB,EAAc,EAAsB,CAClD,OACE,EAEG,QAAQ,0BAA2B,KAAK,CAExC,QAAQ,yBAA0B,KAAK,CAEvC,QAAQ,mBAAoB,KAAK,CACjC,QAAQ,mBAAoB,KAAK,CACjC,QAAQ,aAAc,KAAK,CAC3B,QAAQ,iBAAkB,KAAK,CAC/B,QAAQ,iBAAkB,KAAK,CAC/B,QAAQ,WAAY,KAAK,CAEzB,QAAQ,WAAY,KAAK,CAEzB,QAAQ,aAAc,KAAK,CAUlC,SAAgB,EAAoB,EAAgC,CAClE,IAAI,EAAO,EAGL,EAAmB,EAAQ,MAAM,2BAA2B,CAClE,GAAI,EAAkB,CACpB,IAAM,EAAY,EAAiB,GAAG,MAAM,yBAAyB,CACrE,GAAI,EAAW,CACb,IAAI,EAAO,EAAU,GAAG,MAAM,CAQ9B,OALG,EAAK,WAAW,IAAI,EAAI,EAAK,SAAS,IAAI,EAC1C,EAAK,WAAW,IAAI,EAAI,EAAK,SAAS,IAAI,IAE3C,EAAO,EAAK,MAAM,EAAG,GAAG,EAEnB,EAAc,EAAK,CAG5B,EAAO,EAAQ,MAAM,EAAiB,GAAG,OAAO,CAIlD,IAAM,EAAa,EAChB,MAAM;EAAK,CACX,IAAK,GAAS,EAAK,MAAM,CAAC,CAC1B,KAAM,GAAS,EAAK,OAAS,GAAK,CAAC,EAAK,WAAW,IAAI,EAAI,IAAS,MAAM,CAE7E,GAAI,CAAC,EAAY,OAAO,KAGxB,IAAM,EAAW,EAAc,EAAW,CAE1C,OADiB,EAAS,MAAM,kBACzB,GAAW,IAAM,EAU1B,SAAS,EAAqB,CAC5B,OACA,aACA,WACA,OAC4B,CAC5B,IAAM,GAAA,EAAA,EAAA,aACA,gBAAiB,EAAK,OAAS,EAAK,MAAM,YACrC,EAAc,EAAK,MAAM,YAAY,CAE1C,YAAa,EAAK,OAAS,EAAK,MAAM,QACjC,EAAoB,EAAK,MAAM,QAAQ,CAEzC,KACN,CAAC,EAAK,MAAM,CAAC,CAEhB,OACE,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,gBAAe,EACV,MACL,KAAK,SACL,UAAW,EAAA,GACT,iDACA,EAAa,cAAgB,sCAC9B,CACD,YAAc,GAAM,CAElB,EAAE,gBAAgB,CAClB,EAAS,EAAK,WAZlB,EAeE,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,UAAU,uBAAe,EAAK,QAAe,CAAA,CAClD,IACC,EAAA,EAAA,KAAC,EAAA,KAAD,CAAM,UAAU,gEACb,EACI,CAAA,CAEF,GAUb,SAAgB,EAAiB,CAC/B,QACA,gBACA,YACwB,CACxB,GAAM,CAAE,KAAM,EAAA,eAAe,YAAY,CACnC,GAAA,EAAA,EAAA,QAAgD,EAAE,CAAC,CAiBzD,OAdA,EAAA,EAAA,eAAgB,CACd,EAAS,QAAU,EAAS,QAAQ,MAAM,EAAG,EAAM,OAAO,EACzD,CAAC,EAAM,OAAO,CAAC,EAGlB,EAAA,EAAA,eAAgB,CACd,IAAM,EAAe,EAAS,QAAQ,GAClC,GACF,EAAa,eAAe,CAAE,MAAO,UAAW,CAAC,EAElD,CAAC,EAAc,CAAC,CAEf,EAAM,SAAW,EAAU,MAG7B,EAAA,EAAA,MAAC,MAAD,CACE,KAAK,UACL,aAAY,EAAE,0BAA0B,CACxC,UAAU,kLACV,cAAY,8BAJd,EAME,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,8FACZ,EAAE,0BAA0B,CACzB,CAAA,CACL,EAAM,KAAK,EAAM,KAChB,EAAA,EAAA,KAAC,EAAD,CAEQ,OACN,WAAY,IAAU,EACZ,WACV,IAAM,GAAO,CACX,EAAS,QAAQ,GAAS,GAE5B,CAPK,EAAK,QAOV,CACF,CACE"}
@@ -23,7 +23,7 @@ function l(e) {
23
23
  return i.match(/^[^.!?\n]*[.!?]/)?.[0] || i;
24
24
  }
25
25
  function u({ item: e, isSelected: r, onSelect: a, ref: u }) {
26
- let d = i(() => "content" in e.skill && e.skill.content ? l(e.skill.content) : "description" in e.skill && e.skill.description ? c(e.skill.description) : null, [e.skill]);
26
+ let d = i(() => "description" in e.skill && e.skill.description ? c(e.skill.description) : "content" in e.skill && e.skill.content ? l(e.skill.content) : null, [e.skill]);
27
27
  return /* @__PURE__ */ s("button", {
28
28
  role: "option",
29
29
  "aria-selected": r,
@@ -1 +1 @@
1
- {"version":3,"file":"slash-command-menu.js","names":[],"sources":["../../../../../src/components/features/chat/components/slash-command-menu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"#/utils/utils\";\nimport { Text } from \"#/ui/typography\";\nimport { SlashCommandItem } from \"#/hooks/chat/use-slash-command\";\n\n/**\n * Strip common inline Markdown syntax so descriptions render as plain text.\n * Handles: bold, italic, inline code, links, and images.\n */\nexport function stripMarkdown(text: string): string {\n return (\n text\n // Images: ![alt](url) → alt\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Bold/italic: ***text***, **text**, *text*, ___text___, __text__, _text_\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_{3}(.+?)_{3}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Inline code: `text` → text\n .replace(/`(.+?)`/g, \"$1\")\n // Strikethrough: ~~text~~ → text\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Extract a short description from skill content.\n * Tries YAML frontmatter \"description:\" first, then falls back\n * to the first meaningful line after headers and frontmatter.\n * Returns plain text with Markdown formatting stripped.\n */\nexport function getSkillDescription(content: string): string | null {\n let body = content;\n\n // Try to extract description from YAML frontmatter\n const frontmatterMatch = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/);\n if (frontmatterMatch) {\n const descMatch = frontmatterMatch[1].match(/^description:\\s*(.+)$/m);\n if (descMatch) {\n let desc = descMatch[1].trim();\n // Strip surrounding quotes from YAML values\n if (\n (desc.startsWith('\"') && desc.endsWith('\"')) ||\n (desc.startsWith(\"'\") && desc.endsWith(\"'\"))\n ) {\n desc = desc.slice(1, -1);\n }\n return stripMarkdown(desc);\n }\n // Skip frontmatter for body parsing\n body = content.slice(frontmatterMatch[0].length);\n }\n\n // Fall back to first meaningful line (skip headers, empty lines, frontmatter delimiters)\n const meaningful = body\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0 && !line.startsWith(\"#\") && line !== \"---\");\n\n if (!meaningful) return null;\n\n // Strip Markdown first so URLs inside links don't confuse sentence detection\n const stripped = stripMarkdown(meaningful);\n const sentence = stripped.match(/^[^.!?\\n]*[.!?]/);\n return sentence?.[0] || stripped;\n}\n\ninterface SlashCommandMenuItemProps {\n item: SlashCommandItem;\n isSelected: boolean;\n onSelect: (item: SlashCommandItem) => void;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nfunction SlashCommandMenuItem({\n item,\n isSelected,\n onSelect,\n ref,\n}: SlashCommandMenuItemProps) {\n const description = useMemo(() => {\n if (\"content\" in item.skill && item.skill.content) {\n return getSkillDescription(item.skill.content);\n }\n if (\"description\" in item.skill && item.skill.description) {\n return stripMarkdown(item.skill.description);\n }\n return null;\n }, [item.skill]);\n\n return (\n <button\n role=\"option\"\n aria-selected={isSelected}\n ref={ref}\n type=\"button\"\n className={cn(\n \"w-full px-3 py-2.5 text-left transition-colors\",\n isSelected ? \"bg-tertiary\" : \"hover:bg-[var(--oh-surface-raised)]\",\n )}\n onMouseDown={(e) => {\n // Use mouseDown instead of click to fire before input blur\n e.preventDefault();\n onSelect(item);\n }}\n >\n <Text className=\"font-normal\">{item.command}</Text>\n {description && (\n <Text className=\"text-xs text-[var(--oh-muted)] mt-0.5 truncate block\">\n {description}\n </Text>\n )}\n </button>\n );\n}\n\ninterface SlashCommandMenuProps {\n items: SlashCommandItem[];\n selectedIndex: number;\n onSelect: (item: SlashCommandItem) => void;\n}\n\nexport function SlashCommandMenu({\n items,\n selectedIndex,\n onSelect,\n}: SlashCommandMenuProps) {\n const { t } = useTranslation(\"openhands\");\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n // Keep refs array in sync with items length\n useEffect(() => {\n itemRefs.current = itemRefs.current.slice(0, items.length);\n }, [items.length]);\n\n // Scroll selected item into view\n useEffect(() => {\n const selectedItem = itemRefs.current[selectedIndex];\n if (selectedItem) {\n selectedItem.scrollIntoView({ block: \"nearest\" });\n }\n }, [selectedIndex]);\n\n if (items.length === 0) return null;\n\n return (\n <div\n role=\"listbox\"\n aria-label={t(\"CHAT_INTERFACE$COMMANDS\")}\n className=\"absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50\"\n data-testid=\"slash-command-menu\"\n >\n <div className=\"px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]\">\n {t(\"CHAT_INTERFACE$COMMANDS\")}\n </div>\n {items.map((item, index) => (\n <SlashCommandMenuItem\n key={item.command}\n item={item}\n isSelected={index === selectedIndex}\n onSelect={onSelect}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,EAAc,GAAsB;AAClD,QACE,EAEG,QAAQ,2BAA2B,KAAK,CAExC,QAAQ,0BAA0B,KAAK,CAEvC,QAAQ,oBAAoB,KAAK,CACjC,QAAQ,oBAAoB,KAAK,CACjC,QAAQ,cAAc,KAAK,CAC3B,QAAQ,kBAAkB,KAAK,CAC/B,QAAQ,kBAAkB,KAAK,CAC/B,QAAQ,YAAY,KAAK,CAEzB,QAAQ,YAAY,KAAK,CAEzB,QAAQ,cAAc,KAAK;;AAUlC,SAAgB,EAAoB,GAAgC;CAClE,IAAI,IAAO,GAGL,IAAmB,EAAQ,MAAM,2BAA2B;AAClE,KAAI,GAAkB;EACpB,IAAM,IAAY,EAAiB,GAAG,MAAM,yBAAyB;AACrE,MAAI,GAAW;GACb,IAAI,IAAO,EAAU,GAAG,MAAM;AAQ9B,WALG,EAAK,WAAW,KAAI,IAAI,EAAK,SAAS,KAAI,IAC1C,EAAK,WAAW,IAAI,IAAI,EAAK,SAAS,IAAI,MAE3C,IAAO,EAAK,MAAM,GAAG,GAAG,GAEnB,EAAc,EAAK;;AAG5B,MAAO,EAAQ,MAAM,EAAiB,GAAG,OAAO;;CAIlD,IAAM,IAAa,EAChB,MAAM,KAAK,CACX,KAAK,MAAS,EAAK,MAAM,CAAC,CAC1B,MAAM,MAAS,EAAK,SAAS,KAAK,CAAC,EAAK,WAAW,IAAI,IAAI,MAAS,MAAM;AAE7E,KAAI,CAAC,EAAY,QAAO;CAGxB,IAAM,IAAW,EAAc,EAAW;AAE1C,QADiB,EAAS,MAAM,kBACzB,GAAW,MAAM;;AAU1B,SAAS,EAAqB,EAC5B,SACA,eACA,aACA,UAC4B;CAC5B,IAAM,IAAc,QACd,aAAa,EAAK,SAAS,EAAK,MAAM,UACjC,EAAoB,EAAK,MAAM,QAAQ,GAE5C,iBAAiB,EAAK,SAAS,EAAK,MAAM,cACrC,EAAc,EAAK,MAAM,YAAY,GAEvC,MACN,CAAC,EAAK,MAAM,CAAC;AAEhB,QACE,kBAAC,UAAD;EACE,MAAK;EACL,iBAAe;EACV;EACL,MAAK;EACL,WAAW,EACT,kDACA,IAAa,gBAAgB,sCAC9B;EACD,cAAc,MAAM;AAGlB,GADA,EAAE,gBAAgB,EAClB,EAAS,EAAK;;YAZlB,CAeE,kBAAC,GAAD;GAAM,WAAU;aAAe,EAAK;GAAe,CAAA,EAClD,KACC,kBAAC,GAAD;GAAM,WAAU;aACb;GACI,CAAA,CAEF;;;AAUb,SAAgB,EAAiB,EAC/B,UACA,kBACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAAe,YAAY,EACnC,IAAW,EAAqC,EAAE,CAAC;AAiBzD,QAdA,QAAgB;AACd,IAAS,UAAU,EAAS,QAAQ,MAAM,GAAG,EAAM,OAAO;IACzD,CAAC,EAAM,OAAO,CAAC,EAGlB,QAAgB;EACd,IAAM,IAAe,EAAS,QAAQ;AACtC,EAAI,KACF,EAAa,eAAe,EAAE,OAAO,WAAW,CAAC;IAElD,CAAC,EAAc,CAAC,EAEf,EAAM,WAAW,IAAU,OAG7B,kBAAC,OAAD;EACE,MAAK;EACL,cAAY,EAAE,0BAA0B;EACxC,WAAU;EACV,eAAY;YAJd,CAME,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAE,0BAA0B;GACzB,CAAA,EACL,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD;GAEQ;GACN,YAAY,MAAU;GACZ;GACV,MAAM,MAAO;AACX,MAAS,QAAQ,KAAS;;GAE5B,EAPK,EAAK,QAOV,CACF,CACE"}
1
+ {"version":3,"file":"slash-command-menu.js","names":[],"sources":["../../../../../src/components/features/chat/components/slash-command-menu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"#/utils/utils\";\nimport { Text } from \"#/ui/typography\";\nimport { SlashCommandItem } from \"#/hooks/chat/use-slash-command\";\n\n/**\n * Strip common inline Markdown syntax so descriptions render as plain text.\n * Handles: bold, italic, inline code, links, and images.\n */\nexport function stripMarkdown(text: string): string {\n return (\n text\n // Images: ![alt](url) → alt\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Links: [text](url) → text\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n // Bold/italic: ***text***, **text**, *text*, ___text___, __text__, _text_\n .replace(/\\*{3}(.+?)\\*{3}/g, \"$1\")\n .replace(/\\*{2}(.+?)\\*{2}/g, \"$1\")\n .replace(/\\*(.+?)\\*/g, \"$1\")\n .replace(/_{3}(.+?)_{3}/g, \"$1\")\n .replace(/_{2}(.+?)_{2}/g, \"$1\")\n .replace(/_(.+?)_/g, \"$1\")\n // Inline code: `text` → text\n .replace(/`(.+?)`/g, \"$1\")\n // Strikethrough: ~~text~~ → text\n .replace(/~~(.+?)~~/g, \"$1\")\n );\n}\n\n/**\n * Extract a short description from skill content.\n * Tries YAML frontmatter \"description:\" first, then falls back\n * to the first meaningful line after headers and frontmatter.\n * Returns plain text with Markdown formatting stripped.\n */\nexport function getSkillDescription(content: string): string | null {\n let body = content;\n\n // Try to extract description from YAML frontmatter\n const frontmatterMatch = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---/);\n if (frontmatterMatch) {\n const descMatch = frontmatterMatch[1].match(/^description:\\s*(.+)$/m);\n if (descMatch) {\n let desc = descMatch[1].trim();\n // Strip surrounding quotes from YAML values\n if (\n (desc.startsWith('\"') && desc.endsWith('\"')) ||\n (desc.startsWith(\"'\") && desc.endsWith(\"'\"))\n ) {\n desc = desc.slice(1, -1);\n }\n return stripMarkdown(desc);\n }\n // Skip frontmatter for body parsing\n body = content.slice(frontmatterMatch[0].length);\n }\n\n // Fall back to first meaningful line (skip headers, empty lines, frontmatter delimiters)\n const meaningful = body\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0 && !line.startsWith(\"#\") && line !== \"---\");\n\n if (!meaningful) return null;\n\n // Strip Markdown first so URLs inside links don't confuse sentence detection\n const stripped = stripMarkdown(meaningful);\n const sentence = stripped.match(/^[^.!?\\n]*[.!?]/);\n return sentence?.[0] || stripped;\n}\n\ninterface SlashCommandMenuItemProps {\n item: SlashCommandItem;\n isSelected: boolean;\n onSelect: (item: SlashCommandItem) => void;\n ref?: React.Ref<HTMLButtonElement>;\n}\n\nfunction SlashCommandMenuItem({\n item,\n isSelected,\n onSelect,\n ref,\n}: SlashCommandMenuItemProps) {\n const description = useMemo(() => {\n if (\"description\" in item.skill && item.skill.description) {\n return stripMarkdown(item.skill.description);\n }\n if (\"content\" in item.skill && item.skill.content) {\n return getSkillDescription(item.skill.content);\n }\n return null;\n }, [item.skill]);\n\n return (\n <button\n role=\"option\"\n aria-selected={isSelected}\n ref={ref}\n type=\"button\"\n className={cn(\n \"w-full px-3 py-2.5 text-left transition-colors\",\n isSelected ? \"bg-tertiary\" : \"hover:bg-[var(--oh-surface-raised)]\",\n )}\n onMouseDown={(e) => {\n // Use mouseDown instead of click to fire before input blur\n e.preventDefault();\n onSelect(item);\n }}\n >\n <Text className=\"font-normal\">{item.command}</Text>\n {description && (\n <Text className=\"text-xs text-[var(--oh-muted)] mt-0.5 truncate block\">\n {description}\n </Text>\n )}\n </button>\n );\n}\n\ninterface SlashCommandMenuProps {\n items: SlashCommandItem[];\n selectedIndex: number;\n onSelect: (item: SlashCommandItem) => void;\n}\n\nexport function SlashCommandMenu({\n items,\n selectedIndex,\n onSelect,\n}: SlashCommandMenuProps) {\n const { t } = useTranslation(\"openhands\");\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n // Keep refs array in sync with items length\n useEffect(() => {\n itemRefs.current = itemRefs.current.slice(0, items.length);\n }, [items.length]);\n\n // Scroll selected item into view\n useEffect(() => {\n const selectedItem = itemRefs.current[selectedIndex];\n if (selectedItem) {\n selectedItem.scrollIntoView({ block: \"nearest\" });\n }\n }, [selectedIndex]);\n\n if (items.length === 0) return null;\n\n return (\n <div\n role=\"listbox\"\n aria-label={t(\"CHAT_INTERFACE$COMMANDS\")}\n className=\"absolute bottom-full left-0 w-full mb-1 bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)] rounded-lg shadow-lg max-h-[300px] overflow-y-auto custom-scrollbar z-50\"\n data-testid=\"slash-command-menu\"\n >\n <div className=\"px-3 py-2 text-xs text-[var(--oh-muted)] border-b border-[var(--oh-border-subtle)]\">\n {t(\"CHAT_INTERFACE$COMMANDS\")}\n </div>\n {items.map((item, index) => (\n <SlashCommandMenuItem\n key={item.command}\n item={item}\n isSelected={index === selectedIndex}\n onSelect={onSelect}\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,EAAc,GAAsB;AAClD,QACE,EAEG,QAAQ,2BAA2B,KAAK,CAExC,QAAQ,0BAA0B,KAAK,CAEvC,QAAQ,oBAAoB,KAAK,CACjC,QAAQ,oBAAoB,KAAK,CACjC,QAAQ,cAAc,KAAK,CAC3B,QAAQ,kBAAkB,KAAK,CAC/B,QAAQ,kBAAkB,KAAK,CAC/B,QAAQ,YAAY,KAAK,CAEzB,QAAQ,YAAY,KAAK,CAEzB,QAAQ,cAAc,KAAK;;AAUlC,SAAgB,EAAoB,GAAgC;CAClE,IAAI,IAAO,GAGL,IAAmB,EAAQ,MAAM,2BAA2B;AAClE,KAAI,GAAkB;EACpB,IAAM,IAAY,EAAiB,GAAG,MAAM,yBAAyB;AACrE,MAAI,GAAW;GACb,IAAI,IAAO,EAAU,GAAG,MAAM;AAQ9B,WALG,EAAK,WAAW,KAAI,IAAI,EAAK,SAAS,KAAI,IAC1C,EAAK,WAAW,IAAI,IAAI,EAAK,SAAS,IAAI,MAE3C,IAAO,EAAK,MAAM,GAAG,GAAG,GAEnB,EAAc,EAAK;;AAG5B,MAAO,EAAQ,MAAM,EAAiB,GAAG,OAAO;;CAIlD,IAAM,IAAa,EAChB,MAAM,KAAK,CACX,KAAK,MAAS,EAAK,MAAM,CAAC,CAC1B,MAAM,MAAS,EAAK,SAAS,KAAK,CAAC,EAAK,WAAW,IAAI,IAAI,MAAS,MAAM;AAE7E,KAAI,CAAC,EAAY,QAAO;CAGxB,IAAM,IAAW,EAAc,EAAW;AAE1C,QADiB,EAAS,MAAM,kBACzB,GAAW,MAAM;;AAU1B,SAAS,EAAqB,EAC5B,SACA,eACA,aACA,UAC4B;CAC5B,IAAM,IAAc,QACd,iBAAiB,EAAK,SAAS,EAAK,MAAM,cACrC,EAAc,EAAK,MAAM,YAAY,GAE1C,aAAa,EAAK,SAAS,EAAK,MAAM,UACjC,EAAoB,EAAK,MAAM,QAAQ,GAEzC,MACN,CAAC,EAAK,MAAM,CAAC;AAEhB,QACE,kBAAC,UAAD;EACE,MAAK;EACL,iBAAe;EACV;EACL,MAAK;EACL,WAAW,EACT,kDACA,IAAa,gBAAgB,sCAC9B;EACD,cAAc,MAAM;AAGlB,GADA,EAAE,gBAAgB,EAClB,EAAS,EAAK;;YAZlB,CAeE,kBAAC,GAAD;GAAM,WAAU;aAAe,EAAK;GAAe,CAAA,EAClD,KACC,kBAAC,GAAD;GAAM,WAAU;aACb;GACI,CAAA,CAEF;;;AAUb,SAAgB,EAAiB,EAC/B,UACA,kBACA,eACwB;CACxB,IAAM,EAAE,SAAM,EAAe,YAAY,EACnC,IAAW,EAAqC,EAAE,CAAC;AAiBzD,QAdA,QAAgB;AACd,IAAS,UAAU,EAAS,QAAQ,MAAM,GAAG,EAAM,OAAO;IACzD,CAAC,EAAM,OAAO,CAAC,EAGlB,QAAgB;EACd,IAAM,IAAe,EAAS,QAAQ;AACtC,EAAI,KACF,EAAa,eAAe,EAAE,OAAO,WAAW,CAAC;IAElD,CAAC,EAAc,CAAC,EAEf,EAAM,WAAW,IAAU,OAG7B,kBAAC,OAAD;EACE,MAAK;EACL,cAAY,EAAE,0BAA0B;EACxC,WAAU;EACV,eAAY;YAJd,CAME,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAE,0BAA0B;GACzB,CAAA,EACL,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD;GAEQ;GACN,YAAY,MAAU;GACZ;GACV,MAAM,MAAO;AACX,MAAS,QAAQ,KAAS;;GAE5B,EAPK,EAAK,QAOV,CACF,CACE"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../../node_modules/lucide-react/dist/esm/icons/chevron-left.cjs`),a=require(`../../../node_modules/lucide-react/dist/esm/icons/chevron-right.cjs`),o=require(`../../../node_modules/lucide-react/dist/esm/icons/plus.cjs`),s=require(`../../../node_modules/lucide-react/dist/esm/icons/server.cjs`),c=require(`../../../node_modules/lucide-react/dist/esm/icons/settings.cjs`),l=require(`../../shared/buttons/styled-tooltip.cjs`),u=require(`./sidebar-layout.cjs`),d=require(`./sidebar-collapsed-icon-slot.cjs`),f=require(`./sidebar-nav-link.cjs`),p=require(`../../shared/buttons/openhands-logo-button.cjs`),m=require(`../backends/backend-status-dot.cjs`),h=require(`../backends/backend-selector.cjs`),g=require(`./sidebar-conversation-list.cjs`),_=require(`../../../icons/automations.cjs`);let v=require(`react`);v=e.__toESM(v,1);let y=require(`react/jsx-runtime`);var b=18,x=34,S=Math.round(x*30/46);function C({collapsed:e,showCollapseToggle:v,showMobileCloseButton:C=!1,onCloseMobile:w,linkDisabled:T,collapseToggleLabel:E,onCollapse:D,onExpand:O,showCollapsedExpandButton:k,isExtensionsActive:A,currentPath:j,navigate:M,activeBackendHealth:N,collapsedBackendPopoverOpen:P,setCollapsedBackendPopoverOpen:F,collapsedBackendPopoverRef:I,collapsedBackendCloseTimer:L,onOpenAddBackend:R,onOpenManageBackends:z}){let{t:B}=t.useTranslation(`openhands`),V=L;return(0,y.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:[(0,y.jsxs)(`div`,{className:u.sidebarHeaderRowClassName(e),children:[(0,y.jsxs)(`div`,{className:r.cn(e&&v?u.SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS:`flex min-w-0 shrink-0 items-center`),children:[(0,y.jsx)(`div`,{className:r.cn(e&&v&&`flex h-full w-full items-center justify-start pl-2.5 transition-opacity duration-150`,e&&k&&`opacity-0`),children:(0,y.jsx)(p.OpenHandsLogoButton,{logoWidth:x,logoHeight:S,logoClassName:`max-w-none`,className:r.cn(u.SIDEBAR_ICON_SLOT_CLASS,`overflow-visible`)})}),e&&v?(0,y.jsx)(`button`,{type:`button`,"data-testid":`sidebar-collapse-toggle`,"aria-pressed":e,"aria-label":E,onClick:O,className:r.cn(u.SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,k?`opacity-100 pointer-events-auto`:`opacity-0 pointer-events-none`),children:(0,y.jsx)(a.ChevronRight,{width:14,height:14})}):null]}),!e&&v?(0,y.jsx)(`button`,{type:`button`,"data-testid":`sidebar-collapse-toggle`,"aria-pressed":e,"aria-label":E,onClick:D,className:r.cn(`hidden md:inline-flex ml-auto`,u.SIDEBAR_ICON_BUTTON_CLASS,`text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]`),children:(0,y.jsx)(i.ChevronLeft,{width:14,height:14})}):null,!e&&C?(0,y.jsx)(`button`,{type:`button`,"data-testid":`sidebar-mobile-drawer-close`,onClick:w,"aria-label":B(n.I18nKey.SIDEBAR$CLOSE_MENU),className:r.cn(`inline-flex ml-auto`,u.SIDEBAR_ICON_BUTTON_CLASS,`text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]`),children:(0,y.jsx)(i.ChevronLeft,{width:14,height:14})}):null]}),(0,y.jsxs)(`nav`,{className:u.sidebarNavListClassName(e),children:[(0,y.jsx)(f.SidebarNavLink,{to:`/conversations`,end:!0,label:`New Chat`,testId:`sidebar-conversations-link`,disabled:T,collapsed:e,icon:(0,y.jsx)(o.Plus,{width:b,height:b})}),(0,y.jsx)(f.SidebarNavLink,{to:`/customize`,label:`Customize`,testId:`sidebar-skills-link`,disabled:T,collapsed:e,forceActive:A,icon:(0,y.jsxs)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:b,height:b,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[(0,y.jsx)(`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`}),(0,y.jsx)(`path`,{d:`m7 16.5-4.74-2.85`}),(0,y.jsx)(`path`,{d:`m7 16.5 5-3`}),(0,y.jsx)(`path`,{d:`M7 16.5v5.17`}),(0,y.jsx)(`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`}),(0,y.jsx)(`path`,{d:`m17 16.5-5-3`}),(0,y.jsx)(`path`,{d:`m17 16.5 4.74-2.85`}),(0,y.jsx)(`path`,{d:`m17 16.5v5.17`}),(0,y.jsx)(`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`}),(0,y.jsx)(`path`,{d:`M12 8 7.26 5.15`}),(0,y.jsx)(`path`,{d:`m12 8 4.74-2.85`}),(0,y.jsx)(`path`,{d:`M12 13.5V8`})]})}),(0,y.jsx)(f.SidebarNavLink,{to:`/automations`,label:B(n.I18nKey.SIDEBAR$AUTOMATIONS),testId:`sidebar-automations-link`,disabled:T,collapsed:e,icon:(0,y.jsx)(_.default,{width:b,height:b})})]}),(0,y.jsx)(g.SidebarConversationList,{collapsed:e}),e&&v?(0,y.jsxs)(`nav`,{className:r.cn(u.sidebarNavListClassName(e),`mt-auto pb-2 cursor-pointer`),children:[(0,y.jsx)(l.StyledTooltip,{content:B(n.I18nKey.SIDEBAR$SETTINGS),placement:`right`,children:(0,y.jsxs)(`button`,{type:`button`,"data-testid":`collapsed-settings-link`,"aria-label":B(n.I18nKey.SIDEBAR$SETTINGS),onClick:()=>M(`/settings`),className:u.sidebarNavRowClassName({collapsed:!0}),children:[(0,y.jsx)(d.SidebarCollapsedIconSlot,{active:j.startsWith(`/settings`),children:(0,y.jsx)(c.Settings,{width:b,height:b})}),(0,y.jsx)(`span`,{className:u.sidebarNavLabelClassName(!0),children:B(n.I18nKey.SIDEBAR$SETTINGS)})]})}),(0,y.jsxs)(`div`,{className:`relative`,ref:I,onMouseEnter:()=>{V.current&&=(clearTimeout(V.current),null),F(!0)},onMouseLeave:()=>{V.current=setTimeout(()=>F(!1),150)},children:[(0,y.jsxs)(`button`,{type:`button`,"data-testid":`collapsed-backend-selector-link`,"aria-label":B(n.I18nKey.BACKEND$MANAGE),"aria-expanded":P,onMouseDown:e=>{e.preventDefault(),e.stopPropagation()},onMouseUp:e=>e.stopPropagation(),className:r.cn(u.sidebarNavRowClassName({collapsed:!0}),`relative`),children:[(0,y.jsx)(d.SidebarCollapsedIconSlot,{active:P,children:(0,y.jsxs)(`span`,{className:`relative inline-flex size-[18px] shrink-0 items-center justify-center`,children:[(0,y.jsx)(m.BackendStatusDot,{isConnected:N?.isConnected??null,className:`absolute -left-0.5 -top-0.5 z-[1] pointer-events-none`}),(0,y.jsx)(s.Server,{width:b,height:b})]})}),(0,y.jsx)(`span`,{className:u.sidebarNavLabelClassName(!0),children:B(n.I18nKey.BACKEND$MANAGE)})]}),P?(0,y.jsx)(`div`,{className:`absolute bottom-[-4px] left-full pl-2.5 z-40 w-[272px]`,onClick:e=>e.stopPropagation(),children:(0,y.jsx)(h.BackendSelector,{sidebarCollapsed:e,hideTrigger:!0,defaultOpen:!0,openUpward:!0,onSelectOption:()=>F(!1),onOpenAddBackend:R,onOpenManageBackends:z})}):null]})]}):null,e?null:(0,y.jsx)(`div`,{className:r.cn(`flex flex-col items-stretch max-w-none box-border shrink-0`,`-ml-2.5 w-[calc(100%+0.625rem)] border-t border-[var(--oh-border)] pt-2 px-2.5`),children:(0,y.jsx)(h.BackendSelector,{sidebarCollapsed:e,openUpward:!0})})]})}exports.SidebarRailBody=C;
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../utils/utils.cjs`),i=require(`../../../node_modules/lucide-react/dist/esm/icons/chevron-left.cjs`),a=require(`../../../node_modules/lucide-react/dist/esm/icons/chevron-right.cjs`),o=require(`../../../node_modules/lucide-react/dist/esm/icons/plus.cjs`),s=require(`../../../node_modules/lucide-react/dist/esm/icons/server.cjs`),c=require(`../../../node_modules/lucide-react/dist/esm/icons/settings.cjs`),l=require(`../../shared/buttons/styled-tooltip.cjs`),u=require(`../../shared/navigation-link.cjs`),d=require(`./sidebar-layout.cjs`),f=require(`./sidebar-collapsed-icon-slot.cjs`),p=require(`./sidebar-nav-link.cjs`),m=require(`../../shared/buttons/openhands-logo-button.cjs`),h=require(`../backends/backend-status-dot.cjs`),g=require(`../backends/backend-selector.cjs`),_=require(`./sidebar-conversation-list.cjs`),v=require(`../../../icons/automations.cjs`);let y=require(`react`);y=e.__toESM(y,1);let b=require(`react/jsx-runtime`);var x=18,S=34,C=Math.round(S*30/46);function w({collapsed:e,showCollapseToggle:y,showMobileCloseButton:w=!1,onCloseMobile:T,linkDisabled:E,collapseToggleLabel:D,onCollapse:O,onExpand:k,showCollapsedExpandButton:A,isExtensionsActive:j,currentPath:M,activeBackendHealth:N,collapsedBackendPopoverOpen:P,setCollapsedBackendPopoverOpen:F,collapsedBackendPopoverRef:I,collapsedBackendCloseTimer:L,onOpenAddBackend:R,onOpenManageBackends:z}){let{t:B}=t.useTranslation(`openhands`),V=L;return(0,b.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:[(0,b.jsxs)(`div`,{className:d.sidebarHeaderRowClassName(e),children:[(0,b.jsxs)(`div`,{className:r.cn(e&&y?d.SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS:`flex min-w-0 shrink-0 items-center`),children:[(0,b.jsx)(`div`,{className:r.cn(e&&y&&`flex h-full w-full items-center justify-start pl-2.5 transition-opacity duration-150`,e&&A&&`opacity-0`),children:(0,b.jsx)(m.OpenHandsLogoButton,{logoWidth:S,logoHeight:C,logoClassName:`max-w-none`,className:r.cn(d.SIDEBAR_ICON_SLOT_CLASS,`overflow-visible`)})}),e&&y?(0,b.jsx)(`button`,{type:`button`,"data-testid":`sidebar-collapse-toggle`,"aria-pressed":e,"aria-label":D,onClick:k,className:r.cn(d.SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,A?`opacity-100 pointer-events-auto`:`opacity-0 pointer-events-none`),children:(0,b.jsx)(a.ChevronRight,{width:14,height:14})}):null]}),!e&&y?(0,b.jsx)(`button`,{type:`button`,"data-testid":`sidebar-collapse-toggle`,"aria-pressed":e,"aria-label":D,onClick:O,className:r.cn(`hidden md:inline-flex ml-auto`,d.SIDEBAR_ICON_BUTTON_CLASS,`text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]`),children:(0,b.jsx)(i.ChevronLeft,{width:14,height:14})}):null,!e&&w?(0,b.jsx)(`button`,{type:`button`,"data-testid":`sidebar-mobile-drawer-close`,onClick:T,"aria-label":B(n.I18nKey.SIDEBAR$CLOSE_MENU),className:r.cn(`inline-flex ml-auto`,d.SIDEBAR_ICON_BUTTON_CLASS,`text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]`),children:(0,b.jsx)(i.ChevronLeft,{width:14,height:14})}):null]}),(0,b.jsxs)(`nav`,{className:d.sidebarNavListClassName(e),children:[(0,b.jsx)(p.SidebarNavLink,{to:`/conversations`,end:!0,label:`New Chat`,testId:`sidebar-conversations-link`,disabled:E,collapsed:e,icon:(0,b.jsx)(o.Plus,{width:x,height:x})}),(0,b.jsx)(p.SidebarNavLink,{to:`/customize`,label:`Customize`,testId:`sidebar-skills-link`,disabled:E,collapsed:e,forceActive:j,icon:(0,b.jsxs)(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:x,height:x,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[(0,b.jsx)(`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`}),(0,b.jsx)(`path`,{d:`m7 16.5-4.74-2.85`}),(0,b.jsx)(`path`,{d:`m7 16.5 5-3`}),(0,b.jsx)(`path`,{d:`M7 16.5v5.17`}),(0,b.jsx)(`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`}),(0,b.jsx)(`path`,{d:`m17 16.5-5-3`}),(0,b.jsx)(`path`,{d:`m17 16.5 4.74-2.85`}),(0,b.jsx)(`path`,{d:`m17 16.5v5.17`}),(0,b.jsx)(`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`}),(0,b.jsx)(`path`,{d:`M12 8 7.26 5.15`}),(0,b.jsx)(`path`,{d:`m12 8 4.74-2.85`}),(0,b.jsx)(`path`,{d:`M12 13.5V8`})]})}),(0,b.jsx)(p.SidebarNavLink,{to:`/automations`,label:B(n.I18nKey.SIDEBAR$AUTOMATIONS),testId:`sidebar-automations-link`,disabled:E,collapsed:e,icon:(0,b.jsx)(v.default,{width:x,height:x})})]}),(0,b.jsx)(_.SidebarConversationList,{collapsed:e}),e&&y?(0,b.jsxs)(`nav`,{className:r.cn(d.sidebarNavListClassName(e),`mt-auto pb-2 cursor-pointer`),children:[(0,b.jsx)(l.StyledTooltip,{content:B(n.I18nKey.SIDEBAR$SETTINGS),placement:`right`,children:(0,b.jsxs)(u.NavigationLink,{to:`/settings`,"data-testid":`collapsed-settings-link`,"aria-label":B(n.I18nKey.SIDEBAR$SETTINGS),className:d.sidebarNavRowClassName({collapsed:!0}),children:[(0,b.jsx)(f.SidebarCollapsedIconSlot,{active:M.startsWith(`/settings`),children:(0,b.jsx)(c.Settings,{width:x,height:x})}),(0,b.jsx)(`span`,{className:d.sidebarNavLabelClassName(!0),children:B(n.I18nKey.SIDEBAR$SETTINGS)})]})}),(0,b.jsxs)(`div`,{className:`relative`,ref:I,onMouseEnter:()=>{V.current&&=(clearTimeout(V.current),null),F(!0)},onMouseLeave:()=>{V.current=setTimeout(()=>F(!1),150)},children:[(0,b.jsxs)(`button`,{type:`button`,"data-testid":`collapsed-backend-selector-link`,"aria-label":B(n.I18nKey.BACKEND$MANAGE),"aria-expanded":P,onMouseDown:e=>{e.preventDefault(),e.stopPropagation()},onMouseUp:e=>e.stopPropagation(),className:r.cn(d.sidebarNavRowClassName({collapsed:!0}),`relative`),children:[(0,b.jsx)(f.SidebarCollapsedIconSlot,{active:P,children:(0,b.jsxs)(`span`,{className:`relative inline-flex size-[18px] shrink-0 items-center justify-center`,children:[(0,b.jsx)(h.BackendStatusDot,{isConnected:N?.isConnected??null,className:`absolute -left-0.5 -top-0.5 z-[1] pointer-events-none`}),(0,b.jsx)(s.Server,{width:x,height:x})]})}),(0,b.jsx)(`span`,{className:d.sidebarNavLabelClassName(!0),children:B(n.I18nKey.BACKEND$MANAGE)})]}),P?(0,b.jsx)(`div`,{className:`absolute bottom-[-4px] left-full pl-2.5 z-40 w-[272px]`,onClick:e=>e.stopPropagation(),children:(0,b.jsx)(g.BackendSelector,{sidebarCollapsed:e,hideTrigger:!0,defaultOpen:!0,openUpward:!0,onSelectOption:()=>F(!1),onOpenAddBackend:R,onOpenManageBackends:z})}):null]})]}):null,e?null:(0,b.jsx)(`div`,{className:r.cn(`flex flex-col items-stretch max-w-none box-border shrink-0`,`-ml-2.5 w-[calc(100%+0.625rem)] border-t border-[var(--oh-border)] pt-2 px-2.5`),children:(0,b.jsx)(g.BackendSelector,{sidebarCollapsed:e,openUpward:!0})})]})}exports.SidebarRailBody=w;
2
2
  //# sourceMappingURL=sidebar-rail-body.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar-rail-body.cjs","names":[],"sources":["../../../../src/components/features/sidebar/sidebar-rail-body.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n ChevronLeft,\n ChevronRight,\n Plus,\n Server,\n Settings,\n} from \"lucide-react\";\nimport { OpenHandsLogoButton } from \"#/components/shared/buttons/openhands-logo-button\";\nimport { SidebarCollapsedIconSlot } from \"./sidebar-collapsed-icon-slot\";\nimport { SidebarNavLink } from \"./sidebar-nav-link\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\nimport { BackendSelector } from \"#/components/features/backends/backend-selector\";\nimport { BackendStatusDot } from \"#/components/features/backends/backend-status-dot\";\nimport { SidebarConversationList } from \"./sidebar-conversation-list\";\nimport AutomationsIcon from \"#/icons/automations.svg?react\";\nimport {\n SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,\n SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS,\n SIDEBAR_ICON_BUTTON_CLASS,\n SIDEBAR_ICON_SLOT_CLASS,\n sidebarHeaderRowClassName,\n sidebarNavLabelClassName,\n sidebarNavListClassName,\n sidebarNavRowClassName,\n} from \"./sidebar-layout\";\n\nconst ICON_SIZE = 18;\nconst SIDEBAR_LOGO_WIDTH = 34;\nconst SIDEBAR_LOGO_HEIGHT = Math.round((SIDEBAR_LOGO_WIDTH * 30) / 46);\n\nexport interface SidebarRailBodyProps {\n collapsed: boolean;\n showCollapseToggle: boolean;\n showMobileCloseButton?: boolean;\n onCloseMobile?: () => void;\n linkDisabled: boolean;\n collapseToggleLabel: string;\n onCollapse: () => void;\n onExpand: () => void;\n showCollapsedExpandButton: boolean;\n isExtensionsActive: boolean;\n currentPath: string;\n navigate: (path: string) => void;\n activeBackendHealth: { isConnected: boolean | null } | undefined;\n collapsedBackendPopoverOpen: boolean;\n setCollapsedBackendPopoverOpen: (open: boolean) => void;\n collapsedBackendPopoverRef: React.RefObject<HTMLDivElement | null>;\n collapsedBackendCloseTimer: React.MutableRefObject<ReturnType<\n typeof setTimeout\n > | null>;\n onOpenAddBackend: () => void;\n onOpenManageBackends: () => void;\n}\n\nexport function SidebarRailBody({\n collapsed,\n showCollapseToggle,\n showMobileCloseButton = false,\n onCloseMobile,\n linkDisabled,\n collapseToggleLabel,\n onCollapse,\n onExpand,\n showCollapsedExpandButton,\n isExtensionsActive,\n currentPath,\n navigate,\n activeBackendHealth,\n collapsedBackendPopoverOpen,\n setCollapsedBackendPopoverOpen,\n collapsedBackendPopoverRef,\n collapsedBackendCloseTimer,\n onOpenAddBackend,\n onOpenManageBackends,\n}: SidebarRailBodyProps) {\n const { t } = useTranslation(\"openhands\");\n const backendCloseTimerRef = collapsedBackendCloseTimer;\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className={sidebarHeaderRowClassName(collapsed)}>\n <div\n className={cn(\n collapsed && showCollapseToggle\n ? SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS\n : \"flex min-w-0 shrink-0 items-center\",\n )}\n >\n <div\n className={cn(\n collapsed &&\n showCollapseToggle &&\n \"flex h-full w-full items-center justify-start pl-2.5 transition-opacity duration-150\",\n collapsed && showCollapsedExpandButton && \"opacity-0\",\n )}\n >\n <OpenHandsLogoButton\n logoWidth={SIDEBAR_LOGO_WIDTH}\n logoHeight={SIDEBAR_LOGO_HEIGHT}\n logoClassName=\"max-w-none\"\n className={cn(SIDEBAR_ICON_SLOT_CLASS, \"overflow-visible\")}\n />\n </div>\n {collapsed && showCollapseToggle ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-collapse-toggle\"\n aria-pressed={collapsed}\n aria-label={collapseToggleLabel}\n onClick={onExpand}\n className={cn(\n SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,\n showCollapsedExpandButton\n ? \"opacity-100 pointer-events-auto\"\n : \"opacity-0 pointer-events-none\",\n )}\n >\n <ChevronRight width={14} height={14} />\n </button>\n ) : null}\n </div>\n {!collapsed && showCollapseToggle ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-collapse-toggle\"\n aria-pressed={collapsed}\n aria-label={collapseToggleLabel}\n onClick={onCollapse}\n className={cn(\n \"hidden md:inline-flex ml-auto\",\n SIDEBAR_ICON_BUTTON_CLASS,\n \"text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <ChevronLeft width={14} height={14} />\n </button>\n ) : null}\n {!collapsed && showMobileCloseButton ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-mobile-drawer-close\"\n onClick={onCloseMobile}\n aria-label={t(I18nKey.SIDEBAR$CLOSE_MENU)}\n className={cn(\n \"inline-flex ml-auto\",\n SIDEBAR_ICON_BUTTON_CLASS,\n \"text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <ChevronLeft width={14} height={14} />\n </button>\n ) : null}\n </div>\n\n <nav className={sidebarNavListClassName(collapsed)}>\n <SidebarNavLink\n to=\"/conversations\"\n end\n label=\"New Chat\"\n testId=\"sidebar-conversations-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n icon={<Plus width={ICON_SIZE} height={ICON_SIZE} />}\n />\n <SidebarNavLink\n to=\"/customize\"\n label=\"Customize\"\n testId=\"sidebar-skills-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n forceActive={isExtensionsActive}\n icon={\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={ICON_SIZE}\n height={ICON_SIZE}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z\" />\n <path d=\"m7 16.5-4.74-2.85\" />\n <path d=\"m7 16.5 5-3\" />\n <path d=\"M7 16.5v5.17\" />\n <path d=\"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z\" />\n <path d=\"m17 16.5-5-3\" />\n <path d=\"m17 16.5 4.74-2.85\" />\n <path d=\"m17 16.5v5.17\" />\n <path d=\"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z\" />\n <path d=\"M12 8 7.26 5.15\" />\n <path d=\"m12 8 4.74-2.85\" />\n <path d=\"M12 13.5V8\" />\n </svg>\n }\n />\n <SidebarNavLink\n to=\"/automations\"\n label={t(I18nKey.SIDEBAR$AUTOMATIONS)}\n testId=\"sidebar-automations-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n icon={<AutomationsIcon width={ICON_SIZE} height={ICON_SIZE} />}\n />\n </nav>\n\n <SidebarConversationList collapsed={collapsed} />\n\n {collapsed && showCollapseToggle ? (\n <nav\n className={cn(\n sidebarNavListClassName(collapsed),\n \"mt-auto pb-2 cursor-pointer\",\n )}\n >\n <StyledTooltip\n content={t(I18nKey.SIDEBAR$SETTINGS)}\n placement=\"right\"\n >\n <button\n type=\"button\"\n data-testid=\"collapsed-settings-link\"\n aria-label={t(I18nKey.SIDEBAR$SETTINGS)}\n onClick={() => navigate(\"/settings\")}\n className={sidebarNavRowClassName({ collapsed: true })}\n >\n <SidebarCollapsedIconSlot\n active={currentPath.startsWith(\"/settings\")}\n >\n <Settings width={ICON_SIZE} height={ICON_SIZE} />\n </SidebarCollapsedIconSlot>\n <span className={sidebarNavLabelClassName(true)}>\n {t(I18nKey.SIDEBAR$SETTINGS)}\n </span>\n </button>\n </StyledTooltip>\n <div\n className=\"relative\"\n ref={collapsedBackendPopoverRef}\n onMouseEnter={() => {\n if (backendCloseTimerRef.current) {\n clearTimeout(backendCloseTimerRef.current);\n backendCloseTimerRef.current = null;\n }\n setCollapsedBackendPopoverOpen(true);\n }}\n onMouseLeave={() => {\n backendCloseTimerRef.current = setTimeout(\n () => setCollapsedBackendPopoverOpen(false),\n 150,\n );\n }}\n >\n <button\n type=\"button\"\n data-testid=\"collapsed-backend-selector-link\"\n aria-label={t(I18nKey.BACKEND$MANAGE)}\n aria-expanded={collapsedBackendPopoverOpen}\n onMouseDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n }}\n onMouseUp={(event) => event.stopPropagation()}\n className={cn(\n sidebarNavRowClassName({ collapsed: true }),\n \"relative\",\n )}\n >\n <SidebarCollapsedIconSlot active={collapsedBackendPopoverOpen}>\n <span className=\"relative inline-flex size-[18px] shrink-0 items-center justify-center\">\n <BackendStatusDot\n isConnected={activeBackendHealth?.isConnected ?? null}\n className=\"absolute -left-0.5 -top-0.5 z-[1] pointer-events-none\"\n />\n <Server width={ICON_SIZE} height={ICON_SIZE} />\n </span>\n </SidebarCollapsedIconSlot>\n <span className={sidebarNavLabelClassName(true)}>\n {t(I18nKey.BACKEND$MANAGE)}\n </span>\n </button>\n {collapsedBackendPopoverOpen ? (\n <div\n className=\"absolute bottom-[-4px] left-full pl-2.5 z-40 w-[272px]\"\n onClick={(event) => event.stopPropagation()}\n >\n <BackendSelector\n sidebarCollapsed={collapsed}\n hideTrigger\n defaultOpen\n openUpward\n onSelectOption={() => setCollapsedBackendPopoverOpen(false)}\n onOpenAddBackend={onOpenAddBackend}\n onOpenManageBackends={onOpenManageBackends}\n />\n </div>\n ) : null}\n </div>\n </nav>\n ) : null}\n\n {!collapsed ? (\n <div\n className={cn(\n \"flex flex-col items-stretch max-w-none box-border shrink-0\",\n \"-ml-2.5 w-[calc(100%+0.625rem)] border-t border-[var(--oh-border)] pt-2 px-2.5\",\n )}\n >\n <BackendSelector sidebarCollapsed={collapsed} openUpward />\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sidebar-rail-body.cjs","names":[],"sources":["../../../../src/components/features/sidebar/sidebar-rail-body.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n ChevronLeft,\n ChevronRight,\n Plus,\n Server,\n Settings,\n} from \"lucide-react\";\nimport { OpenHandsLogoButton } from \"#/components/shared/buttons/openhands-logo-button\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport { SidebarCollapsedIconSlot } from \"./sidebar-collapsed-icon-slot\";\nimport { SidebarNavLink } from \"./sidebar-nav-link\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\nimport { BackendSelector } from \"#/components/features/backends/backend-selector\";\nimport { BackendStatusDot } from \"#/components/features/backends/backend-status-dot\";\nimport { SidebarConversationList } from \"./sidebar-conversation-list\";\nimport AutomationsIcon from \"#/icons/automations.svg?react\";\nimport {\n SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,\n SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS,\n SIDEBAR_ICON_BUTTON_CLASS,\n SIDEBAR_ICON_SLOT_CLASS,\n sidebarHeaderRowClassName,\n sidebarNavLabelClassName,\n sidebarNavListClassName,\n sidebarNavRowClassName,\n} from \"./sidebar-layout\";\n\nconst ICON_SIZE = 18;\nconst SIDEBAR_LOGO_WIDTH = 34;\nconst SIDEBAR_LOGO_HEIGHT = Math.round((SIDEBAR_LOGO_WIDTH * 30) / 46);\n\nexport interface SidebarRailBodyProps {\n collapsed: boolean;\n showCollapseToggle: boolean;\n showMobileCloseButton?: boolean;\n onCloseMobile?: () => void;\n linkDisabled: boolean;\n collapseToggleLabel: string;\n onCollapse: () => void;\n onExpand: () => void;\n showCollapsedExpandButton: boolean;\n isExtensionsActive: boolean;\n currentPath: string;\n activeBackendHealth: { isConnected: boolean | null } | undefined;\n collapsedBackendPopoverOpen: boolean;\n setCollapsedBackendPopoverOpen: (open: boolean) => void;\n collapsedBackendPopoverRef: React.RefObject<HTMLDivElement | null>;\n collapsedBackendCloseTimer: React.MutableRefObject<ReturnType<\n typeof setTimeout\n > | null>;\n onOpenAddBackend: () => void;\n onOpenManageBackends: () => void;\n}\n\nexport function SidebarRailBody({\n collapsed,\n showCollapseToggle,\n showMobileCloseButton = false,\n onCloseMobile,\n linkDisabled,\n collapseToggleLabel,\n onCollapse,\n onExpand,\n showCollapsedExpandButton,\n isExtensionsActive,\n currentPath,\n activeBackendHealth,\n collapsedBackendPopoverOpen,\n setCollapsedBackendPopoverOpen,\n collapsedBackendPopoverRef,\n collapsedBackendCloseTimer,\n onOpenAddBackend,\n onOpenManageBackends,\n}: SidebarRailBodyProps) {\n const { t } = useTranslation(\"openhands\");\n const backendCloseTimerRef = collapsedBackendCloseTimer;\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className={sidebarHeaderRowClassName(collapsed)}>\n <div\n className={cn(\n collapsed && showCollapseToggle\n ? SIDEBAR_COLLAPSED_LOGO_WRAPPER_CLASS\n : \"flex min-w-0 shrink-0 items-center\",\n )}\n >\n <div\n className={cn(\n collapsed &&\n showCollapseToggle &&\n \"flex h-full w-full items-center justify-start pl-2.5 transition-opacity duration-150\",\n collapsed && showCollapsedExpandButton && \"opacity-0\",\n )}\n >\n <OpenHandsLogoButton\n logoWidth={SIDEBAR_LOGO_WIDTH}\n logoHeight={SIDEBAR_LOGO_HEIGHT}\n logoClassName=\"max-w-none\"\n className={cn(SIDEBAR_ICON_SLOT_CLASS, \"overflow-visible\")}\n />\n </div>\n {collapsed && showCollapseToggle ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-collapse-toggle\"\n aria-pressed={collapsed}\n aria-label={collapseToggleLabel}\n onClick={onExpand}\n className={cn(\n SIDEBAR_COLLAPSE_TOGGLE_OVERLAY_CLASS,\n showCollapsedExpandButton\n ? \"opacity-100 pointer-events-auto\"\n : \"opacity-0 pointer-events-none\",\n )}\n >\n <ChevronRight width={14} height={14} />\n </button>\n ) : null}\n </div>\n {!collapsed && showCollapseToggle ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-collapse-toggle\"\n aria-pressed={collapsed}\n aria-label={collapseToggleLabel}\n onClick={onCollapse}\n className={cn(\n \"hidden md:inline-flex ml-auto\",\n SIDEBAR_ICON_BUTTON_CLASS,\n \"text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <ChevronLeft width={14} height={14} />\n </button>\n ) : null}\n {!collapsed && showMobileCloseButton ? (\n <button\n type=\"button\"\n data-testid=\"sidebar-mobile-drawer-close\"\n onClick={onCloseMobile}\n aria-label={t(I18nKey.SIDEBAR$CLOSE_MENU)}\n className={cn(\n \"inline-flex ml-auto\",\n SIDEBAR_ICON_BUTTON_CLASS,\n \"text-[var(--oh-muted)] hover:text-white hover:bg-[var(--oh-surface-raised)]\",\n )}\n >\n <ChevronLeft width={14} height={14} />\n </button>\n ) : null}\n </div>\n\n <nav className={sidebarNavListClassName(collapsed)}>\n <SidebarNavLink\n to=\"/conversations\"\n end\n label=\"New Chat\"\n testId=\"sidebar-conversations-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n icon={<Plus width={ICON_SIZE} height={ICON_SIZE} />}\n />\n <SidebarNavLink\n to=\"/customize\"\n label=\"Customize\"\n testId=\"sidebar-skills-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n forceActive={isExtensionsActive}\n icon={\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={ICON_SIZE}\n height={ICON_SIZE}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z\" />\n <path d=\"m7 16.5-4.74-2.85\" />\n <path d=\"m7 16.5 5-3\" />\n <path d=\"M7 16.5v5.17\" />\n <path d=\"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z\" />\n <path d=\"m17 16.5-5-3\" />\n <path d=\"m17 16.5 4.74-2.85\" />\n <path d=\"m17 16.5v5.17\" />\n <path d=\"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z\" />\n <path d=\"M12 8 7.26 5.15\" />\n <path d=\"m12 8 4.74-2.85\" />\n <path d=\"M12 13.5V8\" />\n </svg>\n }\n />\n <SidebarNavLink\n to=\"/automations\"\n label={t(I18nKey.SIDEBAR$AUTOMATIONS)}\n testId=\"sidebar-automations-link\"\n disabled={linkDisabled}\n collapsed={collapsed}\n icon={<AutomationsIcon width={ICON_SIZE} height={ICON_SIZE} />}\n />\n </nav>\n\n <SidebarConversationList collapsed={collapsed} />\n\n {collapsed && showCollapseToggle ? (\n <nav\n className={cn(\n sidebarNavListClassName(collapsed),\n \"mt-auto pb-2 cursor-pointer\",\n )}\n >\n <StyledTooltip\n content={t(I18nKey.SIDEBAR$SETTINGS)}\n placement=\"right\"\n >\n <NavigationLink\n to=\"/settings\"\n data-testid=\"collapsed-settings-link\"\n aria-label={t(I18nKey.SIDEBAR$SETTINGS)}\n className={sidebarNavRowClassName({ collapsed: true })}\n >\n <SidebarCollapsedIconSlot\n active={currentPath.startsWith(\"/settings\")}\n >\n <Settings width={ICON_SIZE} height={ICON_SIZE} />\n </SidebarCollapsedIconSlot>\n <span className={sidebarNavLabelClassName(true)}>\n {t(I18nKey.SIDEBAR$SETTINGS)}\n </span>\n </NavigationLink>\n </StyledTooltip>\n <div\n className=\"relative\"\n ref={collapsedBackendPopoverRef}\n onMouseEnter={() => {\n if (backendCloseTimerRef.current) {\n clearTimeout(backendCloseTimerRef.current);\n backendCloseTimerRef.current = null;\n }\n setCollapsedBackendPopoverOpen(true);\n }}\n onMouseLeave={() => {\n backendCloseTimerRef.current = setTimeout(\n () => setCollapsedBackendPopoverOpen(false),\n 150,\n );\n }}\n >\n <button\n type=\"button\"\n data-testid=\"collapsed-backend-selector-link\"\n aria-label={t(I18nKey.BACKEND$MANAGE)}\n aria-expanded={collapsedBackendPopoverOpen}\n onMouseDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n }}\n onMouseUp={(event) => event.stopPropagation()}\n className={cn(\n sidebarNavRowClassName({ collapsed: true }),\n \"relative\",\n )}\n >\n <SidebarCollapsedIconSlot active={collapsedBackendPopoverOpen}>\n <span className=\"relative inline-flex size-[18px] shrink-0 items-center justify-center\">\n <BackendStatusDot\n isConnected={activeBackendHealth?.isConnected ?? null}\n className=\"absolute -left-0.5 -top-0.5 z-[1] pointer-events-none\"\n />\n <Server width={ICON_SIZE} height={ICON_SIZE} />\n </span>\n </SidebarCollapsedIconSlot>\n <span className={sidebarNavLabelClassName(true)}>\n {t(I18nKey.BACKEND$MANAGE)}\n </span>\n </button>\n {collapsedBackendPopoverOpen ? (\n <div\n className=\"absolute bottom-[-4px] left-full pl-2.5 z-40 w-[272px]\"\n onClick={(event) => event.stopPropagation()}\n >\n <BackendSelector\n sidebarCollapsed={collapsed}\n hideTrigger\n defaultOpen\n openUpward\n onSelectOption={() => setCollapsedBackendPopoverOpen(false)}\n onOpenAddBackend={onOpenAddBackend}\n onOpenManageBackends={onOpenManageBackends}\n />\n </div>\n ) : null}\n </div>\n </nav>\n ) : null}\n\n {!collapsed ? (\n <div\n className={cn(\n \"flex flex-col items-stretch max-w-none box-border shrink-0\",\n \"-ml-2.5 w-[calc(100%+0.625rem)] border-t border-[var(--oh-border)] pt-2 px-2.5\",\n )}\n >\n <BackendSelector sidebarCollapsed={collapsed} openUpward />\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -11,7 +11,6 @@ export interface SidebarRailBodyProps {
11
11
  showCollapsedExpandButton: boolean;
12
12
  isExtensionsActive: boolean;
13
13
  currentPath: string;
14
- navigate: (path: string) => void;
15
14
  activeBackendHealth: {
16
15
  isConnected: boolean | null;
17
16
  } | undefined;
@@ -22,4 +21,4 @@ export interface SidebarRailBodyProps {
22
21
  onOpenAddBackend: () => void;
23
22
  onOpenManageBackends: () => void;
24
23
  }
25
- export declare function SidebarRailBody({ collapsed, showCollapseToggle, showMobileCloseButton, onCloseMobile, linkDisabled, collapseToggleLabel, onCollapse, onExpand, showCollapsedExpandButton, isExtensionsActive, currentPath, navigate, activeBackendHealth, collapsedBackendPopoverOpen, setCollapsedBackendPopoverOpen, collapsedBackendPopoverRef, collapsedBackendCloseTimer, onOpenAddBackend, onOpenManageBackends, }: SidebarRailBodyProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function SidebarRailBody({ collapsed, showCollapseToggle, showMobileCloseButton, onCloseMobile, linkDisabled, collapseToggleLabel, onCollapse, onExpand, showCollapsedExpandButton, isExtensionsActive, currentPath, activeBackendHealth, collapsedBackendPopoverOpen, setCollapsedBackendPopoverOpen, collapsedBackendPopoverRef, collapsedBackendCloseTimer, onOpenAddBackend, onOpenManageBackends, }: SidebarRailBodyProps): import("react/jsx-runtime").JSX.Element;