refactor(web): group components into area subdirectories (#1036)

Move the 40 feature-specific components out of the flat components/ into
chat/, settings/, dialogs/, and mobile/ subdirectories, leaving 9 shared
layout components at the top level. Recompute every relative import (no
path alias in the web app), refresh the line-1 path comments, and update
the layout description in AGENTS.md.

No behavior change; typecheck / test / build / lint all pass.
This commit is contained in:
qer 2026-06-23 20:34:46 +08:00 committed by GitHub
parent b1e6b64319
commit 866b91c8f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 130 additions and 125 deletions

View file

@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---
Reorganize the web app's components into area subdirectories (chat/settings/dialogs/mobile) and refresh the component path comments.

View file

@ -10,7 +10,7 @@ The browser web UI for Kimi Code — a peer to the TUI in `apps/kimi-code`. It t
- `main.ts` — bootstrap (creates the app, installs i18n, mounts `#app`). `App.vue` — root component, holds most app state.
- `api/` — server client. `index.ts` exposes the `getKimiWebApi()` singleton; `config.ts` builds REST/WS URLs; `daemon/` holds the wire client (`http.ts`, `ws.ts`, `wire.ts`, `mappers.ts`, `agentEventProjector.ts`, `eventReducer.ts`).
- `components/`~50 flat SFCs, no subdirectories.
- `components/`SFCs grouped by area: `chat/` (conversation/chat UI), `settings/` (settings & configuration), `dialogs/` (modal dialogs & sheets), `mobile/` (mobile-specific shell), plus shared layout components at the top level.
- `composables/` — reusable state logic, `useX` naming (`useKimiWebClient`, `useIsDark`, `usePaneLayout`, …).
- `lib/` — pure helpers (`parseDiff`, `slashCommands`, `sessionRoute`, `toolMeta`, …).
- `i18n/` — vue-i18n setup plus locale namespaces.

View file

@ -4,25 +4,25 @@ import { computed, nextTick, onMounted, onUnmounted, provide, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import Sidebar from './components/Sidebar.vue';
import ResizeHandle from './components/ResizeHandle.vue';
import ConversationPane from './components/ConversationPane.vue';
import ConversationPane from './components/chat/ConversationPane.vue';
import FilePreview from './components/FilePreview.vue';
import ThinkingPanel from './components/ThinkingPanel.vue';
import AgentDetailPanel from './components/AgentDetailPanel.vue';
import SideChatPanel from './components/SideChatPanel.vue';
import DiffView from './components/DiffView.vue';
import ModelPicker from './components/ModelPicker.vue';
import ProviderManager from './components/ProviderManager.vue';
import LoginDialog from './components/LoginDialog.vue';
import NewSessionDialog from './components/NewSessionDialog.vue';
import SettingsDialog from './components/SettingsDialog.vue';
import SessionsDialog from './components/SessionsDialog.vue';
import AddWorkspaceDialog from './components/AddWorkspaceDialog.vue';
import StatusPanel from './components/StatusPanel.vue';
import ThinkingPanel from './components/chat/ThinkingPanel.vue';
import AgentDetailPanel from './components/chat/AgentDetailPanel.vue';
import SideChatPanel from './components/chat/SideChatPanel.vue';
import DiffView from './components/chat/DiffView.vue';
import ModelPicker from './components/settings/ModelPicker.vue';
import ProviderManager from './components/settings/ProviderManager.vue';
import LoginDialog from './components/dialogs/LoginDialog.vue';
import NewSessionDialog from './components/dialogs/NewSessionDialog.vue';
import SettingsDialog from './components/settings/SettingsDialog.vue';
import SessionsDialog from './components/dialogs/SessionsDialog.vue';
import AddWorkspaceDialog from './components/dialogs/AddWorkspaceDialog.vue';
import StatusPanel from './components/chat/StatusPanel.vue';
import WarningToasts from './components/WarningToasts.vue';
import MobileTopBar from './components/MobileTopBar.vue';
import MobileSwitcherSheet from './components/MobileSwitcherSheet.vue';
import MobileSettingsSheet from './components/MobileSettingsSheet.vue';
import Onboarding from './components/Onboarding.vue';
import MobileTopBar from './components/mobile/MobileTopBar.vue';
import MobileSwitcherSheet from './components/mobile/MobileSwitcherSheet.vue';
import MobileSettingsSheet from './components/mobile/MobileSettingsSheet.vue';
import Onboarding from './components/settings/Onboarding.vue';
import GlobalLoading from './components/GlobalLoading.vue';
import DebugPanel from './debug/DebugPanel.vue';
import { isTraceEnabled } from './debug/trace';

View file

@ -3,7 +3,7 @@
<script setup lang="ts">
import { computed, inject, nextTick, provide, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import Markdown from './Markdown.vue';
import Markdown from './chat/Markdown.vue';
import type { FileData, FilePreviewRequest } from '../types';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/ActivityNotice.vue -->
<!-- apps/kimi-web/src/components/chat/ActivityNotice.vue -->
<!-- Generic in-transcript "working on X" notice: the moon-phase spinner plus a
body-sized label. Used for long-running session activities that are not a
chat turn (e.g. "Compacting context…"). Renders inline at the end of the

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { AgentMember } from '../types';
import type { AgentMember } from '../../types';
const props = defineProps<{ member: AgentMember; compact?: boolean }>();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/AgentDetailPanel.vue -->
<!-- apps/kimi-web/src/components/chat/AgentDetailPanel.vue -->
<!-- A subagent's full detail in the right-side panel (App's shared slot opening
this replaces a thinking/compaction/file view and vice versa). Mirrors the
thinking panel: the content is reactive, so a still-running subagent keeps
@ -7,7 +7,7 @@
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { AgentMember } from '../types';
import type { AgentMember } from '../../types';
const props = defineProps<{ member: AgentMember }>();

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import type { AgentMember } from '../types';
import type { AgentMember } from '../../types';
import AgentCard from './AgentCard.vue';
const props = defineProps<{ members: AgentMember[] }>();

View file

@ -1,9 +1,9 @@
<!-- apps/kimi-web/src/components/ApprovalCard.vue -->
<!-- apps/kimi-web/src/components/chat/ApprovalCard.vue -->
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ApprovalBlock } from '../types';
import type { ApprovalDecision } from '../api/types';
import type { ApprovalBlock } from '../../types';
import type { ApprovalDecision } from '../../api/types';
const props = defineProps<{
block: ApprovalBlock;

View file

@ -5,8 +5,8 @@
<script setup lang="ts">
import { onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ActivationBadges, ApprovalBlock, ConversationStatus, PermissionMode, QueuedPromptView, TaskItem, TodoView, UIQuestion } from '../types';
import type { AppGoal, AppModel, AppSkill, QuestionResponse, ThinkingLevel } from '../api/types';
import type { ActivationBadges, ApprovalBlock, ConversationStatus, PermissionMode, QueuedPromptView, TaskItem, TodoView, UIQuestion } from '../../types';
import type { AppGoal, AppModel, AppSkill, QuestionResponse, ThinkingLevel } from '../../api/types';
import type { FileItem } from './MentionMenu.vue';
import Composer from './Composer.vue';
import GoalStrip from './GoalStrip.vue';

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/ChatHeader.vue -->
<!-- apps/kimi-web/src/components/chat/ChatHeader.vue -->
<!-- Thin context bar above the chat: workspace / session name, git branch +
status, "open in editor", and a more-menu that bundles copy-all plus
the same session actions available from the sidebar session row. -->

View file

@ -1,16 +1,16 @@
<!-- apps/kimi-web/src/components/ChatPane.vue -->
<!-- apps/kimi-web/src/components/chat/ChatPane.vue -->
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ChatTurn, ApprovalBlock, FilePreviewRequest, ToolMedia } from '../types';
import type { ChatTurn, ApprovalBlock, FilePreviewRequest, ToolMedia } from '../../types';
import ToolCall from './ToolCall.vue';
import Markdown from './Markdown.vue';
import ThinkingBlock from './ThinkingBlock.vue';
import ActivityNotice from './ActivityNotice.vue';
import AgentCard from './AgentCard.vue';
import AgentGroup from './AgentGroup.vue';
import MoonSpinner from './MoonSpinner.vue';
import { formatMessageTime } from '../lib/formatMessageTime';
import MoonSpinner from '../MoonSpinner.vue';
import { formatMessageTime } from '../../lib/formatMessageTime';
import {
assistantRenderBlocks,
formatDuration,
@ -21,7 +21,7 @@ import {
turnBlocks,
turnFinalText,
turnToMarkdown,
} from './chatTurnRendering';
} from '../chatTurnRendering';
const { t } = useI18n();

View file

@ -1,19 +1,19 @@
<!-- apps/kimi-web/src/components/Composer.vue -->
<!-- apps/kimi-web/src/components/chat/Composer.vue -->
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import SlashMenu from './SlashMenu.vue';
import MentionMenu from './MentionMenu.vue';
import { buildSlashItems, parseSlash } from '../lib/slashCommands';
import { buildSlashItems, parseSlash } from '../../lib/slashCommands';
import type { FileItem } from './MentionMenu.vue';
import type { ActivationBadges, ConversationStatus, PermissionMode, QueuedPromptView } from '../types';
import type { AppModel, AppSkill, ThinkingLevel } from '../api/types';
import { modelThinkingAvailability } from '../lib/modelThinking';
import { useInputHistory } from '../composables/useInputHistory';
import { useSlashMenu } from '../composables/useSlashMenu';
import { useMentionMenu } from '../composables/useMentionMenu';
import { useComposerDraft } from '../composables/useComposerDraft';
import { useAttachmentUpload } from '../composables/useAttachmentUpload';
import type { ActivationBadges, ConversationStatus, PermissionMode, QueuedPromptView } from '../../types';
import type { AppModel, AppSkill, ThinkingLevel } from '../../api/types';
import { modelThinkingAvailability } from '../../lib/modelThinking';
import { useInputHistory } from '../../composables/useInputHistory';
import { useSlashMenu } from '../../composables/useSlashMenu';
import { useMentionMenu } from '../../composables/useMentionMenu';
import { useComposerDraft } from '../../composables/useComposerDraft';
import { useAttachmentUpload } from '../../composables/useAttachmentUpload';
// ---------------------------------------------------------------------------
// Props & emits

View file

@ -1,10 +1,10 @@
<!-- apps/kimi-web/src/components/ConversationPane.vue -->
<!-- apps/kimi-web/src/components/chat/ConversationPane.vue -->
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch, type ComponentPublicInstance } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ActivationBadges, ApprovalBlock, ChatTurn, ConversationStatus, FilePreviewRequest, PermissionMode, QueuedPromptView, TaskItem, TodoView, ToolMedia, UIQuestion, WorkspaceView } from '../types';
import type { AppGoal, AppModel, AppSkill, QuestionResponse, ThinkingLevel } from '../api/types';
import type { SwarmGroup } from '../composables/swarmGroups';
import type { ActivationBadges, ApprovalBlock, ChatTurn, ConversationStatus, FilePreviewRequest, PermissionMode, QueuedPromptView, TaskItem, TodoView, ToolMedia, UIQuestion, WorkspaceView } from '../../types';
import type { AppGoal, AppModel, AppSkill, QuestionResponse, ThinkingLevel } from '../../api/types';
import type { SwarmGroup } from '../../composables/swarmGroups';
import type { FileItem } from './MentionMenu.vue';
import ChatPane from './ChatPane.vue';
import ChatHeader from './ChatHeader.vue';
@ -12,8 +12,8 @@ import Composer from './Composer.vue';
import SwarmCard from './SwarmCard.vue';
import ChatDock from './ChatDock.vue';
import ConversationToc, { type ConversationTocItem } from './ConversationToc.vue';
import { getVisibleWorkspaces } from '../lib/workspacePicker';
import { safeRemove, STORAGE_KEYS } from '../lib/storage';
import { getVisibleWorkspaces } from '../../lib/workspacePicker';
import { safeRemove, STORAGE_KEYS } from '../../lib/storage';
const props = defineProps<{
turns: ChatTurn[];

View file

@ -1,8 +1,8 @@
<!-- apps/kimi-web/src/components/ConversationToc.vue -->
<!-- apps/kimi-web/src/components/chat/ConversationToc.vue -->
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ChatTurn } from '../types';
import type { ChatTurn } from '../../types';
export interface ConversationTocItem {
id: string;

View file

@ -1,11 +1,11 @@
<!-- apps/kimi-web/src/components/DiffView.vue -->
<!-- apps/kimi-web/src/components/chat/DiffView.vue -->
<!-- ~/diff tab: real git changes from the daemon's fs:git_status, with a
line-by-line unified-diff view (fs:diff) when a file is tapped.
The changed-file list can be viewed as a flat list or as a tree. -->
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { DiffViewLine } from '../types';
import type { DiffViewLine } from '../../types';
const { t } = useI18n();

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { AppGoal } from '../api/types';
import type { AppGoal } from '../../api/types';
const props = defineProps<{ goal: AppGoal; forceExpanded?: number }>();
const emit = defineEmits<{ controlGoal: [action: 'pause' | 'resume' | 'cancel'] }>();

View file

@ -1,12 +1,12 @@
<!-- apps/kimi-web/src/components/Markdown.vue -->
<!-- apps/kimi-web/src/components/chat/Markdown.vue -->
<script setup lang="ts">
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { MarkdownRender } from 'markstream-vue';
import { useIsDark } from '../composables/useIsDark';
import type { FilePreviewRequest } from '../types';
import { collectFilePathAliases, findFilePathLinks } from '../lib/filePathLinks';
import { markdownRenderPlan } from '../lib/markdownPerformance';
import { useIsDark } from '../../composables/useIsDark';
import type { FilePreviewRequest } from '../../types';
import { collectFilePathAliases, findFilePathLinks } from '../../lib/filePathLinks';
import { markdownRenderPlan } from '../../lib/markdownPerformance';
// px-based CSS build (our app is px, not rem). Imported here so the styles
// load wherever Markdown is used; scoped overrides below re-skin it to
// Terminal Pro. Importing the same file from multiple components is a no-op

View file

@ -1,8 +1,8 @@
<!-- apps/kimi-web/src/components/MentionMenu.vue -->
<!-- apps/kimi-web/src/components/chat/MentionMenu.vue -->
<!-- Popup list of file paths shown when user types @ in the Composer textarea. -->
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import type { FileItem } from '../types';
import type { FileItem } from '../../types';
// Re-exported for the .vue consumers (Composer / ChatDock / ConversationPane)
// that import FileItem from this component.

View file

@ -1,11 +1,11 @@
<!-- apps/kimi-web/src/components/OpenInMenu.vue -->
<!-- apps/kimi-web/src/components/chat/OpenInMenu.vue -->
<!-- "Open" button group for the chat header: workspace path label + quick-open
(last used target) + dropdown caret, matching the kimi-cli/web pattern.
Falls back to a simple icon+text "Open" button on non-mac platforms. -->
<script setup lang="ts">
import { computed, nextTick, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { safeGetString, safeSetString, STORAGE_KEYS } from '../lib/storage';
import { safeGetString, safeSetString, STORAGE_KEYS } from '../../lib/storage';
const { t } = useI18n();

View file

@ -1,9 +1,9 @@
<!-- apps/kimi-web/src/components/QuestionCard.vue -->
<!-- apps/kimi-web/src/components/chat/QuestionCard.vue -->
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { UIQuestion } from '../types';
import type { QuestionAnswer, QuestionResponse } from '../api/types';
import type { UIQuestion } from '../../types';
import type { QuestionAnswer, QuestionResponse } from '../../api/types';
import Markdown from './Markdown.vue';
const props = defineProps<{ question: UIQuestion }>();

View file

@ -1,7 +1,7 @@
<!-- apps/kimi-web/src/components/QueuePane.vue -->
<!-- apps/kimi-web/src/components/chat/QueuePane.vue -->
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import type { QueuedPromptView } from '../types';
import type { QueuedPromptView } from '../../types';
const props = defineProps<{
queued: QueuedPromptView[];

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/SideChatPanel.vue -->
<!-- apps/kimi-web/src/components/chat/SideChatPanel.vue -->
<!-- BTW "side chat": a side-channel agent rendered in the right-side panel.
It keeps the parent's context without creating a sidebar session. Reuses
ChatPane for the transcript; its panel-open emits are no-ops here. -->
@ -6,8 +6,8 @@
import { computed, nextTick, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import ChatPane from './ChatPane.vue';
import MoonSpinner from './MoonSpinner.vue';
import type { ChatTurn } from '../types';
import MoonSpinner from '../MoonSpinner.vue';
import type { ChatTurn } from '../../types';
const props = defineProps<{
turns: ChatTurn[];

View file

@ -1,9 +1,9 @@
<!-- apps/kimi-web/src/components/SlashMenu.vue -->
<!-- apps/kimi-web/src/components/chat/SlashMenu.vue -->
<!-- Popup list of slash commands shown above the Composer textarea. -->
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { SlashCommand } from '../lib/slashCommands';
import type { SlashCommand } from '../../lib/slashCommands';
const { t } = useI18n();

View file

@ -1,11 +1,11 @@
<!-- apps/kimi-web/src/components/StatusPanel.vue -->
<!-- apps/kimi-web/src/components/chat/StatusPanel.vue -->
<!-- /status overlay renders the CURRENT session status from existing client -->
<!-- state (no daemon call). Light only, monospace, Kimi blue, no emoji. -->
<script setup lang="ts">
import { computed, onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ConversationStatus, PermissionMode } from '../types';
import type { ThinkingLevel } from '../api/types';
import type { ConversationStatus, PermissionMode } from '../../types';
import type { ThinkingLevel } from '../../api/types';
const { t } = useI18n();

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { AppSubagentPhase } from '../api/types';
import type { SwarmGroup, SwarmMember } from '../composables/swarmGroups';
import type { AppSubagentPhase } from '../../api/types';
import type { SwarmGroup, SwarmMember } from '../../composables/swarmGroups';
const props = defineProps<{ group: SwarmGroup }>();

View file

@ -1,10 +1,10 @@
<!-- apps/kimi-web/src/components/TasksPane.vue -->
<!-- apps/kimi-web/src/components/chat/TasksPane.vue -->
<!-- TUI-inspired todo list: clean rows with status glyphs, strikethrough done,
compact output, minimal chrome. Matches the terminal todo-panel style. -->
<script setup lang="ts">
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import type { TaskItem } from '../types';
import type { TaskItem } from '../../types';
defineProps<{ tasks: TaskItem[] }>();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/ThinkingBlock.vue -->
<!-- apps/kimi-web/src/components/chat/ThinkingBlock.vue -->
<!-- 9e97773-style presentation: while this block is streaming it shows a live
5-line scrolling window; when the stream moves past it the window folds
into a one-paragraph teaser (the LAST paragraph of the thinking text).

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/ThinkingPanel.vue -->
<!-- apps/kimi-web/src/components/chat/ThinkingPanel.vue -->
<!-- Full thinking text in the right-side panel (App's shared preview slot
opening this replaces a file preview and vice versa). Content is reactive:
while the block is still streaming the text keeps growing, and the body

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/TodoCard.vue -->
<!-- apps/kimi-web/src/components/chat/TodoCard.vue -->
<!-- Todo list driven by the model's TodoList tool (latest full-list write
wins). Two render modes: a bordered card (used inside the wide-screen
floating stack the PARENT positions it) and `inline` for the ~/todo tab.
@ -6,7 +6,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { TodoView } from '../types';
import type { TodoView } from '../../types';
const props = defineProps<{
todos: TodoView[];

View file

@ -1,8 +1,8 @@
<!-- apps/kimi-web/src/components/ToolCall.vue -->
<!-- apps/kimi-web/src/components/chat/ToolCall.vue -->
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import type { ToolCall, ToolMedia } from '../types';
import { toolLabel, toolGlyph, toolChip, toolSummary } from '../lib/toolMeta';
import type { ToolCall, ToolMedia } from '../../types';
import { toolLabel, toolGlyph, toolChip, toolSummary } from '../../lib/toolMeta';
const props = withDefaults(
defineProps<{

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/AddWorkspaceDialog.vue -->
<!-- apps/kimi-web/src/components/dialogs/AddWorkspaceDialog.vue -->
<!-- Daemon-driven folder browser for adding a workspace: starts at $HOME -->
<!-- (fs:home), shows recent roots as quick-picks, a clickable breadcrumb, and -->
<!-- the folder list (fs:browse). "Open this folder" adds the current path. -->
@ -7,7 +7,7 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { FsBrowseEntry, FsBrowseResult } from '../api/types';
import type { FsBrowseEntry, FsBrowseResult } from '../../api/types';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/BottomSheet.vue -->
<!-- apps/kimi-web/src/components/dialogs/BottomSheet.vue -->
<!-- Reusable mobile bottom sheet: a fading scrim + a panel that slides up from -->
<!-- the bottom (rounded top, grab handle). v-model controls open state; tapping -->
<!-- the scrim or the grab handle closes it. Terminal Pro styling, no emoji. -->

View file

@ -1,10 +1,10 @@
<!-- apps/kimi-web/src/components/LoginDialog.vue -->
<!-- apps/kimi-web/src/components/dialogs/LoginDialog.vue -->
<!-- Managed Kimi OAuth device-code login dialog. -->
<!-- Light only, monospace-forward, Kimi blue #1565C0, no emoji. -->
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDialogFocus } from '../composables/useDialogFocus';
import { useDialogFocus } from '../../composables/useDialogFocus';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/NewSessionDialog.vue -->
<!-- apps/kimi-web/src/components/dialogs/NewSessionDialog.vue -->
<!-- Modal dialog for creating a new session with a required working directory. -->
<!-- Light only, monospace-forward, Kimi blue #1565C0, no emoji. -->
<script setup lang="ts">

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/SessionsDialog.vue -->
<!-- apps/kimi-web/src/components/dialogs/SessionsDialog.vue -->
<!-- Session browser popup: lists ALL client-side sessions, searchable by title, -->
<!-- click to switch. Reuses the composable's sessions / workspaceGroups / -->
<!-- attentionBySession view data no daemon call. -->
@ -6,7 +6,7 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { Session, WorkspaceGroup } from '../types';
import type { Session, WorkspaceGroup } from '../../types';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/MobileSettingsSheet.vue -->
<!-- apps/kimi-web/src/components/mobile/MobileSettingsSheet.vue -->
<!-- Mobile settings: a bottom sheet that surfaces the desktop Composer-toolbar -->
<!-- controls as big tappable rows model (opens ModelPicker), thinking level -->
<!-- (inline cycle picker), plan mode (toggle), permission (cycle), and a -->
@ -8,11 +8,11 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import type { ConversationStatus, PermissionMode } from '../types';
import type { ThinkingLevel } from '../api/types';
import type { ColorScheme, Theme } from '../composables/useKimiWebClient';
import BottomSheet from './BottomSheet.vue';
import LanguageSwitcher from './LanguageSwitcher.vue';
import type { ConversationStatus, PermissionMode } from '../../types';
import type { ThinkingLevel } from '../../api/types';
import type { ColorScheme, Theme } from '../../composables/useKimiWebClient';
import BottomSheet from '../dialogs/BottomSheet.vue';
import LanguageSwitcher from '../settings/LanguageSwitcher.vue';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/MobileSwitcherSheet.vue -->
<!-- apps/kimi-web/src/components/mobile/MobileSwitcherSheet.vue -->
<!-- Mobile switcher bottom sheet, mirroring the desktop sidebar: a "+ New
chat" row, then collapsible workspace groups (folder icon + name +
branch/path sub-line + per-group "+") with their session rows beneath.
@ -7,8 +7,8 @@
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { Session, WorkspaceGroup, WorkspaceView } from '../types';
import BottomSheet from './BottomSheet.vue';
import type { Session, WorkspaceGroup, WorkspaceView } from '../../types';
import BottomSheet from '../dialogs/BottomSheet.vue';
const { t } = useI18n();

View file

@ -1,4 +1,4 @@
<!-- apps/kimi-web/src/components/MobileTopBar.vue -->
<!-- apps/kimi-web/src/components/mobile/MobileTopBar.vue -->
<!-- Mobile title bar (50px): a 28px dark workspace square, a tappable middle -->
<!-- zone showing the mono `workspace / session ⌄` path with a status sub-line -->
<!-- ( running · branch · N sessions), and a trailing sliders button. Tapping -->
@ -7,7 +7,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import type { WorkspaceView } from '../types';
import type { WorkspaceView } from '../../types';
const { t } = useI18n();

View file

@ -1,7 +1,7 @@
<!-- apps/kimi-web/src/components/LanguageSwitcher.vue -->
<!-- apps/kimi-web/src/components/settings/LanguageSwitcher.vue -->
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { availableLocales, setLocale, type LocaleCode } from '../i18n';
import { availableLocales, setLocale, type LocaleCode } from '../../i18n';
const { locale } = useI18n();

View file

@ -1,11 +1,11 @@
<!-- apps/kimi-web/src/components/ModelPicker.vue -->
<!-- apps/kimi-web/src/components/settings/ModelPicker.vue -->
<!-- Modal overlay for switching the active session's model. -->
<!-- Light only, monospace-forward, Kimi blue #1565C0, no emoji. -->
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { AppModel } from '../api/types';
import { useDialogFocus } from '../composables/useDialogFocus';
import type { AppModel } from '../../api/types';
import { useDialogFocus } from '../../composables/useDialogFocus';
const { t } = useI18n();

View file

@ -1,12 +1,12 @@
<!-- apps/kimi-web/src/components/Onboarding.vue -->
<!-- apps/kimi-web/src/components/settings/Onboarding.vue -->
<!-- First-run onboarding overlay: a short welcome + the two preferences
(language, theme). Both apply live. Re-openable from the settings popover.
Preferences can be changed any time later, so there's nothing to "lose". -->
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { availableLocales, setLocale, type LocaleCode } from '../i18n';
import type { Theme } from '../composables/useKimiWebClient';
import { availableLocales, setLocale, type LocaleCode } from '../../i18n';
import type { Theme } from '../../composables/useKimiWebClient';
const props = defineProps<{ theme: Theme }>();
const emit = defineEmits<{ setTheme: [theme: Theme]; complete: []; skip: [] }>();

View file

@ -1,11 +1,11 @@
<!-- apps/kimi-web/src/components/ProviderManager.vue -->
<!-- apps/kimi-web/src/components/settings/ProviderManager.vue -->
<!-- Modal overlay for managing providers: list, add, refresh, delete. -->
<!-- Light only, monospace-forward, Kimi blue #1565C0, no emoji. -->
<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import type { AppProvider } from '../api/types';
import { useDialogFocus } from '../composables/useDialogFocus';
import type { AppProvider } from '../../api/types';
import { useDialogFocus } from '../../composables/useDialogFocus';
const { t } = useI18n();

View file

@ -1,16 +1,16 @@
<!-- apps/kimi-web/src/components/SettingsDialog.vue -->
<!-- apps/kimi-web/src/components/settings/SettingsDialog.vue -->
<!-- The app's dedicated Settings page (modal). Consolidates what used to be
scattered in the sidebar account popover: appearance, language, account,
connection, plus notifications and the troubleshooting-log export. -->
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDialogFocus } from '../composables/useDialogFocus';
import { useDialogFocus } from '../../composables/useDialogFocus';
import LanguageSwitcher from './LanguageSwitcher.vue';
import { serverEndpointLabel } from '../api/config';
import { downloadTraceLog, isTraceEnabled } from '../debug/trace';
import type { ColorScheme, Theme } from '../composables/useKimiWebClient';
import type { AppConfig, AppConfigProvider, AppModel } from '../api/types';
import { serverEndpointLabel } from '../../api/config';
import { downloadTraceLog, isTraceEnabled } from '../../debug/trace';
import type { ColorScheme, Theme } from '../../composables/useKimiWebClient';
import type { AppConfig, AppConfigProvider, AppModel } from '../../api/types';
const { t } = useI18n();