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
14 changes: 13 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ Default login: admin / admin. Guest demo: demo / demo (read-only).

**Deploy gotcha**: Vite deletes and recreates `admin/dist/` (new inode), breaking Docker bind mounts. Always `docker compose restart` after `npm run build`.

### Mobile App

```bash
cd mobile && npm install # First-time setup
cd mobile && npm run build # Production build (vue-tsc type-check + vite build)
cd mobile && npm run dev # Dev server
cd mobile && npx cap sync android # Sync web assets to Android project
cd mobile && npx cap open android # Open in Android Studio → Build APK
```

### User Management

```bash
Expand Down Expand Up @@ -115,7 +125,7 @@ Always run lint locally before pushing. Protected branches require PR workflow
```
┌──────────────────────────────────────────────────────────────┐
│ Orchestrator (port 8002) │
│ orchestrator.py + app/routers/ (21 routers, ~371 endpoints)
│ orchestrator.py + modules/*/router*.py (~28 routers)
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Vue 3 Admin Panel (24 views, PWA) │ │
│ │ admin/dist/ │ │
Expand Down Expand Up @@ -270,6 +280,8 @@ New routers import domain services directly (`from modules.monitoring.service im

**Build**: `cd mobile && npm run build && npx cap sync android`. APK via Android Studio: `npx cap open android` → Build → Build APK.

**No lint/format/test** — mobile app has only `dev`, `build`, `preview` scripts. Type checking happens during `npm run build` via `vue-tsc -b`.

## Code Patterns

**Adding a new API endpoint:**
Expand Down
28 changes: 14 additions & 14 deletions mobile/src/components/MessageBubble.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ function cancelEdit() {
<button
class="text-xs text-stone-400 hover:text-white px-2 py-1 rounded"
@click="cancelEdit"
>Cancel</button>
>Отмена</button>
<button
class="text-xs text-amber-400 hover:text-amber-300 bg-amber-600/20 px-2 py-1 rounded"
@click="saveEdit"
>Save</button>
>Сохранить</button>
</div>
</template>

Expand Down Expand Up @@ -125,7 +125,7 @@ function cancelEdit() {
<!-- TTS -->
<button
class="p-1.5 rounded text-stone-500 hover:text-stone-300 transition-colors"
:title="isSpeaking ? 'Stop' : 'Speak'"
:title="isSpeaking ? 'Стоп' : 'Озвучить'"
@click="handleSpeak"
>
<svg v-if="!isSpeaking" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -142,7 +142,7 @@ function cancelEdit() {
<button
class="p-1.5 rounded transition-colors"
:class="copied ? 'text-green-400' : 'text-stone-500 hover:text-stone-300'"
title="Copy"
title="Копировать"
@click="handleCopy"
>
<svg v-if="!copied" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -157,7 +157,7 @@ function cancelEdit() {
<!-- Edit -->
<button
class="p-1.5 rounded text-stone-500 hover:text-stone-300 transition-colors"
title="Edit"
title="Редактировать"
@click="startEdit"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -168,7 +168,7 @@ function cancelEdit() {
<!-- Save to context -->
<button
class="p-1.5 rounded text-stone-500 hover:text-stone-300 transition-colors"
title="Save to context"
title="В контекст"
@click="$emit('saveToContext', message.id, message.content)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -179,7 +179,7 @@ function cancelEdit() {
<!-- Summarize branch -->
<button
class="p-1.5 rounded text-stone-500 hover:text-stone-300 transition-colors"
title="Summarize branch"
title="Суммаризация ветки"
@click="$emit('summarizeBranch', message.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -190,7 +190,7 @@ function cancelEdit() {
<!-- Delete branch from here -->
<button
class="p-1.5 rounded text-stone-500 hover:text-red-400 transition-colors"
title="Delete branch"
title="Удалить ветку"
@click="$emit('deleteBranch', message.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -207,7 +207,7 @@ function cancelEdit() {
<!-- TTS -->
<button
class="p-1.5 rounded text-amber-300/50 hover:text-amber-200 transition-colors"
:title="isSpeaking ? 'Stop' : 'Speak'"
:title="isSpeaking ? 'Стоп' : 'Озвучить'"
@click="handleSpeak"
>
<svg v-if="!isSpeaking" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -222,7 +222,7 @@ function cancelEdit() {
<button
class="p-1.5 rounded transition-colors"
:class="copied ? 'text-green-400' : 'text-amber-300/50 hover:text-amber-200'"
title="Copy"
title="Копировать"
@click="handleCopy"
>
<svg v-if="!copied" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -237,7 +237,7 @@ function cancelEdit() {
<!-- Edit -->
<button
class="p-1.5 rounded text-amber-300/50 hover:text-amber-200 transition-colors"
title="Edit"
title="Редактировать"
@click="startEdit"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -248,7 +248,7 @@ function cancelEdit() {
<!-- Regenerate response -->
<button
class="p-1.5 rounded text-amber-300/50 hover:text-amber-200 transition-colors"
title="Regenerate response"
title="Перегенерировать"
@click="$emit('regenerate', message.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -259,7 +259,7 @@ function cancelEdit() {
<!-- Summarize branch -->
<button
class="p-1.5 rounded text-amber-300/50 hover:text-amber-200 transition-colors"
title="Summarize branch"
title="Суммаризация ветки"
@click="$emit('summarizeBranch', message.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand All @@ -270,7 +270,7 @@ function cancelEdit() {
<!-- Delete branch from here -->
<button
class="p-1.5 rounded text-amber-300/50 hover:text-red-400 transition-colors"
title="Delete branch"
title="Удалить ветку"
@click="$emit('deleteBranch', message.id)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
Expand Down
18 changes: 9 additions & 9 deletions mobile/src/views/ChatListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function loadSessions() {
await autoOpenChat();
}
} catch (e) {
error.value = e instanceof Error ? e.message : "Failed to load";
error.value = e instanceof Error ? e.message : "Не удалось загрузить";
} finally {
isLoading.value = false;
}
Expand Down Expand Up @@ -77,20 +77,20 @@ async function createNewChat() {
const data = await chatApi.createSession();
router.push(`/chat/${data.session.id}`);
} catch (e) {
error.value = e instanceof Error ? e.message : "Failed to create";
error.value = e instanceof Error ? e.message : "Не удалось создать";
} finally {
isCreating.value = false;
}
}

async function deleteSession(id: string, event: Event) {
event.stopPropagation();
if (!confirm("Delete this chat?")) return;
if (!confirm("Удалить этот чат?")) return;
try {
await chatApi.deleteSession(id);
sessions.value = sessions.value.filter((s) => s.id !== id);
} catch (e) {
error.value = e instanceof Error ? e.message : "Failed to delete";
error.value = e instanceof Error ? e.message : "Не удалось удалить";
}
}

Expand Down Expand Up @@ -135,7 +135,7 @@ async function sendFromWelcome() {
router.push(`/chat/${data.session.id}?msg=${encodeURIComponent(text)}`);
}
} catch (e) {
error.value = e instanceof Error ? e.message : "Failed to start chat";
error.value = e instanceof Error ? e.message : "Не удалось начать чат";
} finally {
isSending.value = false;
}
Expand Down Expand Up @@ -173,7 +173,7 @@ onMounted(loadSessions);
</div>
<div v-else-if="error" class="p-4 text-center">
<p class="text-red-400 text-sm">{{ error }}</p>
<button class="mt-2 text-amber-400 text-sm" @click="loadSessions">Retry</button>
<button class="mt-2 text-amber-400 text-sm" @click="loadSessions">Повторить</button>
</div>
<div v-else-if="!sessions.length" class="flex flex-col items-center justify-center h-64 text-stone-500">
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="mb-3 opacity-50">
Expand All @@ -195,11 +195,11 @@ onMounted(loadSessions);
<span class="text-xs text-stone-500 shrink-0">{{ formatDate(session.updated) }}</span>
</div>
<p class="text-xs text-stone-400 truncate">{{ truncate(session.last_message || "", 80) }}</p>
<span class="text-xs text-stone-600">{{ session.message_count }} messages</span>
<span class="text-xs text-stone-600">{{ session.message_count }} сообщ.</span>
</div>
<button
class="shrink-0 p-2 rounded-lg text-stone-600 hover:text-red-400 hover:bg-red-900/20 transition-colors"
title="Delete chat"
title="Удалить чат"
@click="deleteSession(session.id, $event)"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
Expand Down Expand Up @@ -262,7 +262,7 @@ onMounted(loadSessions);
<div v-else-if="error" class="flex-1 flex items-center justify-center text-center">
<div>
<p class="text-red-400 text-sm mb-2">{{ error }}</p>
<button class="text-amber-400 text-sm" @click="loadSessions">Retry</button>
<button class="text-amber-400 text-sm" @click="loadSessions">Повторить</button>
</div>
</div>

Expand Down
Loading
Loading