mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-24 08:34:23 +00:00
feat(web): rework the todo / background-task panels
Four fixes folded together: - The ~/todo TAB was still collapsible, so opening it could hide everything. Collapse now applies only to the floating card; the tab is always expanded. - The floating background-task card had no collapse. Add a chevron toggle while keeping the header click as 'open the tasks tab'. - The card truncated to 4 rows and showed '+N' even with room. Raise the cap to 12 and let the list scroll (max-height) so it shows more before truncating. - Bump the floating stack width 260→300 and card font so the panels aren't so cramped.
This commit is contained in:
parent
0f33d72a3c
commit
da18be330c
5 changed files with 79 additions and 20 deletions
|
|
@ -871,7 +871,9 @@ onUnmounted(() => {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 260px;
|
||||
/* Wider than the old 260px — the todo/task cards were cramped. */
|
||||
width: 300px;
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
@media (max-width: 1199px) {
|
||||
.float-stack { display: none; }
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
of the conversation pane, codex-style). Shows the running tasks only;
|
||||
clicking the header jumps to the full ~/tasks tab. -->
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import type { TaskItem } from '../types';
|
||||
|
||||
const MAX_ROWS = 4;
|
||||
// Show plenty of rows before truncating — the old cap of 4 collapsed even a
|
||||
// handful of tasks into a "+N" when the card had room. Beyond this the list
|
||||
// scrolls (see .tk-list max-height) instead of growing without bound.
|
||||
const MAX_ROWS = 12;
|
||||
|
||||
const props = defineProps<{
|
||||
tasks: TaskItem[];
|
||||
|
|
@ -19,6 +22,7 @@ const emit = defineEmits<{
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const collapsed = ref(false);
|
||||
const running = computed(() => props.tasks.filter((tk) => tk.state === 'run'));
|
||||
const shown = computed(() => running.value.slice(0, MAX_ROWS));
|
||||
const overflow = computed(() => running.value.length - shown.value.length);
|
||||
|
|
@ -26,12 +30,25 @@ const overflow = computed(() => running.value.length - shown.value.length);
|
|||
|
||||
<template>
|
||||
<div class="tasks-card">
|
||||
<button class="tk-head" type="button" :title="t('tasks.openTab')" @click="emit('open')">
|
||||
<span class="tk-dot" aria-hidden="true" />
|
||||
<span class="tk-title">{{ t('sidebar.tabTasks') }}</span>
|
||||
<span class="tk-count">{{ running.length }}</span>
|
||||
</button>
|
||||
<div class="tk-list">
|
||||
<div class="tk-head">
|
||||
<button class="tk-open" type="button" :title="t('tasks.openTab')" @click="emit('open')">
|
||||
<span class="tk-dot" aria-hidden="true" />
|
||||
<span class="tk-title">{{ t('sidebar.tabTasks') }}</span>
|
||||
<span class="tk-count">{{ running.length }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="tk-collapse"
|
||||
type="button"
|
||||
:aria-expanded="!collapsed"
|
||||
:title="collapsed ? t('tasks.expand') : t('tasks.collapse')"
|
||||
@click="collapsed = !collapsed"
|
||||
>
|
||||
<svg class="tk-chev" :class="{ open: !collapsed }" viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<polyline points="4,6 8,10 12,6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="!collapsed" class="tk-list">
|
||||
<div v-for="tk in shown" :key="tk.id" class="tk-row">
|
||||
<span class="tk-name">{{ tk.name }}</span>
|
||||
<span class="tk-time">{{ tk.timing }}</span>
|
||||
|
|
@ -53,18 +70,40 @@ const overflow = computed(() => running.value.length - shown.value.length);
|
|||
.tk-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
color: var(--muted);
|
||||
font-family: var(--mono);
|
||||
font-size: 12.5px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.tk-open {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 7px 4px 7px 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--muted);
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
.tk-head:hover { color: var(--ink); }
|
||||
.tk-open:hover { color: var(--ink); }
|
||||
.tk-collapse {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 7px 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--faint);
|
||||
}
|
||||
.tk-collapse:hover { color: var(--ink); }
|
||||
.tk-chev { transition: transform 0.15s; transform: rotate(-90deg); }
|
||||
.tk-chev.open { transform: none; }
|
||||
.tk-title { font-weight: 700; letter-spacing: 0.04em; }
|
||||
.tk-count { color: var(--faint); }
|
||||
|
||||
|
|
@ -84,6 +123,8 @@ const overflow = computed(() => running.value.length - shown.value.length);
|
|||
.tk-list {
|
||||
border-top: 1px solid var(--line);
|
||||
padding: 4px 10px 6px;
|
||||
max-height: 45vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.tk-row {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,16 @@ const props = defineProps<{
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
// Collapse only applies to the floating CARD (it overlays the transcript). In
|
||||
// the dedicated ~/todo TAB the whole pane IS the list, so collapsing it would
|
||||
// just hide everything the user opened the tab to see — keep it always open.
|
||||
const collapsed = ref(false);
|
||||
const canCollapse = computed(() => !props.inline);
|
||||
const showList = computed(() => props.inline || !collapsed.value);
|
||||
|
||||
function toggle(): void {
|
||||
if (canCollapse.value) collapsed.value = !collapsed.value;
|
||||
}
|
||||
|
||||
const doneCount = computed(() => props.todos.filter((td) => td.status === 'done').length);
|
||||
|
||||
|
|
@ -27,7 +36,7 @@ function glyph(status: TodoView['status']): string {
|
|||
|
||||
<template>
|
||||
<div class="todo-card" :class="{ 'tab-mode': inline }">
|
||||
<button class="tc-head" type="button" @click="collapsed = !collapsed">
|
||||
<component :is="canCollapse ? 'button' : 'div'" class="tc-head" :class="{ static: !canCollapse }" :type="canCollapse ? 'button' : undefined" @click="toggle">
|
||||
<svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<polyline points="2,4.5 3.5,6 5.5,3" />
|
||||
<polyline points="2,11 3.5,12.5 5.5,9.5" />
|
||||
|
|
@ -36,12 +45,12 @@ function glyph(status: TodoView['status']): string {
|
|||
</svg>
|
||||
<span class="tc-title">{{ t('tasks.todoTag') }}</span>
|
||||
<span v-if="todos.length > 0" class="tc-count">{{ doneCount }}/{{ todos.length }}</span>
|
||||
<svg v-if="todos.length > 0" class="tc-chev" :class="{ open: !collapsed }" viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<svg v-if="canCollapse && todos.length > 0" class="tc-chev" :class="{ open: !collapsed }" viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<polyline points="4,6 8,10 12,6" />
|
||||
</svg>
|
||||
</button>
|
||||
</component>
|
||||
|
||||
<div v-if="!collapsed" class="tc-list">
|
||||
<div v-if="showList" class="tc-list">
|
||||
<div v-if="todos.length === 0" class="tc-empty">{{ t('tasks.emptyTodo') }}</div>
|
||||
<div v-for="(td, i) in todos" :key="i" class="tc-row" :class="`s-${td.status}`">
|
||||
<span class="tc-glyph">{{ glyph(td.status) }}</span>
|
||||
|
|
@ -91,6 +100,9 @@ function glyph(status: TodoView['status']): string {
|
|||
line-height: 1.4;
|
||||
}
|
||||
.tc-head:hover { color: var(--ink); }
|
||||
/* Tab-mode header is a static label, not a collapse toggle. */
|
||||
.tc-head.static { cursor: default; }
|
||||
.tc-head.static:hover { color: var(--muted); }
|
||||
.tc-title { font-weight: 700; letter-spacing: 0.04em; }
|
||||
.tc-count { color: var(--faint); }
|
||||
.tc-chev {
|
||||
|
|
|
|||
|
|
@ -9,4 +9,6 @@ export default {
|
|||
emptyTasks: 'No background tasks running',
|
||||
emptyTodo: 'No todos yet',
|
||||
openTab: 'Open the tasks tab',
|
||||
collapse: 'Collapse',
|
||||
expand: 'Expand',
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -9,4 +9,6 @@ export default {
|
|||
emptyTasks: '暂无后台任务',
|
||||
emptyTodo: '暂无待办事项',
|
||||
openTab: '查看全部后台任务',
|
||||
collapse: '折叠',
|
||||
expand: '展开',
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue