Merge branch 'dev' of github.com:NeuralNomadsAI/CodeNomad into dev

This commit is contained in:
Shantur Rathore 2026-05-14 10:49:31 +01:00
commit d3950df816
14 changed files with 495 additions and 29 deletions

View file

@ -0,0 +1,85 @@
import { DropdownMenu } from "@kobalte/core/dropdown-menu"
import { For, Show, createSignal, onCleanup, type JSXElement } from "solid-js"
import { MoreHorizontal } from "lucide-solid"
export interface ActionOverflowMenuItem {
key: string
label: string
icon?: JSXElement
disabled?: boolean
destructive?: boolean
onSelect: () => void | Promise<void>
onMouseEnter?: () => void
onMouseLeave?: () => void
}
interface ActionOverflowMenuProps {
items: ActionOverflowMenuItem[]
label: string
triggerClass?: string
minItems?: number
}
export default function ActionOverflowMenu(props: ActionOverflowMenuProps) {
const [hoveredItem, setHoveredItem] = createSignal<ActionOverflowMenuItem | null>(null)
const enabledItems = () => props.items.filter((item) => !item.disabled)
const hasItems = () => props.items.length >= (props.minItems ?? 1)
const clearHoveredItem = () => {
const item = hoveredItem()
if (!item) return
item.onMouseLeave?.()
setHoveredItem(null)
}
onCleanup(clearHoveredItem)
return (
<Show when={hasItems()}>
<DropdownMenu placement="bottom-end" gutter={4} onOpenChange={(open) => { if (!open) clearHoveredItem() }}>
<DropdownMenu.Trigger
class={`action-overflow-trigger ${props.triggerClass ?? ""}`.trim()}
aria-label={props.label}
title={props.label}
disabled={enabledItems().length === 0}
>
<MoreHorizontal class="w-3.5 h-3.5" aria-hidden="true" />
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content class="action-overflow-content">
<For each={props.items}>
{(item) => (
<DropdownMenu.Item
class="action-overflow-item"
data-destructive={item.destructive ? "true" : undefined}
disabled={item.disabled}
onPointerEnter={() => {
if (item.disabled) return
const previous = hoveredItem()
if (previous !== item) previous?.onMouseLeave?.()
setHoveredItem(item)
item.onMouseEnter?.()
}}
onPointerLeave={() => {
if (item.disabled) return
if (hoveredItem() === item) setHoveredItem(null)
item.onMouseLeave?.()
}}
onSelect={() => {
clearHoveredItem()
void item.onSelect()
}}
>
<Show when={item.icon} fallback={<span class="action-overflow-item-icon" aria-hidden="true" />}>
{(icon) => <span class="action-overflow-item-icon" aria-hidden="true">{icon()}</span>}
</Show>
<span class="action-overflow-item-label">{item.label}</span>
</DropdownMenu.Item>
)}
</For>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu>
</Show>
)
}

View file

