Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-stuck-web-tooltip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix web tooltips that could get stuck on screen when their trigger element is removed while open.
5 changes: 5 additions & 0 deletions .changeset/trim-redundant-web-tooltips.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Trim redundant and incorrect tooltips in the web UI.
17 changes: 7 additions & 10 deletions apps/kimi-web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import type { AppConfig, ThinkingLevel } from './api/types';
import Button from './components/ui/Button.vue';
import IconButton from './components/ui/IconButton.vue';
import Icon from './components/ui/Icon.vue';
import Tooltip from './components/ui/Tooltip.vue';

// Hydrate the server-transport credential (fragment token or sessionStorage)
// BEFORE the client connects, so the first REST/WS calls already carry it.
Expand Down Expand Up @@ -625,15 +624,13 @@ function openPr(url: string): void {
@update:width="sessionColWidth = $event"
/>
<div v-if="sidebarCollapsed" class="sidebar-rail">
<Tooltip :text="t('sidebar.expandSidebar')">
<IconButton
size="sm"
:label="t('sidebar.expandSidebar')"
@click="toggleSidebarCollapse"
>
<Icon name="panel-expand" size="sm" />
</IconButton>
</Tooltip>
<IconButton
size="sm"
:label="t('sidebar.expandSidebar')"
@click="toggleSidebarCollapse"
>
<Icon name="panel-expand" size="sm" />
</IconButton>
</div>
</template>

Expand Down
90 changes: 38 additions & 52 deletions apps/kimi-web/src/components/FilePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -482,60 +482,46 @@ function truncatePath(path: string, maxLen = 55): string {
<span v-if="searchQuery.trim()" class="fp-search-count">
{{ searchMatches.length }}
</span>
<Tooltip :text="t('filePreview.prevMatch')">
<IconButton size="sm" :disabled="searchMatches.length === 0" :label="t('filePreview.prevMatch')" @click="nextMatch(-1)">
<Icon name="arrow-up" size="md" />
</IconButton>
</Tooltip>
<Tooltip :text="t('filePreview.nextMatch')">
<IconButton size="sm" :disabled="searchMatches.length === 0" :label="t('filePreview.nextMatch')" @click="nextMatch(1)">
<Icon name="arrow-down" size="md" />
</IconButton>
</Tooltip>
</div>
<!-- Icon actions: text labels made the header wrap to two rows at the
default panel width — icons + title tooltips keep it single-line. -->
<Tooltip :text="copiedPath ? t('filePreview.copied') : t('filePreview.copyPath')">
<IconButton size="sm" :class="{ copied: copiedPath }" :label="copiedPath ? t('filePreview.copied') : t('filePreview.copyPath')" @click="copyPath">
<Icon v-if="!copiedPath" name="link" size="md" />
<Icon v-else class="fp-check" name="check" size="md" />
</IconButton>
</Tooltip>
<Tooltip :text="t('filePreview.openInEditor')">
<IconButton v-if="externalActions" size="sm" :label="t('filePreview.openInEditor')" @click="emit('openExternal')">
<Icon name="external-link" size="md" />
<IconButton size="sm" :disabled="searchMatches.length === 0" :label="t('filePreview.prevMatch')" @click="nextMatch(-1)">
<Icon name="arrow-up" size="md" />
</IconButton>
</Tooltip>
<Tooltip :text="t('filePreview.reveal')">
<IconButton v-if="externalActions" size="sm" :label="t('filePreview.reveal')" @click="emit('reveal')">
<Icon name="folder" size="md" />
</IconButton>
</Tooltip>
<Tooltip :text="t('filePreview.download')">
<a
v-if="downloadUrl"
class="fp-download"
:href="downloadUrl"
target="_blank"
rel="noreferrer"
download
:aria-label="t('filePreview.download')"
>
<Icon name="download" size="md" />
</a>
</Tooltip>
<Tooltip :text="copied ? t('filePreview.copied') : t('filePreview.copy')">
<IconButton
v-if="!file.isBinary && contentKind !== 'image'"
size="sm"
:class="{ copied }"
:label="copied ? t('filePreview.copied') : t('filePreview.copy')"
@click="copyContent"
>
<Icon v-if="!copied" name="copy" size="md" />
<Icon v-else class="fp-check" name="check" size="md" />
<IconButton size="sm" :disabled="searchMatches.length === 0" :label="t('filePreview.nextMatch')" @click="nextMatch(1)">
<Icon name="arrow-down" size="md" />
</IconButton>
</Tooltip>
</div>
<!-- Icon actions: text labels made the header wrap to two rows at the
default panel width — icon-only buttons keep it single-line. -->
<IconButton size="sm" :class="{ copied: copiedPath }" :label="copiedPath ? t('filePreview.copied') : t('filePreview.copyPath')" @click="copyPath">
<Icon v-if="!copiedPath" name="link" size="md" />
<Icon v-else class="fp-check" name="check" size="md" />
</IconButton>
<IconButton v-if="externalActions" size="sm" :label="t('filePreview.openInEditor')" @click="emit('openExternal')">
<Icon name="external-link" size="md" />
</IconButton>
<IconButton v-if="externalActions" size="sm" :label="t('filePreview.reveal')" @click="emit('reveal')">
<Icon name="folder" size="md" />
</IconButton>
<a
v-if="downloadUrl"
class="fp-download"
:href="downloadUrl"
target="_blank"
rel="noreferrer"
download
:aria-label="t('filePreview.download')"
>
<Icon name="download" size="md" />
</a>
<IconButton
v-if="!file.isBinary && contentKind !== 'image'"
size="sm"
:class="{ copied }"
:label="copied ? t('filePreview.copied') : t('filePreview.copy')"
@click="copyContent"
>
<Icon v-if="!copied" name="copy" size="md" />
<Icon v-else class="fp-check" name="check" size="md" />
</IconButton>
</PanelHeader>

<!-- Body: Markdown -->
Expand Down
93 changes: 41 additions & 52 deletions apps/kimi-web/src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import IconButton from './ui/IconButton.vue';
import Icon from './ui/Icon.vue';
import Menu from './ui/Menu.vue';
import MenuItem from './ui/MenuItem.vue';
import Tooltip from './ui/Tooltip.vue';
import { useConfirmDialog } from '../composables/useConfirmDialog';

const { t } = useI18n();
Expand Down Expand Up @@ -553,24 +552,20 @@ onBeforeUnmount(() => {
<span class="ch-name">Kimi Code<span v-if="isDev" class="ch-endpoint"> · {{ endpoint }}</span></span>
<InternalBuildBanner />
</div>
<Tooltip :text="t('sidebar.collapseSidebar')">
<IconButton
size="sm"
:label="t('sidebar.collapseSidebar')"
@click.stop="emit('collapse')"
>
<Icon name="panel-collapse" />
</IconButton>
</Tooltip>
<Tooltip :text="t('settings.title')">
<IconButton
size="sm"
:label="t('settings.title')"
@click.stop="emit('openSettings')"
>
<Icon name="settings" />
</IconButton>
</Tooltip>
<IconButton
size="sm"
:label="t('sidebar.collapseSidebar')"
@click.stop="emit('collapse')"
>
<Icon name="panel-collapse" />
</IconButton>
<IconButton
size="sm"
:label="t('settings.title')"
@click.stop="emit('openSettings')"
>
<Icon name="settings" />
</IconButton>
</div>

<!-- Session search — opens the Spotlight-style search dialog -->
Expand All @@ -585,16 +580,14 @@ onBeforeUnmount(() => {
<Icon name="chat-new" />
<span>{{ t('sidebar.newChat') }}</span>
</button>
<Tooltip :text="t('sidebar.newWorkspace')">
<IconButton
v-if="showNewWorkspaceButton"
size="sm"
:label="t('sidebar.newWorkspace')"
@click.stop="emit('addWorkspace')"
>
<Icon name="folder" />
</IconButton>
</Tooltip>
<IconButton
v-if="showNewWorkspaceButton"
size="sm"
:label="t('sidebar.newWorkspace')"
@click.stop="emit('addWorkspace')"
>
<Icon name="folder" />
</IconButton>
</div>

<!-- Session list — grouped by workspace -->
Expand All @@ -609,29 +602,25 @@ onBeforeUnmount(() => {
<div class="side-section-label">
<span class="side-section-title">{{ t('sidebar.workspaces') }}</span>
<div class="side-section-actions">
<Tooltip :text="allCollapsed ? t('sidebar.expandAll') : t('sidebar.collapseAll')">
<IconButton
class="side-section-toggle"
size="sm"
:label="allCollapsed ? t('sidebar.expandAll') : t('sidebar.collapseAll')"
@click.stop="allCollapsed ? expandAllWorkspaces() : collapseAllWorkspaces()"
>
<Icon v-if="allCollapsed" name="expand" />
<Icon v-else name="collapse" />
</IconButton>
</Tooltip>
<Tooltip :text="t('sidebar.options')">
<IconButton
class="side-section-toggle side-section-kebab"
size="sm"
:label="t('sidebar.options')"
aria-haspopup="menu"
:aria-expanded="sectionMenuOpen"
@click.stop="toggleSectionMenu($event)"
>
<Icon name="dots-horizontal" />
</IconButton>
</Tooltip>
<IconButton
class="side-section-toggle"
size="sm"
:label="allCollapsed ? t('sidebar.expandAll') : t('sidebar.collapseAll')"
@click.stop="allCollapsed ? expandAllWorkspaces() : collapseAllWorkspaces()"
>
<Icon v-if="allCollapsed" name="expand" />
<Icon v-else name="collapse" />
</IconButton>
<IconButton
class="side-section-toggle side-section-kebab"
size="sm"
:label="t('sidebar.options')"
aria-haspopup="menu"
:aria-expanded="sectionMenuOpen"
@click.stop="toggleSectionMenu($event)"
>
<Icon name="dots-horizontal" />
</IconButton>
</div>
</div>
<div
Expand Down
42 changes: 19 additions & 23 deletions apps/kimi-web/src/components/WorkspaceGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,26 @@ function onHeaderDragStart(event: DragEvent): void {
@click.stop
/>

<Tooltip :text="t('sidebar.options')">
<IconButton
class="gh-more"
:class="{ open: wsMenuOpenId === group.workspace.id }"
size="sm"
:label="t('sidebar.options')"
aria-haspopup="menu"
:aria-expanded="wsMenuOpenId === group.workspace.id"
@click.stop="emit('toggleWsMenu', group.workspace, $event)"
>
<Icon name="dots-horizontal" size="sm" />
</IconButton>
</Tooltip>
<IconButton
class="gh-more"
:class="{ open: wsMenuOpenId === group.workspace.id }"
size="sm"
:label="t('sidebar.options')"
aria-haspopup="menu"
:aria-expanded="wsMenuOpenId === group.workspace.id"
@click.stop="emit('toggleWsMenu', group.workspace, $event)"
>
<Icon name="dots-horizontal" size="sm" />
</IconButton>

<Tooltip :text="t('workspace.newInGroup')">
<IconButton
class="gh-add"
size="sm"
:label="t('workspace.newInGroup')"
@click.stop="emit('createInWorkspace', group.workspace.id)"
>
<Icon name="chat-new" />
</IconButton>
</Tooltip>
<IconButton
class="gh-add"
size="sm"
:label="t('workspace.newInGroup')"
@click.stop="emit('createInWorkspace', group.workspace.id)"
>
<Icon name="chat-new" />
</IconButton>
</div>

<Tooltip :text="group.workspace.root">
Expand Down
20 changes: 9 additions & 11 deletions apps/kimi-web/src/components/chat/ApprovalCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,15 @@ onUnmounted(() => document.removeEventListener('keydown', handleKeydown));
</span>
<Badge v-if="agentName && !minimized" variant="neutral" size="sm">{{ t('approval.subagentBadge', { name: agentName }) }}</Badge>
<Badge v-if="!minimized" variant="warning" size="sm" class="aw">{{ t('approval.required') }}</Badge>
<Tooltip :text="minimized ? t('question.expand') : t('question.minimize')">
<IconButton
class="amin"
size="sm"
:label="minimized ? t('question.expand') : t('question.minimize')"
@click="minimized = !minimized"
>
<Icon v-if="minimized" name="chevron-down" size="md" />
<Icon v-else name="minus" size="md" />
</IconButton>
</Tooltip>
<IconButton
class="amin"
size="sm"
:label="minimized ? t('question.expand') : t('question.minimize')"
@click="minimized = !minimized"
>
<Icon v-if="minimized" name="chevron-down" size="md" />
<Icon v-else name="minus" size="md" />
</IconButton>
</div>
</template>

Expand Down
Loading
Loading