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/web-components-subdirs.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion apps/kimi-web/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 17 additions & 17 deletions apps/kimi-web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-web/src/components/FilePreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 }>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 }>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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[] }>();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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. -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -21,7 +21,7 @@ import {
turnBlocks,
turnFinalText,
turnToMarkdown,
} from './chatTurnRendering';
} from '../chatTurnRendering';

const { t } = useI18n();

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<!-- 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';
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[];
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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'] }>();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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 }>();
Expand Down
Original file line number Diff line number Diff line change
@@ -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[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<!-- 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. -->
<script setup lang="ts">
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[];
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Expand Down
Original file line number Diff line number Diff line change
@@ -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 }>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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[] }>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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).
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading
Loading