@ -1,5 +1,5 @@
import { For, Index, Match, Show, Suspense, Switch, createEffect, createMemo, createSignal, lazy, onCleanup, untrack, type Accessor } from "solid-js"
import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash } from "lucide-solid"
import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash, Volume2 } from "lucide-solid"
import MessageItem from "./message-item"
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
import type { ClientPart, MessageInfo } from "../types/message"
@ -18,6 +18,7 @@ import { useSpeech } from "../lib/hooks/use-speech"
import SpeechActionButton from "./speech-action-button"
import { createFollowScroll } from "../lib/follow-scroll"
import type { SessionSearchMatch } from "../lib/session-search"
import ActionOverflowMenu, { type ActionOverflowMenuItem } from "./action-overflow-menu"
function DeleteUpToIcon() {
return (
@ -486,6 +487,12 @@ function ToolCallItem(props: ToolCallItemProps) {
navigateToTaskSession(location)
}
const goToTaskSession = () => {
const location = taskLocation()
if (!location) return
navigateToTaskSession(location)
}
const handleDeleteMessage = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
@ -507,9 +514,7 @@ function ToolCallItem(props: ToolCallItemProps) {
}
}
const handleDeleteUpTo = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
const deleteUpTo = async () => {
if (!props.showDeleteMessage) return
if (!props.onDeleteMessagesUpTo) return
if (deletingUpTo()) return
@ -522,11 +527,72 @@ function ToolCallItem(props: ToolCallItemProps) {
}
}
const handleDeleteUpTo = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
await deleteUpTo()
}
const actionMenuItems = (): ActionOverflowMenuItem[] => {
const items: ActionOverflowMenuItem[] = []
if (taskSessionId()) {
items.push({
key: "go-to-session",
label: t("messageBlock.tool.goToSession.label"),
icon: <ExternalLink class="w-3.5 h-3.5" aria-hidden="true" />,
disabled: !taskLocation(),
onSelect: goToTaskSession,
})
}
if (props.showDeleteMessage) {
items.push(
{
key: "delete-up-to",
label: t("messageItem.actions.deleteMessagesUpTo"),
icon: <DeleteUpToIcon />,
disabled: !props.onDeleteMessagesUpTo || deletingUpTo(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "deleteUpTo", messageId: props.messageId }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: deleteUpTo,
},
{
key: "delete-message",
label: deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage"),
icon: <Trash class="w-3.5 h-3.5" aria-hidden="true" />,
disabled: deletingMessage(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "message", messageId: props.messageId }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: async () => {
if (deletingMessage()) return
setDeletingMessage(true)
try {
await deleteMessage(props.instanceId, props.sessionId, props.messageId)
} catch (error) {
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
title: t("messageItem.actions.deleteMessageFailedTitle"),
detail: error instanceof Error ? error.message : String(error),
variant: "error",
})
} finally {
setDeletingMessage(false)
}
},
},
)
}
return items
}
return (
<Show when={toolPart()}>
{(resolvedToolPart) => (
<div class="delete-hover-scope" data-delete-part-hover={isDeleteOverlayActive() ? "true" : undefined}>
<div class="tool-call-header-label">
<div class="tool-call-header-label" data-action-overflow={actionMenuItems().length > 1 ? "true" : undefined}>
<div class="tool-call-header-meta">
<Show when={props.showDeleteMessage}>
<input
@ -551,7 +617,7 @@ function ToolCallItem(props: ToolCallItemProps) {
<span class="tool-name">{toolName() || t("messageBlock.tool.unknown")}</span>
</div>
<div class="flex items-center gap-0">
<div class="tool-call-header-actions flex items-center gap-0">
<Show when={taskSessionId()}>
<button
class="tool-call-header-button"
@ -593,6 +659,12 @@ function ToolCallItem(props: ToolCallItemProps) {
</button>
</Show>
</div>
<ActionOverflowMenu
items={actionMenuItems()}
label={t("messageItem.actions.more")}
triggerClass="tool-call-header-button"
minItems={2}
/>
</div>
<Suspense fallback={<ToolCallFallback />}>
@ -1582,6 +1654,73 @@ function ReasoningCard(props: ReasoningCardProps) {
const canDeleteMessage = () => Boolean(props.showDeleteMessage) && !deletingMessage()
const deleteUpTo = async () => {
if (!props.showDeleteMessage) return
if (!props.onDeleteMessagesUpTo) return
if (deletingUpTo()) return
setDeletingUpTo(true)
try {
await props.onDeleteMessagesUpTo(props.messageId)
} finally {
setDeletingUpTo(false)
}
}
const actionMenuItems = (): ActionOverflowMenuItem[] => {
const items: ActionOverflowMenuItem[] = []
if (canSpeakReasoning()) {
items.push({
key: "speak",
label: speech.buttonTitle(),
icon: <Volume2 class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: () => void speech.toggle(),
})
}
if (props.showDeleteMessage) {
items.push(
{
key: "delete-up-to",
label: t("messageItem.actions.deleteMessagesUpTo"),
icon: <DeleteUpToIcon />,
disabled: !props.onDeleteMessagesUpTo || deletingUpTo(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "deleteUpTo", messageId: props.messageId }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: deleteUpTo,
},
{
key: "delete-message",
label: deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage"),
icon: <Trash class="w-3.5 h-3.5" aria-hidden="true" />,
disabled: !canDeleteMessage(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "message", messageId: props.messageId }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: async () => {
if (!canDeleteMessage()) return
setDeletingMessage(true)
try {
await deleteMessage(props.instanceId, props.sessionId, props.messageId)
} catch (error) {
showAlertDialog(t("messageItem.actions.deleteMessageFailedMessage"), {
title: t("messageItem.actions.deleteMessageFailedTitle"),
detail: error instanceof Error ? error.message : String(error),
variant: "error",
})
} finally {
setDeletingMessage(false)
}
},
},
)
}
return items
}
const handleDeleteMessage = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
@ -1604,16 +1743,7 @@ function ReasoningCard(props: ReasoningCardProps) {
const handleDeleteUpTo = async (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
if (!props.showDeleteMessage) return
if (!props.onDeleteMessagesUpTo) return
if (deletingUpTo()) return
setDeletingUpTo(true)
try {
await props.onDeleteMessagesUpTo(props.messageId)
} finally {
setDeletingUpTo(false)
}
await deleteUpTo()
}
return (
@ -1654,7 +1784,7 @@ function ReasoningCard(props: ReasoningCardProps) {
</span>
</button>
<div class="message-reasoning-actions">
<div class="message-reasoning-actions" data-action-overflow={actionMenuItems().length > 1 ? "true" : undefined}>
<Show when={canSpeakReasoning()}>
<SpeechActionButton
class="message-action-button"
@ -1671,7 +1801,7 @@ function ReasoningCard(props: ReasoningCardProps) {
<button
type="button"
class="message-action-button"
class="message-action-button message-reasoning-primary-action"
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
@ -1713,6 +1843,13 @@ function ReasoningCard(props: ReasoningCardProps) {
</button>
</Show>
<ActionOverflowMenu
items={actionMenuItems()}
label={t("messageItem.actions.more")}
triggerClass="message-action-button"
minItems={2}
/>
<span class="message-reasoning-time">{timestamp()}</span>
</div>
</div>

View file

@ -1,6 +1,6 @@
import { For, Show, createEffect, createSignal, onCleanup } from "solid-js"
import { Portal } from "solid-js/web"
import { Copy, ListStart, Split, Trash, Undo } from "lucide-solid"
import { Copy, ListStart, Split, Trash, Undo, Volume2 } from "lucide-solid"
import type { MessageInfo, ClientPart, SDKAssistantMessageV2 } from "../types/message"
import { isHiddenSyntheticTextPart, partHasRenderableText } from "../types/message"
import type { MessageRecord } from "../stores/message-v2/types"
@ -13,6 +13,7 @@ import { isTauriHost } from "../lib/runtime-env"
import type { DeleteHoverState } from "../types/delete-hover"
import { useSpeech } from "../lib/hooks/use-speech"
import SpeechActionButton from "./speech-action-button"
import ActionOverflowMenu, { type ActionOverflowMenuItem } from "./action-overflow-menu"
function DeleteUpToIcon() {
return (
@ -388,6 +389,71 @@ export default function MessageItem(props: MessageItemProps) {
return segments.join(" • ")
}
const actionMenuItems = (): ActionOverflowMenuItem[] => {
const items: ActionOverflowMenuItem[] = [
{
key: "copy",
label: copyLabel(),
icon: <Copy class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: handleCopy,
},
]
if (canSpeakMessage()) {
items.push({
key: "speak",
label: speech.buttonTitle(),
icon: <Volume2 class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: () => void speech.toggle(),
})
}
if (isUser() && props.onFork) {
items.push({
key: "fork",
label: t("messageItem.actions.fork"),
icon: <Split class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: () => props.onFork?.(props.record.id),
})
}
if (isUser() && props.onRevert) {
items.push({
key: "revert",
label: t("messageItem.actions.revertTitle"),
icon: <Undo class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: handleRevert,
})
}
if (props.showDeleteMessage) {
items.push(
{
key: "delete-up-to",
label: t("messageItem.actions.deleteMessagesUpTo"),
icon: <DeleteUpToIcon />,
disabled: !props.onDeleteMessagesUpTo || deletingUpTo(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "deleteUpTo", messageId: props.record.id }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: () => void handleDeleteUpTo(),
},
{
key: "delete-message",
label: deletingMessage() ? t("messageItem.actions.deletingMessage") : t("messageItem.actions.deleteMessage"),
icon: <Trash class="w-3.5 h-3.5" aria-hidden="true" />,
disabled: deletingMessage(),
destructive: true,
onMouseEnter: () => props.onDeleteHoverChange?.({ kind: "message", messageId: props.record.id }),
onMouseLeave: () => props.onDeleteHoverChange?.({ kind: "none" }),
onSelect: handleDeleteMessage,
},
)
}
return items
}
return (
<div
@ -440,7 +506,11 @@ export default function MessageItem(props: MessageItemProps) {
</Show>
</div>
<div class="message-item-actions" ref={(el) => (actionsEl = el)}>
<div
class="message-item-actions"
data-action-overflow={actionMenuItems().length > 1 ? "true" : undefined}
ref={(el) => (actionsEl = el)}
>
<Show when={isUser()}>
<div class="message-action-group">
<button
@ -510,6 +580,12 @@ export default function MessageItem(props: MessageItemProps) {
</button>
</Show>
</div>
<ActionOverflowMenu
items={actionMenuItems()}
label={t("messageItem.actions.more")}
triggerClass="message-action-button"
minItems={2}
/>
</Show>
<Show when={!isUser()}>
<div class="message-action-group">
@ -558,6 +634,12 @@ export default function MessageItem(props: MessageItemProps) {
</button>
</Show>
</div>
<ActionOverflowMenu
items={actionMenuItems()}
label={t("messageItem.actions.more")}
triggerClass="message-action-button"
minItems={2}
/>
</Show>
<time class="message-timestamp" dateTime={timestampIso()}>{timestamp()}</time>
</div>

View file

@ -1,5 +1,5 @@
import { createSignal, Show, createEffect, createMemo, onCleanup, type Accessor } from "solid-js"
import { ArrowRightSquare, Check, Copy, Hourglass, Loader2, XCircle } from "lucide-solid"
import { ArrowRightSquare, Check, Copy, Hourglass, Loader2, Volume2, XCircle } from "lucide-solid"
import { stringify as stringifyYaml } from "yaml"
import { messageStoreBus } from "../stores/message-v2/bus"
import { useTheme } from "../lib/theme"
@ -45,6 +45,7 @@ import { getLogger } from "../lib/logger"
import { useSpeech } from "../lib/hooks/use-speech"
import SpeechActionButton from "./speech-action-button"
import { createFollowScroll } from "../lib/follow-scroll"
import ActionOverflowMenu, { type ActionOverflowMenuItem } from "./action-overflow-menu"
const log = getLogger("session")
@ -821,23 +822,62 @@ export default function ToolCall(props: ToolCallProps) {
await copyToClipboard(text)
}
const actionMenuItems = (): ActionOverflowMenuItem[] => {
const items: ActionOverflowMenuItem[] = []
if (hasToolInput()) {
items.push({
key: "toggle-input",
label: isToolInputVisible() ? t("toolCall.header.hideInputTitle") : t("toolCall.header.showInputTitle"),
icon: <ArrowRightSquare class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: () => {
if (!expanded()) toggle()
const currentlyVisible = isToolInputVisible()
setToolInputVisibilityOverride(currentlyVisible ? "hidden" : "expanded")
},
})
}
items.push({
key: "copy",
label: t("toolCall.header.copyTitle"),
icon: <Copy class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: async () => {
const text = headerText()
if (!text) return
await copyToClipboard(text)
},
})
if (canSpeakToolCall()) {
items.push({
key: "speak",
label: speech.buttonTitle(),
icon: <Volume2 class="w-3.5 h-3.5" aria-hidden="true" />,
onSelect: () => void speech.toggle(),
})
}
return items
}
const status = () => toolState()?.status || ""
return (
<div
<div
ref={(element) => {
ref={(element) => {
setToolCallRootEl(element || undefined)
}}
class={`tool-call ${combinedStatusClass()}`}
data-part-type="tool"
data-tool-name={toolName()}
data-instance-id={props.instanceId}
data-session-id={props.sessionId}
data-message-id={props.messageId}
data-part-id={toolCallIdentifier()}
>
<div class="tool-call-header">
data-session-id={props.sessionId}
data-message-id={props.messageId}
data-part-id={toolCallIdentifier()}
>
<div class="tool-call-header" data-action-overflow={actionMenuItems().length > 1 ? "true" : undefined}>
<button
type="button"
class="tool-call-header-toggle"
@ -886,6 +926,13 @@ export default function ToolCall(props: ToolCallProps) {
/>
</Show>
<ActionOverflowMenu
items={actionMenuItems()}
label={t("messageItem.actions.more")}
triggerClass="tool-call-header-copy"
minItems={2}
/>
<ToolStatusIndicator status={status} />
</div>

View file

@ -89,6 +89,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "Copy",
"messageItem.actions.copyTitle": "Copy message",
"messageItem.actions.copied": "Copied!",
"messageItem.actions.more": "More actions",
"messageItem.actions.speak": "Speak message",
"messageItem.actions.generatingSpeech": "Generating speech",
"messageItem.actions.stopSpeech": "Stop playback",

View file

@ -91,6 +91,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "Copiar",
"messageItem.actions.copyTitle": "Copiar mensaje",
"messageItem.actions.copied": "¡Copiado!",
"messageItem.actions.more": "Más acciones",
"messageItem.actions.speak": "Reproducir mensaje",
"messageItem.actions.generatingSpeech": "Generando audio",
"messageItem.actions.stopSpeech": "Detener reproduccion",

View file

@ -91,6 +91,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "Copier",
"messageItem.actions.copyTitle": "Copier le message",
"messageItem.actions.copied": "Copié !",
"messageItem.actions.more": "Plus d'actions",
"messageItem.actions.speak": "Lire le message",
"messageItem.actions.generatingSpeech": "Generation de l'audio",
"messageItem.actions.stopSpeech": "Arreter la lecture",

View file

@ -89,6 +89,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "העתק",
"messageItem.actions.copyTitle": "העתק הודעה",
"messageItem.actions.copied": "הועתק!",
"messageItem.actions.more": "פעולות נוספות",
"messageItem.actions.speak": "השמע הודעה",
"messageItem.actions.generatingSpeech": "יוצר אודיו",
"messageItem.actions.stopSpeech": "עצור ניגון",

View file

@ -91,6 +91,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "コピー",
"messageItem.actions.copyTitle": "メッセージをコピー",
"messageItem.actions.copied": "コピーしました!",
"messageItem.actions.more": "その他の操作",
"messageItem.actions.speak": "メッセージを読み上げ",
"messageItem.actions.generatingSpeech": "音声を生成中",
"messageItem.actions.stopSpeech": "再生を停止",

View file

@ -91,6 +91,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "Копировать",
"messageItem.actions.copyTitle": "Копировать сообщение",
"messageItem.actions.copied": "Скопировано!",
"messageItem.actions.more": "Больше действий",
"messageItem.actions.speak": "Озвучить сообщение",
"messageItem.actions.generatingSpeech": "Генерация аудио",
"messageItem.actions.stopSpeech": "Остановить воспроизведение",

View file

@ -91,6 +91,7 @@ export const messagingMessages = {
"messageItem.actions.copy": "复制",
"messageItem.actions.copyTitle": "复制消息",
"messageItem.actions.copied": "已复制!",
"messageItem.actions.more": "更多操作",
"messageItem.actions.speak": "朗读消息",
"messageItem.actions.generatingSpeech": "正在生成语音",
"messageItem.actions.stopSpeech": "停止播放",

View file

@ -70,3 +70,62 @@
.dropdown-icon-accent {
color: var(--accent-primary);
}
.action-overflow-trigger {
display: none;
}
.action-overflow-content {
min-width: 12rem;
padding: 0.25rem;
border: 1px solid var(--border-base);
border-radius: 0;
background-color: var(--surface-base);
box-shadow: var(--popover-shadow);
color: var(--text-primary);
z-index: 1000;
}
.action-overflow-item {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
min-height: 2rem;
padding: 0.4rem 0.5rem;
border-radius: 0;
font-size: 0.8125rem;
line-height: 1.2;
color: var(--text-primary);
cursor: pointer;
user-select: none;
outline: none;
}
.action-overflow-item:hover,
.action-overflow-item[data-highlighted] {
background-color: var(--surface-hover);
}
.action-overflow-item[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.action-overflow-item[data-destructive="true"] {
color: var(--status-error);
}
.action-overflow-item-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1rem;
height: 1rem;
flex: 0 0 auto;
}
.action-overflow-item-label {
min-width: 0;
flex: 1 1 auto;
}

View file

@ -113,6 +113,18 @@
transform: scale(0.95);
}
.message-item-actions > .action-overflow-trigger {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .message-item-actions[data-action-overflow="true"] .message-action-group {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .message-item-actions[data-action-overflow="true"] > .action-overflow-trigger {
display: inline-flex;
}
.message-timestamp {
@apply text-[11px] text-[var(--text-muted)];
}
@ -433,10 +445,22 @@
.message-reasoning-actions {
display: inline-flex;
align-items: center;
gap: 0.5rem;
gap: 0;
padding: 0.25rem 0.6rem 0.25rem 0;
}
.message-reasoning-actions > .action-overflow-trigger {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .message-reasoning-actions[data-action-overflow="true"] > .message-action-button:not(.message-reasoning-primary-action):not(.action-overflow-trigger) {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .message-reasoning-actions[data-action-overflow="true"] > .action-overflow-trigger {
display: inline-flex;
}
.message-reasoning-toggle:hover {
background-color: transparent;
}

View file

@ -49,6 +49,18 @@
cursor: not-allowed;
}
.tool-call-header-label > .action-overflow-trigger {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .tool-call-header-label[data-action-overflow="true"] .tool-call-header-actions {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .tool-call-header-label[data-action-overflow="true"] > .action-overflow-trigger {
display: inline-flex;
}
.tool-call-header-label .tool-call-icon {
@apply text-base;
}
@ -151,6 +163,19 @@
color: var(--text-primary);
}
.tool-call-header > .action-overflow-trigger {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .tool-call-header[data-action-overflow="true"] > .tool-call-header-input,
.session-center-column[data-session-center-width="narrow"] .tool-call-header[data-action-overflow="true"] > .tool-call-header-copy:not(.action-overflow-trigger) {
display: none;
}
.session-center-column[data-session-center-width="narrow"] .tool-call-header[data-action-overflow="true"] > .action-overflow-trigger {
display: inline-flex;
}
.tool-call-header-status {
@apply inline-flex items-center justify-center;
font-size: 0.95rem;