@agent-link/server 0.1.160 → 0.1.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web/app.js +263 -238
- package/web/locales/en.json +250 -0
- package/web/locales/zh.json +250 -0
- package/web/modules/appHelpers.js +4 -2
- package/web/modules/connection.js +17 -15
- package/web/modules/i18n.js +101 -0
- package/web/modules/messageHelpers.js +27 -20
- package/web/modules/sidebar.js +19 -10
package/web/app.js
CHANGED
|
@@ -23,6 +23,7 @@ import { TEMPLATES, TEMPLATE_KEYS, buildFullLeadPrompt } from './modules/teamTem
|
|
|
23
23
|
import { createLoop } from './modules/loop.js';
|
|
24
24
|
import { LOOP_TEMPLATES, LOOP_TEMPLATE_KEYS, buildCronExpression, formatSchedule } from './modules/loopTemplates.js';
|
|
25
25
|
import { createScrollManager, createHighlightScheduler, formatUsage } from './modules/appHelpers.js';
|
|
26
|
+
import { createI18n } from './modules/i18n.js';
|
|
26
27
|
|
|
27
28
|
// ── App ─────────────────────────────────────────────────────────────────────
|
|
28
29
|
const App = {
|
|
@@ -267,6 +268,25 @@ const App = {
|
|
|
267
268
|
}
|
|
268
269
|
applyTheme();
|
|
269
270
|
|
|
271
|
+
// ── i18n ──
|
|
272
|
+
const { t, locale, setLocale, toggleLocale, localeLabel } = createI18n();
|
|
273
|
+
|
|
274
|
+
// Map internal English status values to translated display strings
|
|
275
|
+
const STATUS_KEYS = {
|
|
276
|
+
'No Session': 'status.noSession',
|
|
277
|
+
'Connecting...': 'status.connecting',
|
|
278
|
+
'Connected': 'status.connected',
|
|
279
|
+
'Waiting': 'status.waiting',
|
|
280
|
+
'Reconnecting...': 'status.reconnecting',
|
|
281
|
+
'Disconnected': 'status.disconnected',
|
|
282
|
+
'Authentication Required': 'status.authRequired',
|
|
283
|
+
'Locked': 'status.locked',
|
|
284
|
+
};
|
|
285
|
+
const displayStatus = computed(() => {
|
|
286
|
+
const key = STATUS_KEYS[status.value];
|
|
287
|
+
return key ? t(key) : status.value;
|
|
288
|
+
});
|
|
289
|
+
|
|
270
290
|
// ── Scroll management ──
|
|
271
291
|
const { onScroll: onMessageListScroll, scrollToBottom, cleanup: cleanupScroll } = createScrollManager('.message-list');
|
|
272
292
|
|
|
@@ -296,8 +316,9 @@ const App = {
|
|
|
296
316
|
// Multi-session parallel
|
|
297
317
|
currentConversationId, conversationCache, processingConversations,
|
|
298
318
|
switchConversation,
|
|
319
|
+
// i18n
|
|
320
|
+
t,
|
|
299
321
|
});
|
|
300
|
-
|
|
301
322
|
const { connect, wsSend, closeWs, submitPassword, setDequeueNext, setFileBrowser, setFilePreview, setTeam, setLoop, getToolMsgMap, restoreToolMsgMap, clearToolMsgMap } = createConnection({
|
|
302
323
|
status, agentName, hostname, workDir, sessionId, error,
|
|
303
324
|
serverVersion, agentVersion, latency,
|
|
@@ -310,6 +331,8 @@ const App = {
|
|
|
310
331
|
// Multi-session parallel
|
|
311
332
|
currentConversationId, processingConversations, conversationCache,
|
|
312
333
|
switchConversation,
|
|
334
|
+
// i18n
|
|
335
|
+
t,
|
|
313
336
|
});
|
|
314
337
|
|
|
315
338
|
// Now wire up the forwarding function
|
|
@@ -534,21 +557,23 @@ const App = {
|
|
|
534
557
|
sendMessage, handleKeydown, cancelExecution, removeQueuedMessage, onMessageListScroll,
|
|
535
558
|
getRenderedContent, copyMessage, toggleTool,
|
|
536
559
|
isPrevAssistant: _isPrevAssistant,
|
|
537
|
-
toggleContextSummary, formatTimestamp, formatUsage,
|
|
538
|
-
getToolIcon, getToolSummary, isEditTool, getEditDiffHtml, getFormattedToolInput, autoResize,
|
|
560
|
+
toggleContextSummary, formatTimestamp, formatUsage: (u) => formatUsage(u, t),
|
|
561
|
+
getToolIcon, getToolSummary: (msg) => getToolSummary(msg, t), isEditTool, getEditDiffHtml: (msg) => getEditDiffHtml(msg, t), getFormattedToolInput: (msg) => getFormattedToolInput(msg, t), autoResize,
|
|
539
562
|
// AskUserQuestion
|
|
540
563
|
selectQuestionOption,
|
|
541
564
|
submitQuestionAnswer: _submitQuestionAnswer,
|
|
542
565
|
hasQuestionAnswer, getQuestionResponseSummary,
|
|
543
566
|
// Theme
|
|
544
567
|
theme, toggleTheme,
|
|
568
|
+
// i18n
|
|
569
|
+
t, locale, toggleLocale, localeLabel, displayStatus,
|
|
545
570
|
// Sidebar
|
|
546
571
|
sidebarOpen, historySessions, currentClaudeSessionId, loadingSessions, loadingHistory,
|
|
547
572
|
toggleSidebar: sidebar.toggleSidebar,
|
|
548
573
|
resumeSession: sidebar.resumeSession,
|
|
549
574
|
newConversation: sidebar.newConversation,
|
|
550
575
|
requestSessionList: sidebar.requestSessionList,
|
|
551
|
-
formatRelativeTime,
|
|
576
|
+
formatRelativeTime: (ts) => formatRelativeTime(ts, t),
|
|
552
577
|
groupedSessions: sidebar.groupedSessions,
|
|
553
578
|
isSessionProcessing: sidebar.isSessionProcessing,
|
|
554
579
|
processingConversations,
|
|
@@ -574,9 +599,9 @@ const App = {
|
|
|
574
599
|
// Team rename/delete
|
|
575
600
|
renamingTeamId, renameTeamText,
|
|
576
601
|
deleteTeamConfirmOpen, deleteTeamConfirmTitle, pendingDeleteTeamId,
|
|
577
|
-
startTeamRename(
|
|
578
|
-
renamingTeamId.value =
|
|
579
|
-
renameTeamText.value =
|
|
602
|
+
startTeamRename(tm) {
|
|
603
|
+
renamingTeamId.value = tm.teamId;
|
|
604
|
+
renameTeamText.value = tm.title || '';
|
|
580
605
|
},
|
|
581
606
|
confirmTeamRename() {
|
|
582
607
|
const tid = renamingTeamId.value;
|
|
@@ -590,9 +615,9 @@ const App = {
|
|
|
590
615
|
renamingTeamId.value = null;
|
|
591
616
|
renameTeamText.value = '';
|
|
592
617
|
},
|
|
593
|
-
requestDeleteTeam(
|
|
594
|
-
pendingDeleteTeamId.value =
|
|
595
|
-
deleteTeamConfirmTitle.value =
|
|
618
|
+
requestDeleteTeam(tm) {
|
|
619
|
+
pendingDeleteTeamId.value = tm.teamId;
|
|
620
|
+
deleteTeamConfirmTitle.value = tm.title || tm.teamId.slice(0, 8);
|
|
596
621
|
deleteTeamConfirmOpen.value = true;
|
|
597
622
|
},
|
|
598
623
|
confirmDeleteTeam() {
|
|
@@ -938,7 +963,7 @@ const App = {
|
|
|
938
963
|
loopLastRunDisplay(l) {
|
|
939
964
|
if (!l.lastExecution) return '';
|
|
940
965
|
const exec = l.lastExecution;
|
|
941
|
-
const ago = formatRelativeTime(exec.startedAt);
|
|
966
|
+
const ago = formatRelativeTime(exec.startedAt, t);
|
|
942
967
|
const icon = exec.status === 'success' ? 'OK' : exec.status === 'error' ? 'ERR' : exec.status;
|
|
943
968
|
return ago + ' ' + icon;
|
|
944
969
|
},
|
|
@@ -966,41 +991,42 @@ const App = {
|
|
|
966
991
|
<div class="layout">
|
|
967
992
|
<header class="top-bar">
|
|
968
993
|
<div class="top-bar-left">
|
|
969
|
-
<button class="sidebar-toggle" @click="toggleSidebar" title="
|
|
994
|
+
<button class="sidebar-toggle" @click="toggleSidebar" :title="t('header.toggleSidebar')">
|
|
970
995
|
<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
|
|
971
996
|
</button>
|
|
972
997
|
<h1>AgentLink</h1>
|
|
973
998
|
</div>
|
|
974
999
|
<div class="top-bar-info">
|
|
975
|
-
<span :class="['badge', status.toLowerCase()]">{{
|
|
1000
|
+
<span :class="['badge', status.toLowerCase()]">{{ displayStatus }}</span>
|
|
976
1001
|
<span v-if="latency !== null && status === 'Connected'" class="latency" :class="{ good: latency < 100, ok: latency >= 100 && latency < 500, bad: latency >= 500 }">{{ latency }}ms</span>
|
|
977
1002
|
<span v-if="agentName" class="agent-label">{{ agentName }}</span>
|
|
978
1003
|
<div class="team-mode-toggle">
|
|
979
|
-
<button :class="['team-mode-btn', { active: viewMode === 'chat' }]" @click="viewMode = 'chat'">
|
|
980
|
-
<button :class="['team-mode-btn', { active: viewMode === 'team' }]" @click="viewMode = 'team'">
|
|
981
|
-
<button :class="['team-mode-btn', { active: viewMode === 'loop' }]" @click="viewMode = 'loop'">
|
|
1004
|
+
<button :class="['team-mode-btn', { active: viewMode === 'chat' }]" @click="viewMode = 'chat'">{{ t('header.chat') }}</button>
|
|
1005
|
+
<button :class="['team-mode-btn', { active: viewMode === 'team' }]" @click="viewMode = 'team'">{{ t('header.team') }}</button>
|
|
1006
|
+
<button :class="['team-mode-btn', { active: viewMode === 'loop' }]" @click="viewMode = 'loop'">{{ t('header.loop') }}</button>
|
|
982
1007
|
</div>
|
|
983
1008
|
<select class="team-mode-select" :value="viewMode" @change="viewMode = $event.target.value">
|
|
984
|
-
<option value="chat">
|
|
985
|
-
<option value="team">
|
|
986
|
-
<option value="loop">
|
|
1009
|
+
<option value="chat">{{ t('header.chat') }}</option>
|
|
1010
|
+
<option value="team">{{ t('header.team') }}</option>
|
|
1011
|
+
<option value="loop">{{ t('header.loop') }}</option>
|
|
987
1012
|
</select>
|
|
988
|
-
<button class="theme-toggle" @click="toggleTheme" :title="theme === 'dark' ? '
|
|
1013
|
+
<button class="theme-toggle" @click="toggleTheme" :title="theme === 'dark' ? t('header.lightMode') : t('header.darkMode')">
|
|
989
1014
|
<svg v-if="theme === 'dark'" viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41l-1.06-1.06zm1.06-10.96a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/></svg>
|
|
990
1015
|
<svg v-else viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.389 5.389 0 0 1-4.4 2.26 5.403 5.403 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1z"/></svg>
|
|
991
1016
|
</button>
|
|
1017
|
+
<button class="theme-toggle" @click="toggleLocale" :title="localeLabel">{{ localeLabel }}</button>
|
|
992
1018
|
</div>
|
|
993
1019
|
</header>
|
|
994
1020
|
|
|
995
1021
|
<div v-if="status === 'No Session' || (status !== 'Connected' && status !== 'Connecting...' && status !== 'Reconnecting...' && messages.length === 0)" class="center-card">
|
|
996
1022
|
<div class="status-card">
|
|
997
1023
|
<p class="status">
|
|
998
|
-
<span class="label">
|
|
999
|
-
<span :class="['badge', status.toLowerCase()]">{{
|
|
1024
|
+
<span class="label">{{ t('statusCard.status') }}</span>
|
|
1025
|
+
<span :class="['badge', status.toLowerCase()]">{{ displayStatus }}</span>
|
|
1000
1026
|
</p>
|
|
1001
|
-
<p v-if="agentName" class="info"><span class="label">
|
|
1002
|
-
<p v-if="workDir" class="info"><span class="label">
|
|
1003
|
-
<p v-if="sessionId" class="info muted"><span class="label">
|
|
1027
|
+
<p v-if="agentName" class="info"><span class="label">{{ t('statusCard.agent') }}</span> {{ agentName }}</p>
|
|
1028
|
+
<p v-if="workDir" class="info"><span class="label">{{ t('statusCard.directory') }}</span> {{ workDir }}</p>
|
|
1029
|
+
<p v-if="sessionId" class="info muted"><span class="label">{{ t('statusCard.session') }}</span> {{ sessionId }}</p>
|
|
1004
1030
|
<p v-if="error" class="error-msg">{{ error }}</p>
|
|
1005
1031
|
</div>
|
|
1006
1032
|
</div>
|
|
@@ -1015,16 +1041,16 @@ const App = {
|
|
|
1015
1041
|
<div class="file-panel-mobile-header">
|
|
1016
1042
|
<button class="file-panel-mobile-back" @click="sidebarView = 'sessions'">
|
|
1017
1043
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
1018
|
-
|
|
1044
|
+
{{ t('sidebar.sessions') }}
|
|
1019
1045
|
</button>
|
|
1020
|
-
<button class="file-panel-btn" @click="fileBrowser.refreshTree()" title="
|
|
1046
|
+
<button class="file-panel-btn" @click="fileBrowser.refreshTree()" :title="t('sidebar.refresh')">
|
|
1021
1047
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
1022
1048
|
</button>
|
|
1023
1049
|
</div>
|
|
1024
1050
|
<div class="file-panel-breadcrumb" :title="workDir">{{ workDir }}</div>
|
|
1025
|
-
<div v-if="fileTreeLoading" class="file-panel-loading">
|
|
1051
|
+
<div v-if="fileTreeLoading" class="file-panel-loading">{{ t('filePanel.loading') }}</div>
|
|
1026
1052
|
<div v-else-if="!fileTreeRoot || !fileTreeRoot.children || fileTreeRoot.children.length === 0" class="file-panel-empty">
|
|
1027
|
-
|
|
1053
|
+
{{ t('filePanel.noFiles') }}
|
|
1028
1054
|
</div>
|
|
1029
1055
|
<div v-else class="file-tree">
|
|
1030
1056
|
<template v-for="item in flattenedTree" :key="item.node.path">
|
|
@@ -1042,7 +1068,7 @@ const App = {
|
|
|
1042
1068
|
<span class="file-tree-name" :title="item.node.path">{{ item.node.name }}</span>
|
|
1043
1069
|
<span v-if="item.node.loading" class="file-tree-spinner"></span>
|
|
1044
1070
|
</div>
|
|
1045
|
-
<div v-if="item.node.type === 'directory' && item.node.expanded && item.node.children && item.node.children.length === 0 && !item.node.loading" class="file-tree-empty" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">(empty)</div>
|
|
1071
|
+
<div v-if="item.node.type === 'directory' && item.node.expanded && item.node.children && item.node.children.length === 0 && !item.node.loading" class="file-tree-empty" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">{{ t('filePanel.empty') }}</div>
|
|
1046
1072
|
<div v-if="item.node.error" class="file-tree-error" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">{{ item.node.error }}</div>
|
|
1047
1073
|
</template>
|
|
1048
1074
|
</div>
|
|
@@ -1053,13 +1079,13 @@ const App = {
|
|
|
1053
1079
|
<div class="file-preview-mobile-header">
|
|
1054
1080
|
<button class="file-panel-mobile-back" @click="filePreview.closePreview()">
|
|
1055
1081
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
1056
|
-
|
|
1082
|
+
{{ t('sidebar.files') }}
|
|
1057
1083
|
</button>
|
|
1058
1084
|
<div class="preview-header-actions">
|
|
1059
1085
|
<button v-if="previewFile?.content && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
1060
1086
|
class="preview-md-toggle" :class="{ active: previewMarkdownRendered }"
|
|
1061
1087
|
@click="previewMarkdownRendered = !previewMarkdownRendered"
|
|
1062
|
-
:title="previewMarkdownRendered ? '
|
|
1088
|
+
:title="previewMarkdownRendered ? t('preview.showSource') : t('preview.renderMarkdown')">
|
|
1063
1089
|
<svg viewBox="0 0 16 16" width="14" height="14"><path fill="currentColor" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"/></svg>
|
|
1064
1090
|
</button>
|
|
1065
1091
|
<span v-if="previewFile" class="file-preview-mobile-size">
|
|
@@ -1068,10 +1094,10 @@ const App = {
|
|
|
1068
1094
|
</div>
|
|
1069
1095
|
</div>
|
|
1070
1096
|
<div class="file-preview-mobile-filename" :title="previewFile?.filePath">
|
|
1071
|
-
{{ previewFile?.fileName || '
|
|
1097
|
+
{{ previewFile?.fileName || t('preview.preview') }}
|
|
1072
1098
|
</div>
|
|
1073
1099
|
<div class="preview-panel-body">
|
|
1074
|
-
<div v-if="previewLoading" class="preview-loading">
|
|
1100
|
+
<div v-if="previewLoading" class="preview-loading">{{ t('preview.loading') }}</div>
|
|
1075
1101
|
<div v-else-if="previewFile?.error" class="preview-error">
|
|
1076
1102
|
{{ previewFile.error }}
|
|
1077
1103
|
</div>
|
|
@@ -1086,11 +1112,11 @@ const App = {
|
|
|
1086
1112
|
<div v-else-if="previewFile?.content" class="preview-text-container">
|
|
1087
1113
|
<pre class="preview-code"><code v-html="filePreview.highlightCode(previewFile.content, previewFile.fileName)"></code></pre>
|
|
1088
1114
|
<div v-if="previewFile.truncated" class="preview-truncated-notice">
|
|
1089
|
-
|
|
1115
|
+
{{ t('preview.fileTruncated', { size: filePreview.formatFileSize(previewFile.totalSize) }) }}
|
|
1090
1116
|
</div>
|
|
1091
1117
|
</div>
|
|
1092
1118
|
<div v-else-if="previewFile && !previewFile.content && !previewFile.error" class="preview-binary-info">
|
|
1093
|
-
<p>
|
|
1119
|
+
<p>{{ t('preview.binaryFile') }} — {{ previewFile.mimeType }}</p>
|
|
1094
1120
|
<p>{{ filePreview.formatFileSize(previewFile.totalSize) }}</p>
|
|
1095
1121
|
</div>
|
|
1096
1122
|
</div>
|
|
@@ -1105,7 +1131,7 @@ const App = {
|
|
|
1105
1131
|
<span>{{ hostname }}</span>
|
|
1106
1132
|
</div>
|
|
1107
1133
|
<div class="sidebar-workdir-header">
|
|
1108
|
-
<div class="sidebar-workdir-label">
|
|
1134
|
+
<div class="sidebar-workdir-label">{{ t('sidebar.workingDirectory') }}</div>
|
|
1109
1135
|
</div>
|
|
1110
1136
|
<div class="sidebar-workdir-path-row" @click.stop="toggleWorkdirMenu()">
|
|
1111
1137
|
<div class="sidebar-workdir-path" :title="workDir">{{ workDir }}</div>
|
|
@@ -1114,19 +1140,19 @@ const App = {
|
|
|
1114
1140
|
<div v-if="workdirMenuOpen" class="workdir-menu">
|
|
1115
1141
|
<div class="workdir-menu-item" @click.stop="workdirMenuBrowse()">
|
|
1116
1142
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10zM8 13h8v2H8v-2z"/></svg>
|
|
1117
|
-
<span>
|
|
1143
|
+
<span>{{ t('sidebar.browseFiles') }}</span>
|
|
1118
1144
|
</div>
|
|
1119
1145
|
<div class="workdir-menu-item" @click.stop="workdirMenuChangeDir()">
|
|
1120
1146
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>
|
|
1121
|
-
<span>
|
|
1147
|
+
<span>{{ t('sidebar.changeDirectory') }}</span>
|
|
1122
1148
|
</div>
|
|
1123
1149
|
<div class="workdir-menu-item" @click.stop="workdirMenuCopyPath()">
|
|
1124
1150
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
|
|
1125
|
-
<span>
|
|
1151
|
+
<span>{{ t('sidebar.copyPath') }}</span>
|
|
1126
1152
|
</div>
|
|
1127
1153
|
</div>
|
|
1128
1154
|
<div v-if="filteredWorkdirHistory.length > 0" class="workdir-history">
|
|
1129
|
-
<div class="workdir-history-label">
|
|
1155
|
+
<div class="workdir-history-label">{{ t('sidebar.recentDirectories') }}</div>
|
|
1130
1156
|
<div class="workdir-history-list">
|
|
1131
1157
|
<div
|
|
1132
1158
|
v-for="path in filteredWorkdirHistory" :key="path"
|
|
@@ -1135,7 +1161,7 @@ const App = {
|
|
|
1135
1161
|
:title="path"
|
|
1136
1162
|
>
|
|
1137
1163
|
<span class="workdir-history-path">{{ path }}</span>
|
|
1138
|
-
<button class="workdir-history-delete" @click.stop="removeFromWorkdirHistory(path)" title="
|
|
1164
|
+
<button class="workdir-history-delete" @click.stop="removeFromWorkdirHistory(path)" :title="t('sidebar.removeFromHistory')">
|
|
1139
1165
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
|
1140
1166
|
</button>
|
|
1141
1167
|
</div>
|
|
@@ -1147,12 +1173,12 @@ const App = {
|
|
|
1147
1173
|
<!-- Chat History section -->
|
|
1148
1174
|
<div class="sidebar-section sidebar-sessions" :style="{ flex: chatsCollapsed ? '0 0 auto' : '1 1 0', minHeight: chatsCollapsed ? 'auto' : '0' }">
|
|
1149
1175
|
<div class="sidebar-section-header" @click="chatsCollapsed = !chatsCollapsed" style="cursor: pointer;">
|
|
1150
|
-
<span>
|
|
1176
|
+
<span>{{ t('sidebar.chatHistory') }}</span>
|
|
1151
1177
|
<span class="sidebar-section-header-actions">
|
|
1152
|
-
<button class="sidebar-refresh-btn" @click.stop="requestSessionList" title="
|
|
1178
|
+
<button class="sidebar-refresh-btn" @click.stop="requestSessionList" :title="t('sidebar.refresh')" :disabled="loadingSessions">
|
|
1153
1179
|
<svg :class="{ spinning: loadingSessions }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
1154
1180
|
</button>
|
|
1155
|
-
<button class="sidebar-collapse-btn" :title="chatsCollapsed ? '
|
|
1181
|
+
<button class="sidebar-collapse-btn" :title="chatsCollapsed ? t('sidebar.expand') : t('sidebar.collapse')">
|
|
1156
1182
|
<svg :class="{ collapsed: chatsCollapsed }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>
|
|
1157
1183
|
</button>
|
|
1158
1184
|
</span>
|
|
@@ -1161,14 +1187,14 @@ const App = {
|
|
|
1161
1187
|
<div v-show="!chatsCollapsed" class="sidebar-section-collapsible">
|
|
1162
1188
|
<button class="new-conversation-btn" @click="newConversation">
|
|
1163
1189
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
1164
|
-
|
|
1190
|
+
{{ t('sidebar.newConversation') }}
|
|
1165
1191
|
</button>
|
|
1166
1192
|
|
|
1167
1193
|
<div v-if="loadingSessions && historySessions.length === 0" class="sidebar-loading">
|
|
1168
|
-
|
|
1194
|
+
{{ t('sidebar.loadingSessions') }}
|
|
1169
1195
|
</div>
|
|
1170
1196
|
<div v-else-if="historySessions.length === 0" class="sidebar-empty">
|
|
1171
|
-
|
|
1197
|
+
{{ t('sidebar.noSessions') }}
|
|
1172
1198
|
</div>
|
|
1173
1199
|
<div v-else class="session-list">
|
|
1174
1200
|
<div v-for="group in groupedSessions" :key="group.label" class="session-group">
|
|
@@ -1189,8 +1215,8 @@ const App = {
|
|
|
1189
1215
|
@keydown.escape.stop="cancelRename"
|
|
1190
1216
|
@vue:mounted="$event.el.focus()"
|
|
1191
1217
|
/>
|
|
1192
|
-
<button class="session-rename-ok" @click.stop="confirmRename" title="
|
|
1193
|
-
<button class="session-rename-cancel" @click.stop="cancelRename" title="
|
|
1218
|
+
<button class="session-rename-ok" @click.stop="confirmRename" :title="t('sidebar.confirm')">✓</button>
|
|
1219
|
+
<button class="session-rename-cancel" @click.stop="cancelRename" :title="t('sidebar.cancel')">×</button>
|
|
1194
1220
|
</div>
|
|
1195
1221
|
<div v-else class="session-title">
|
|
1196
1222
|
<svg v-if="s.title && s.title.startsWith('You are a team lead')" class="session-team-icon" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
|
|
@@ -1202,7 +1228,7 @@ const App = {
|
|
|
1202
1228
|
<button
|
|
1203
1229
|
class="session-rename-btn"
|
|
1204
1230
|
@click.stop="startRename(s)"
|
|
1205
|
-
title="
|
|
1231
|
+
:title="t('sidebar.renameSession')"
|
|
1206
1232
|
>
|
|
1207
1233
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
|
1208
1234
|
</button>
|
|
@@ -1210,7 +1236,7 @@ const App = {
|
|
|
1210
1236
|
v-if="currentClaudeSessionId !== s.sessionId"
|
|
1211
1237
|
class="session-delete-btn"
|
|
1212
1238
|
@click.stop="deleteSession(s)"
|
|
1213
|
-
title="
|
|
1239
|
+
:title="t('sidebar.deleteSession')"
|
|
1214
1240
|
>
|
|
1215
1241
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
|
1216
1242
|
</button>
|
|
@@ -1225,12 +1251,12 @@ const App = {
|
|
|
1225
1251
|
<!-- Teams section -->
|
|
1226
1252
|
<div class="sidebar-section sidebar-teams" :style="{ flex: teamsCollapsed ? '0 0 auto' : '1 1 0', minHeight: teamsCollapsed ? 'auto' : '0' }">
|
|
1227
1253
|
<div class="sidebar-section-header" @click="teamsCollapsed = !teamsCollapsed" style="cursor: pointer;">
|
|
1228
|
-
<span>
|
|
1254
|
+
<span>{{ t('sidebar.teamsHistory') }}</span>
|
|
1229
1255
|
<span class="sidebar-section-header-actions">
|
|
1230
|
-
<button class="sidebar-refresh-btn" @click.stop="requestTeamsList" title="
|
|
1256
|
+
<button class="sidebar-refresh-btn" @click.stop="requestTeamsList" :title="t('sidebar.refresh')" :disabled="loadingTeams">
|
|
1231
1257
|
<svg :class="{ spinning: loadingTeams }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
1232
1258
|
</button>
|
|
1233
|
-
<button class="sidebar-collapse-btn" :title="teamsCollapsed ? '
|
|
1259
|
+
<button class="sidebar-collapse-btn" :title="teamsCollapsed ? t('sidebar.expand') : t('sidebar.collapse')">
|
|
1234
1260
|
<svg :class="{ collapsed: teamsCollapsed }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>
|
|
1235
1261
|
</button>
|
|
1236
1262
|
</span>
|
|
@@ -1239,18 +1265,18 @@ const App = {
|
|
|
1239
1265
|
<div v-show="!teamsCollapsed" class="sidebar-section-collapsible">
|
|
1240
1266
|
<button class="new-conversation-btn" @click="newTeam" :disabled="isTeamActive">
|
|
1241
1267
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
1242
|
-
|
|
1268
|
+
{{ t('sidebar.newTeam') }}
|
|
1243
1269
|
</button>
|
|
1244
1270
|
|
|
1245
1271
|
<div class="team-history-list">
|
|
1246
1272
|
<div
|
|
1247
|
-
v-for="
|
|
1248
|
-
:class="['team-history-item', { active: displayTeam && displayTeam.teamId ===
|
|
1249
|
-
@click="renamingTeamId !==
|
|
1250
|
-
:title="
|
|
1273
|
+
v-for="tm in teamsList" :key="tm.teamId"
|
|
1274
|
+
:class="['team-history-item', { active: displayTeam && displayTeam.teamId === tm.teamId }]"
|
|
1275
|
+
@click="renamingTeamId !== tm.teamId && viewHistoricalTeam(tm.teamId)"
|
|
1276
|
+
:title="tm.title"
|
|
1251
1277
|
>
|
|
1252
1278
|
<div class="team-history-info">
|
|
1253
|
-
<div v-if="renamingTeamId ===
|
|
1279
|
+
<div v-if="renamingTeamId === tm.teamId" class="session-rename-row">
|
|
1254
1280
|
<input
|
|
1255
1281
|
class="session-rename-input"
|
|
1256
1282
|
v-model="renameTeamText"
|
|
@@ -1259,19 +1285,19 @@ const App = {
|
|
|
1259
1285
|
@keydown.escape.stop="cancelTeamRename"
|
|
1260
1286
|
@vue:mounted="$event.el.focus()"
|
|
1261
1287
|
/>
|
|
1262
|
-
<button class="session-rename-ok" @click.stop="confirmTeamRename" title="
|
|
1263
|
-
<button class="session-rename-cancel" @click.stop="cancelTeamRename" title="
|
|
1288
|
+
<button class="session-rename-ok" @click.stop="confirmTeamRename" :title="t('sidebar.confirm')">✓</button>
|
|
1289
|
+
<button class="session-rename-cancel" @click.stop="cancelTeamRename" :title="t('sidebar.cancel')">×</button>
|
|
1264
1290
|
</div>
|
|
1265
|
-
<div v-else class="team-history-title">{{
|
|
1266
|
-
<div v-if="renamingTeamId !==
|
|
1267
|
-
<span :class="['team-status-badge', 'team-status-badge-sm', 'team-status-' +
|
|
1268
|
-
<span v-if="
|
|
1269
|
-
<span v-if="
|
|
1291
|
+
<div v-else class="team-history-title">{{ tm.title || t('sidebar.untitledTeam') }}</div>
|
|
1292
|
+
<div v-if="renamingTeamId !== tm.teamId" class="team-history-meta">
|
|
1293
|
+
<span :class="['team-status-badge', 'team-status-badge-sm', 'team-status-' + tm.status]">{{ tm.status }}</span>
|
|
1294
|
+
<span v-if="tm.taskCount" class="team-history-tasks">{{ tm.taskCount }} {{ t('sidebar.tasks') }}</span>
|
|
1295
|
+
<span v-if="tm.totalCost" class="team-history-tasks">{{'$' + tm.totalCost.toFixed(2) }}</span>
|
|
1270
1296
|
<span class="session-actions">
|
|
1271
|
-
<button class="session-rename-btn" @click.stop="startTeamRename(
|
|
1297
|
+
<button class="session-rename-btn" @click.stop="startTeamRename(tm)" :title="t('sidebar.renameTeam')">
|
|
1272
1298
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
|
1273
1299
|
</button>
|
|
1274
|
-
<button class="session-delete-btn" @click.stop="requestDeleteTeam(
|
|
1300
|
+
<button class="session-delete-btn" @click.stop="requestDeleteTeam(tm)" :title="t('sidebar.deleteTeam')">
|
|
1275
1301
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
|
1276
1302
|
</button>
|
|
1277
1303
|
</span>
|
|
@@ -1285,12 +1311,12 @@ const App = {
|
|
|
1285
1311
|
<!-- Loops section -->
|
|
1286
1312
|
<div class="sidebar-section sidebar-loops" :style="{ flex: loopsCollapsed ? '0 0 auto' : '1 1 0', minHeight: loopsCollapsed ? 'auto' : '0' }">
|
|
1287
1313
|
<div class="sidebar-section-header" @click="loopsCollapsed = !loopsCollapsed" style="cursor: pointer;">
|
|
1288
|
-
<span>
|
|
1314
|
+
<span>{{ t('sidebar.loops') }}</span>
|
|
1289
1315
|
<span class="sidebar-section-header-actions">
|
|
1290
|
-
<button class="sidebar-refresh-btn" @click.stop="requestLoopsList" title="
|
|
1316
|
+
<button class="sidebar-refresh-btn" @click.stop="requestLoopsList" :title="t('sidebar.refresh')" :disabled="loadingLoops">
|
|
1291
1317
|
<svg :class="{ spinning: loadingLoops }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
1292
1318
|
</button>
|
|
1293
|
-
<button class="sidebar-collapse-btn" :title="loopsCollapsed ? '
|
|
1319
|
+
<button class="sidebar-collapse-btn" :title="loopsCollapsed ? t('sidebar.expand') : t('sidebar.collapse')">
|
|
1294
1320
|
<svg :class="{ collapsed: loopsCollapsed }" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>
|
|
1295
1321
|
</button>
|
|
1296
1322
|
</span>
|
|
@@ -1299,11 +1325,11 @@ const App = {
|
|
|
1299
1325
|
<div v-show="!loopsCollapsed" class="sidebar-section-collapsible">
|
|
1300
1326
|
<button class="new-conversation-btn" @click="newLoop">
|
|
1301
1327
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
1302
|
-
|
|
1328
|
+
{{ t('sidebar.newLoop') }}
|
|
1303
1329
|
</button>
|
|
1304
1330
|
|
|
1305
1331
|
<div v-if="loopsList.length === 0 && !loadingLoops" class="sidebar-empty">
|
|
1306
|
-
|
|
1332
|
+
{{ t('sidebar.noLoops') }}
|
|
1307
1333
|
</div>
|
|
1308
1334
|
<div v-else class="loop-history-list">
|
|
1309
1335
|
<div
|
|
@@ -1322,18 +1348,18 @@ const App = {
|
|
|
1322
1348
|
@keydown.escape.stop="cancelLoopRename"
|
|
1323
1349
|
@vue:mounted="$event.el.focus()"
|
|
1324
1350
|
/>
|
|
1325
|
-
<button class="session-rename-ok" @click.stop="confirmLoopRename" title="
|
|
1326
|
-
<button class="session-rename-cancel" @click.stop="cancelLoopRename" title="
|
|
1351
|
+
<button class="session-rename-ok" @click.stop="confirmLoopRename" :title="t('sidebar.confirm')">✓</button>
|
|
1352
|
+
<button class="session-rename-cancel" @click.stop="cancelLoopRename" :title="t('sidebar.cancel')">×</button>
|
|
1327
1353
|
</div>
|
|
1328
|
-
<div v-else class="team-history-title">{{ l.name || '
|
|
1354
|
+
<div v-else class="team-history-title">{{ l.name || t('sidebar.untitledLoop') }}</div>
|
|
1329
1355
|
<div v-if="renamingLoopId !== l.id" class="team-history-meta">
|
|
1330
|
-
<span :class="['team-status-badge', 'team-status-badge-sm', l.enabled ? 'team-status-running' : 'team-status-completed']">{{ l.enabled ? 'active' : 'paused' }}</span>
|
|
1356
|
+
<span :class="['team-status-badge', 'team-status-badge-sm', l.enabled ? 'team-status-running' : 'team-status-completed']">{{ l.enabled ? t('sidebar.active') : t('sidebar.paused') }}</span>
|
|
1331
1357
|
<span v-if="l.scheduleType" class="team-history-tasks">{{ formatSchedule(l.scheduleType, l.scheduleConfig || {}, l.schedule) }}</span>
|
|
1332
1358
|
<span class="session-actions">
|
|
1333
|
-
<button class="session-rename-btn" @click.stop="startLoopRename(l)" title="
|
|
1359
|
+
<button class="session-rename-btn" @click.stop="startLoopRename(l)" :title="t('sidebar.renameLoop')">
|
|
1334
1360
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
|
1335
1361
|
</button>
|
|
1336
|
-
<button class="session-delete-btn" @click.stop="requestDeleteLoop(l)" title="
|
|
1362
|
+
<button class="session-delete-btn" @click.stop="requestDeleteLoop(l)" :title="t('sidebar.deleteLoop')">
|
|
1337
1363
|
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
|
1338
1364
|
</button>
|
|
1339
1365
|
</span>
|
|
@@ -1345,9 +1371,9 @@ const App = {
|
|
|
1345
1371
|
</div>
|
|
1346
1372
|
|
|
1347
1373
|
<div v-if="serverVersion || agentVersion" class="sidebar-version-footer">
|
|
1348
|
-
<span v-if="serverVersion">server {{ serverVersion }}</span>
|
|
1374
|
+
<span v-if="serverVersion">{{ t('sidebar.server') }} {{ serverVersion }}</span>
|
|
1349
1375
|
<span v-if="serverVersion && agentVersion" class="sidebar-version-sep">/</span>
|
|
1350
|
-
<span v-if="agentVersion">agent {{ agentVersion }}</span>
|
|
1376
|
+
<span v-if="agentVersion">{{ t('sidebar.agent') }} {{ agentVersion }}</span>
|
|
1351
1377
|
</div>
|
|
1352
1378
|
</template>
|
|
1353
1379
|
</aside>
|
|
@@ -1357,20 +1383,20 @@ const App = {
|
|
|
1357
1383
|
<div v-if="filePanelOpen && !isMobile" class="file-panel" :style="{ width: filePanelWidth + 'px' }">
|
|
1358
1384
|
<div class="file-panel-resize-handle" @mousedown="fileBrowser.onResizeStart($event)" @touchstart="fileBrowser.onResizeStart($event)"></div>
|
|
1359
1385
|
<div class="file-panel-header">
|
|
1360
|
-
<span class="file-panel-title">
|
|
1386
|
+
<span class="file-panel-title">{{ t('filePanel.files') }}</span>
|
|
1361
1387
|
<div class="file-panel-actions">
|
|
1362
|
-
<button class="file-panel-btn" @click="fileBrowser.refreshTree()" title="
|
|
1388
|
+
<button class="file-panel-btn" @click="fileBrowser.refreshTree()" :title="t('sidebar.refresh')">
|
|
1363
1389
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
|
1364
1390
|
</button>
|
|
1365
|
-
<button class="file-panel-btn" @click="filePanelOpen = false" title="
|
|
1391
|
+
<button class="file-panel-btn" @click="filePanelOpen = false" :title="t('sidebar.close')">
|
|
1366
1392
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
|
1367
1393
|
</button>
|
|
1368
1394
|
</div>
|
|
1369
1395
|
</div>
|
|
1370
1396
|
<div class="file-panel-breadcrumb" :title="workDir">{{ workDir }}</div>
|
|
1371
|
-
<div v-if="fileTreeLoading" class="file-panel-loading">
|
|
1397
|
+
<div v-if="fileTreeLoading" class="file-panel-loading">{{ t('filePanel.loading') }}</div>
|
|
1372
1398
|
<div v-else-if="!fileTreeRoot || !fileTreeRoot.children || fileTreeRoot.children.length === 0" class="file-panel-empty">
|
|
1373
|
-
|
|
1399
|
+
{{ t('filePanel.noFiles') }}
|
|
1374
1400
|
</div>
|
|
1375
1401
|
<div v-else class="file-tree">
|
|
1376
1402
|
<template v-for="item in flattenedTree" :key="item.node.path">
|
|
@@ -1388,7 +1414,7 @@ const App = {
|
|
|
1388
1414
|
<span class="file-tree-name" :title="item.node.path">{{ item.node.name }}</span>
|
|
1389
1415
|
<span v-if="item.node.loading" class="file-tree-spinner"></span>
|
|
1390
1416
|
</div>
|
|
1391
|
-
<div v-if="item.node.type === 'directory' && item.node.expanded && item.node.children && item.node.children.length === 0 && !item.node.loading" class="file-tree-empty" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">(empty)</div>
|
|
1417
|
+
<div v-if="item.node.type === 'directory' && item.node.expanded && item.node.children && item.node.children.length === 0 && !item.node.loading" class="file-tree-empty" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">{{ t('filePanel.empty') }}</div>
|
|
1392
1418
|
<div v-if="item.node.error" class="file-tree-error" :style="{ paddingLeft: ((item.depth + 1) * 16 + 8) + 'px' }">{{ item.node.error }}</div>
|
|
1393
1419
|
</template>
|
|
1394
1420
|
</div>
|
|
@@ -1406,13 +1432,13 @@ const App = {
|
|
|
1406
1432
|
<div class="team-create-inner">
|
|
1407
1433
|
<div class="team-create-header">
|
|
1408
1434
|
<svg viewBox="0 0 24 24" width="28" height="28"><path fill="currentColor" opacity="0.5" d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
|
|
1409
|
-
<h2>
|
|
1435
|
+
<h2>{{ t('team.launchAgentTeam') }}</h2>
|
|
1410
1436
|
</div>
|
|
1411
|
-
<p class="team-create-desc">
|
|
1437
|
+
<p class="team-create-desc">{{ t('team.selectTemplateDesc') }}</p>
|
|
1412
1438
|
|
|
1413
1439
|
<!-- Template selector -->
|
|
1414
1440
|
<div class="team-tpl-section">
|
|
1415
|
-
<label class="team-tpl-label">
|
|
1441
|
+
<label class="team-tpl-label">{{ t('team.template') }}</label>
|
|
1416
1442
|
<select class="team-tpl-select" :value="selectedTemplate" @change="onTemplateChange($event.target.value)">
|
|
1417
1443
|
<option v-for="key in TEMPLATE_KEYS" :key="key" :value="key">{{ TEMPLATES[key].label }}</option>
|
|
1418
1444
|
</select>
|
|
@@ -1423,7 +1449,7 @@ const App = {
|
|
|
1423
1449
|
<div class="team-lead-prompt-section">
|
|
1424
1450
|
<div class="team-lead-prompt-header" @click="leadPromptExpanded = !leadPromptExpanded">
|
|
1425
1451
|
<svg class="team-lead-prompt-arrow" :class="{ expanded: leadPromptExpanded }" viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>
|
|
1426
|
-
<span class="team-lead-prompt-title">
|
|
1452
|
+
<span class="team-lead-prompt-title">{{ t('team.leadPrompt') }}</span>
|
|
1427
1453
|
<span v-if="!leadPromptExpanded" class="team-lead-prompt-preview">{{ leadPromptPreview() }}</span>
|
|
1428
1454
|
</div>
|
|
1429
1455
|
<div v-if="leadPromptExpanded" class="team-lead-prompt-body">
|
|
@@ -1433,18 +1459,18 @@ const App = {
|
|
|
1433
1459
|
rows="10"
|
|
1434
1460
|
></textarea>
|
|
1435
1461
|
<div class="team-lead-prompt-actions">
|
|
1436
|
-
<button class="team-lead-prompt-reset" @click="resetLeadPrompt()" title="
|
|
1462
|
+
<button class="team-lead-prompt-reset" @click="resetLeadPrompt()" :title="t('team.reset')">{{ t('team.reset') }}</button>
|
|
1437
1463
|
</div>
|
|
1438
1464
|
</div>
|
|
1439
1465
|
</div>
|
|
1440
1466
|
|
|
1441
1467
|
<!-- Task description -->
|
|
1442
1468
|
<div class="team-tpl-section">
|
|
1443
|
-
<label class="team-tpl-label">
|
|
1469
|
+
<label class="team-tpl-label">{{ t('team.taskDescription') }}</label>
|
|
1444
1470
|
<textarea
|
|
1445
1471
|
v-model="teamInstruction"
|
|
1446
1472
|
class="team-create-textarea"
|
|
1447
|
-
placeholder="
|
|
1473
|
+
:placeholder="t('team.taskPlaceholder')"
|
|
1448
1474
|
rows="4"
|
|
1449
1475
|
></textarea>
|
|
1450
1476
|
</div>
|
|
@@ -1452,14 +1478,14 @@ const App = {
|
|
|
1452
1478
|
<div class="team-create-actions">
|
|
1453
1479
|
<button class="team-create-launch" :disabled="!teamInstruction.trim()" @click="launchTeamFromPanel()">
|
|
1454
1480
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
|
1455
|
-
|
|
1481
|
+
{{ t('team.launchTeam') }}
|
|
1456
1482
|
</button>
|
|
1457
|
-
<button class="team-create-cancel" @click="backToChat()">
|
|
1483
|
+
<button class="team-create-cancel" @click="backToChat()">{{ t('team.backToChat') }}</button>
|
|
1458
1484
|
</div>
|
|
1459
1485
|
|
|
1460
1486
|
<!-- Example instructions -->
|
|
1461
1487
|
<div class="team-examples-section">
|
|
1462
|
-
<div class="team-examples-header">
|
|
1488
|
+
<div class="team-examples-header">{{ t('team.examples') }}</div>
|
|
1463
1489
|
<div class="team-examples-list">
|
|
1464
1490
|
<div class="team-example-card" v-for="(ex, i) in teamExamples" :key="i">
|
|
1465
1491
|
<div class="team-example-icon" v-html="ex.icon"></div>
|
|
@@ -1467,27 +1493,27 @@ const App = {
|
|
|
1467
1493
|
<div class="team-example-title">{{ ex.title }}</div>
|
|
1468
1494
|
<div class="team-example-text">{{ ex.text }}</div>
|
|
1469
1495
|
</div>
|
|
1470
|
-
<button class="team-example-try" @click="onTemplateChange(ex.template); teamInstruction = ex.text">
|
|
1496
|
+
<button class="team-example-try" @click="onTemplateChange(ex.template); teamInstruction = ex.text">{{ t('team.tryIt') }}</button>
|
|
1471
1497
|
</div>
|
|
1472
1498
|
</div>
|
|
1473
1499
|
</div>
|
|
1474
1500
|
|
|
1475
1501
|
<!-- Historical teams -->
|
|
1476
1502
|
<div v-if="teamsList.length > 0" class="team-history-section">
|
|
1477
|
-
<div class="team-history-section-header">
|
|
1503
|
+
<div class="team-history-section-header">{{ t('team.previousTeams') }}</div>
|
|
1478
1504
|
<div class="team-history-list">
|
|
1479
1505
|
<div
|
|
1480
|
-
v-for="
|
|
1506
|
+
v-for="tm in teamsList" :key="tm.teamId"
|
|
1481
1507
|
class="team-history-item"
|
|
1482
|
-
@click="viewHistoricalTeam(
|
|
1483
|
-
:title="
|
|
1508
|
+
@click="viewHistoricalTeam(tm.teamId)"
|
|
1509
|
+
:title="tm.title"
|
|
1484
1510
|
>
|
|
1485
1511
|
<div class="team-history-info">
|
|
1486
|
-
<div class="team-history-title">{{
|
|
1512
|
+
<div class="team-history-title">{{ tm.title || t('sidebar.untitledTeam') }}</div>
|
|
1487
1513
|
<div class="team-history-meta">
|
|
1488
|
-
<span :class="['team-status-badge', 'team-status-badge-sm', 'team-status-' +
|
|
1489
|
-
<span v-if="
|
|
1490
|
-
<span v-if="
|
|
1514
|
+
<span :class="['team-status-badge', 'team-status-badge-sm', 'team-status-' + tm.status]">{{ tm.status }}</span>
|
|
1515
|
+
<span v-if="tm.taskCount" class="team-history-tasks">{{ tm.taskCount }} {{ t('sidebar.tasks') }}</span>
|
|
1516
|
+
<span v-if="tm.totalCost" class="team-history-tasks">{{'$' + tm.totalCost.toFixed(2) }}</span>
|
|
1491
1517
|
</div>
|
|
1492
1518
|
</div>
|
|
1493
1519
|
</div>
|
|
@@ -1503,15 +1529,15 @@ const App = {
|
|
|
1503
1529
|
<div class="team-dash-header-top">
|
|
1504
1530
|
<span :class="['team-status-badge', 'team-status-' + displayTeam.status]">{{ displayTeam.status }}</span>
|
|
1505
1531
|
<div class="team-dash-header-right">
|
|
1506
|
-
<button v-if="isTeamRunning" class="team-dissolve-btn" @click="dissolveTeam()">
|
|
1507
|
-
<button v-if="!isTeamActive" class="team-new-btn" @click="newTeam()">
|
|
1508
|
-
<button v-if="!isTeamActive" class="team-back-btn" @click="backToChat()">
|
|
1532
|
+
<button v-if="isTeamRunning" class="team-dissolve-btn" @click="dissolveTeam()">{{ t('team.dissolveTeam') }}</button>
|
|
1533
|
+
<button v-if="!isTeamActive" class="team-new-btn" @click="newTeam()">{{ t('team.newTeam') }}</button>
|
|
1534
|
+
<button v-if="!isTeamActive" class="team-back-btn" @click="backToChat()">{{ t('team.backToChat') }}</button>
|
|
1509
1535
|
</div>
|
|
1510
1536
|
</div>
|
|
1511
1537
|
<div class="team-dash-instruction" :class="{ expanded: instructionExpanded }">
|
|
1512
|
-
<div class="team-dash-instruction-text">{{ displayTeam.config?.instruction || displayTeam.title || '
|
|
1538
|
+
<div class="team-dash-instruction-text">{{ displayTeam.config?.instruction || displayTeam.title || t('team.agentTeam') }}</div>
|
|
1513
1539
|
<button v-if="(displayTeam.config?.instruction || '').length > 120" class="team-dash-instruction-toggle" @click="instructionExpanded = !instructionExpanded">
|
|
1514
|
-
{{ instructionExpanded ? '
|
|
1540
|
+
{{ instructionExpanded ? t('team.showLess') : t('team.showMore') }}
|
|
1515
1541
|
</button>
|
|
1516
1542
|
</div>
|
|
1517
1543
|
</div>
|
|
@@ -1519,7 +1545,7 @@ const App = {
|
|
|
1519
1545
|
<!-- Lead status bar (clickable to view lead detail) -->
|
|
1520
1546
|
<div v-if="displayTeam.leadStatus && (displayTeam.status === 'planning' || displayTeam.status === 'running' || displayTeam.status === 'summarizing')" class="team-lead-bar team-lead-bar-clickable" @click="viewAgent('lead')">
|
|
1521
1547
|
<span class="team-lead-dot"></span>
|
|
1522
|
-
<span class="team-lead-label">
|
|
1548
|
+
<span class="team-lead-label">{{ t('team.lead') }}</span>
|
|
1523
1549
|
<span class="team-lead-text">{{ displayTeam.leadStatus }}</span>
|
|
1524
1550
|
</div>
|
|
1525
1551
|
|
|
@@ -1533,14 +1559,14 @@ const App = {
|
|
|
1533
1559
|
<div class="team-kanban-section">
|
|
1534
1560
|
<div class="team-kanban-section-header" @click="kanbanExpanded = !kanbanExpanded">
|
|
1535
1561
|
<span class="team-kanban-section-toggle">{{ kanbanExpanded ? '\u25BC' : '\u25B6' }}</span>
|
|
1536
|
-
<span class="team-kanban-section-title">
|
|
1537
|
-
<span class="team-kanban-section-summary">{{ doneTasks.length }}/{{ displayTeam.tasks.length }} done</span>
|
|
1562
|
+
<span class="team-kanban-section-title">{{ t('team.tasks') }}</span>
|
|
1563
|
+
<span class="team-kanban-section-summary">{{ doneTasks.length }}/{{ displayTeam.tasks.length }} {{ t('team.done') }}</span>
|
|
1538
1564
|
</div>
|
|
1539
1565
|
<div v-show="kanbanExpanded" class="team-kanban">
|
|
1540
1566
|
<div class="team-kanban-col">
|
|
1541
1567
|
<div class="team-kanban-col-header">
|
|
1542
1568
|
<span class="team-kanban-col-dot pending"></span>
|
|
1543
|
-
|
|
1569
|
+
{{ t('team.pending') }}
|
|
1544
1570
|
<span class="team-kanban-col-count">{{ pendingTasks.length }}</span>
|
|
1545
1571
|
</div>
|
|
1546
1572
|
<div class="team-kanban-col-body">
|
|
@@ -1548,13 +1574,13 @@ const App = {
|
|
|
1548
1574
|
<div class="team-task-title">{{ task.title }}</div>
|
|
1549
1575
|
<div v-if="task.description" class="team-task-desc team-task-desc-clamp" @click.stop="$event.target.classList.toggle('team-task-desc-expanded')">{{ task.description }}</div>
|
|
1550
1576
|
</div>
|
|
1551
|
-
<div v-if="pendingTasks.length === 0" class="team-kanban-empty">
|
|
1577
|
+
<div v-if="pendingTasks.length === 0" class="team-kanban-empty">{{ t('team.noTasks') }}</div>
|
|
1552
1578
|
</div>
|
|
1553
1579
|
</div>
|
|
1554
1580
|
<div class="team-kanban-col">
|
|
1555
1581
|
<div class="team-kanban-col-header">
|
|
1556
1582
|
<span class="team-kanban-col-dot active"></span>
|
|
1557
|
-
|
|
1583
|
+
{{ t('team.activeCol') }}
|
|
1558
1584
|
<span class="team-kanban-col-count">{{ activeTasks.length }}</span>
|
|
1559
1585
|
</div>
|
|
1560
1586
|
<div class="team-kanban-col-body">
|
|
@@ -1566,13 +1592,13 @@ const App = {
|
|
|
1566
1592
|
{{ getTaskAgent(task).name || task.assignee || task.assignedTo }}
|
|
1567
1593
|
</div>
|
|
1568
1594
|
</div>
|
|
1569
|
-
<div v-if="activeTasks.length === 0" class="team-kanban-empty">
|
|
1595
|
+
<div v-if="activeTasks.length === 0" class="team-kanban-empty">{{ t('team.noTasks') }}</div>
|
|
1570
1596
|
</div>
|
|
1571
1597
|
</div>
|
|
1572
1598
|
<div class="team-kanban-col">
|
|
1573
1599
|
<div class="team-kanban-col-header">
|
|
1574
1600
|
<span class="team-kanban-col-dot done"></span>
|
|
1575
|
-
|
|
1601
|
+
{{ t('team.doneCol') }}
|
|
1576
1602
|
<span class="team-kanban-col-count">{{ doneTasks.length }}</span>
|
|
1577
1603
|
</div>
|
|
1578
1604
|
<div class="team-kanban-col-body">
|
|
@@ -1584,13 +1610,13 @@ const App = {
|
|
|
1584
1610
|
{{ getTaskAgent(task).name || task.assignee || task.assignedTo }}
|
|
1585
1611
|
</div>
|
|
1586
1612
|
</div>
|
|
1587
|
-
<div v-if="doneTasks.length === 0" class="team-kanban-empty">
|
|
1613
|
+
<div v-if="doneTasks.length === 0" class="team-kanban-empty">{{ t('team.noTasks') }}</div>
|
|
1588
1614
|
</div>
|
|
1589
1615
|
</div>
|
|
1590
1616
|
<div v-if="failedTasks.length > 0" class="team-kanban-col">
|
|
1591
1617
|
<div class="team-kanban-col-header">
|
|
1592
1618
|
<span class="team-kanban-col-dot failed"></span>
|
|
1593
|
-
|
|
1619
|
+
{{ t('team.failed') }}
|
|
1594
1620
|
<span class="team-kanban-col-count">{{ failedTasks.length }}</span>
|
|
1595
1621
|
</div>
|
|
1596
1622
|
<div class="team-kanban-col-body">
|
|
@@ -1605,7 +1631,7 @@ const App = {
|
|
|
1605
1631
|
|
|
1606
1632
|
<!-- Agent cards (horizontal) -->
|
|
1607
1633
|
<div class="team-agents-bar">
|
|
1608
|
-
<div class="team-agents-bar-header">
|
|
1634
|
+
<div class="team-agents-bar-header">{{ t('team.agents') }}</div>
|
|
1609
1635
|
<div class="team-agents-bar-list">
|
|
1610
1636
|
<div
|
|
1611
1637
|
v-for="agent in (displayTeam.agents || [])" :key="agent.id"
|
|
@@ -1620,15 +1646,15 @@ const App = {
|
|
|
1620
1646
|
<div v-if="getLatestAgentActivity(agent.id)" class="team-agent-card-activity">{{ getLatestAgentActivity(agent.id) }}</div>
|
|
1621
1647
|
</div>
|
|
1622
1648
|
<div v-if="!displayTeam.agents || displayTeam.agents.length === 0" class="team-agents-empty">
|
|
1623
|
-
<span v-if="displayTeam.status === 'planning'">
|
|
1624
|
-
<span v-else>
|
|
1649
|
+
<span v-if="displayTeam.status === 'planning'">{{ t('team.planningTasks') }}</span>
|
|
1650
|
+
<span v-else>{{ t('team.noAgents') }}</span>
|
|
1625
1651
|
</div>
|
|
1626
1652
|
</div>
|
|
1627
1653
|
</div>
|
|
1628
1654
|
|
|
1629
1655
|
<!-- Activity feed -->
|
|
1630
1656
|
<div v-if="displayTeam.feed && displayTeam.feed.length > 0" class="team-feed">
|
|
1631
|
-
<div class="team-feed-header">
|
|
1657
|
+
<div class="team-feed-header">{{ t('team.activity') }}</div>
|
|
1632
1658
|
<div class="team-feed-list">
|
|
1633
1659
|
<div v-for="(entry, fi) in displayTeam.feed" :key="fi" class="team-feed-entry">
|
|
1634
1660
|
<span v-if="entry.agentId" class="team-agent-dot" :style="{ background: getAgentColor(entry.agentId) }"></span>
|
|
@@ -1642,26 +1668,26 @@ const App = {
|
|
|
1642
1668
|
<!-- Completion stats -->
|
|
1643
1669
|
<div v-if="displayTeam.status === 'completed' || displayTeam.status === 'failed'" class="team-stats-bar">
|
|
1644
1670
|
<div class="team-stat">
|
|
1645
|
-
<span class="team-stat-label">
|
|
1671
|
+
<span class="team-stat-label">{{ t('team.tasksStat') }}</span>
|
|
1646
1672
|
<span class="team-stat-value">{{ doneTasks.length }}/{{ displayTeam.tasks.length }}</span>
|
|
1647
1673
|
</div>
|
|
1648
1674
|
<div v-if="displayTeam.durationMs" class="team-stat">
|
|
1649
|
-
<span class="team-stat-label">
|
|
1675
|
+
<span class="team-stat-label">{{ t('team.duration') }}</span>
|
|
1650
1676
|
<span class="team-stat-value">{{ Math.round(displayTeam.durationMs / 1000) }}s</span>
|
|
1651
1677
|
</div>
|
|
1652
1678
|
<div v-if="displayTeam.totalCost" class="team-stat">
|
|
1653
|
-
<span class="team-stat-label">
|
|
1679
|
+
<span class="team-stat-label">{{ t('team.cost') }}</span>
|
|
1654
1680
|
<span class="team-stat-value">{{ '$' + displayTeam.totalCost.toFixed(2) }}</span>
|
|
1655
1681
|
</div>
|
|
1656
1682
|
<div class="team-stat">
|
|
1657
|
-
<span class="team-stat-label">
|
|
1683
|
+
<span class="team-stat-label">{{ t('team.agentsStat') }}</span>
|
|
1658
1684
|
<span class="team-stat-value">{{ (displayTeam.agents || []).length }}</span>
|
|
1659
1685
|
</div>
|
|
1660
1686
|
</div>
|
|
1661
1687
|
|
|
1662
1688
|
<!-- Completion summary -->
|
|
1663
1689
|
<div v-if="displayTeam.status === 'completed' && displayTeam.summary" class="team-summary">
|
|
1664
|
-
<div class="team-summary-header">
|
|
1690
|
+
<div class="team-summary-header">{{ t('team.summary') }}</div>
|
|
1665
1691
|
<div class="team-summary-body markdown-body" v-html="getRenderedContent({ role: 'assistant', content: displayTeam.summary })"></div>
|
|
1666
1692
|
</div>
|
|
1667
1693
|
|
|
@@ -1670,16 +1696,16 @@ const App = {
|
|
|
1670
1696
|
<textarea
|
|
1671
1697
|
v-model="teamInstruction"
|
|
1672
1698
|
class="team-new-launcher-input"
|
|
1673
|
-
placeholder="
|
|
1699
|
+
:placeholder="t('team.launchAnotherPlaceholder')"
|
|
1674
1700
|
rows="2"
|
|
1675
1701
|
@keydown.enter.ctrl="launchTeamFromPanel()"
|
|
1676
1702
|
></textarea>
|
|
1677
1703
|
<div class="team-new-launcher-actions">
|
|
1678
1704
|
<button class="team-create-launch" :disabled="!teamInstruction.trim()" @click="launchTeamFromPanel()">
|
|
1679
1705
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
|
1680
|
-
|
|
1706
|
+
{{ t('team.newTeam') }}
|
|
1681
1707
|
</button>
|
|
1682
|
-
<button class="team-create-cancel" @click="backToChat()">
|
|
1708
|
+
<button class="team-create-cancel" @click="backToChat()">{{ t('team.backToChat') }}</button>
|
|
1683
1709
|
</div>
|
|
1684
1710
|
</div>
|
|
1685
1711
|
</div>
|
|
@@ -1689,7 +1715,7 @@ const App = {
|
|
|
1689
1715
|
<div class="team-agent-detail-header" :style="{ borderBottom: '2px solid ' + getAgentColor(activeAgentView) }">
|
|
1690
1716
|
<button class="team-agent-back-btn" @click="viewDashboard()">
|
|
1691
1717
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
1692
|
-
|
|
1718
|
+
{{ t('team.dashboard') }}
|
|
1693
1719
|
</button>
|
|
1694
1720
|
<span :class="['team-agent-dot', { working: findAgent(activeAgentView)?.status === 'working' || findAgent(activeAgentView)?.status === 'starting' }]" :style="{ background: getAgentColor(activeAgentView) }"></span>
|
|
1695
1721
|
<span class="team-agent-detail-name" :style="{ color: getAgentColor(activeAgentView) }">{{ findAgent(activeAgentView)?.name || activeAgentView }}</span>
|
|
@@ -1698,12 +1724,12 @@ const App = {
|
|
|
1698
1724
|
<div class="team-agent-messages">
|
|
1699
1725
|
<div class="team-agent-messages-inner">
|
|
1700
1726
|
<div v-if="getAgentMessages(activeAgentView).length === 0" class="team-agent-empty-msg">
|
|
1701
|
-
|
|
1727
|
+
{{ t('team.noMessages') }}
|
|
1702
1728
|
</div>
|
|
1703
1729
|
<template v-for="(msg, mi) in getAgentMessages(activeAgentView)" :key="msg.id">
|
|
1704
1730
|
<!-- Agent user/prompt message -->
|
|
1705
1731
|
<div v-if="msg.role === 'user' && msg.content" class="team-agent-prompt">
|
|
1706
|
-
<div class="team-agent-prompt-label">
|
|
1732
|
+
<div class="team-agent-prompt-label">{{ t('team.taskPrompt') }}</div>
|
|
1707
1733
|
<div class="team-agent-prompt-body markdown-body" v-html="getRenderedContent(msg)"></div>
|
|
1708
1734
|
</div>
|
|
1709
1735
|
<!-- System notice (e.g. completion message) -->
|
|
@@ -1760,14 +1786,14 @@ const App = {
|
|
|
1760
1786
|
|
|
1761
1787
|
<div v-if="loadingExecution" class="loop-loading">
|
|
1762
1788
|
<div class="history-loading-spinner"></div>
|
|
1763
|
-
<span>
|
|
1789
|
+
<span>{{ t('loop.loadingExecution') }}</span>
|
|
1764
1790
|
</div>
|
|
1765
1791
|
|
|
1766
1792
|
<div v-else class="loop-exec-messages">
|
|
1767
|
-
<div v-if="executionMessages.length === 0" class="team-agent-empty-msg">
|
|
1793
|
+
<div v-if="executionMessages.length === 0" class="team-agent-empty-msg">{{ t('loop.noExecMessages') }}</div>
|
|
1768
1794
|
<template v-for="(msg, mi) in executionMessages" :key="msg.id">
|
|
1769
1795
|
<div v-if="msg.role === 'user' && msg.content" class="team-agent-prompt">
|
|
1770
|
-
<div class="team-agent-prompt-label">
|
|
1796
|
+
<div class="team-agent-prompt-label">{{ t('loop.loopPrompt') }}</div>
|
|
1771
1797
|
<div class="team-agent-prompt-body markdown-body" v-html="getRenderedContent(msg)"></div>
|
|
1772
1798
|
</div>
|
|
1773
1799
|
<div v-else-if="msg.role === 'assistant'" :class="['message', 'message-assistant']">
|
|
@@ -1804,34 +1830,34 @@ const App = {
|
|
|
1804
1830
|
<div class="loop-detail-header">
|
|
1805
1831
|
<button class="team-agent-back-btn" @click="backToLoopsList()">
|
|
1806
1832
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
1807
|
-
|
|
1833
|
+
{{ t('loop.backToLoops') }}
|
|
1808
1834
|
</button>
|
|
1809
1835
|
</div>
|
|
1810
1836
|
<div class="loop-detail-info">
|
|
1811
1837
|
<h2 class="loop-detail-name">{{ selectedLoop.name }}</h2>
|
|
1812
1838
|
<div class="loop-detail-meta">
|
|
1813
1839
|
<span class="loop-detail-schedule">{{ loopScheduleDisplay(selectedLoop) }}</span>
|
|
1814
|
-
<span :class="['loop-status-badge', selectedLoop.enabled ? 'loop-status-enabled' : 'loop-status-disabled']">{{ selectedLoop.enabled ? '
|
|
1840
|
+
<span :class="['loop-status-badge', selectedLoop.enabled ? 'loop-status-enabled' : 'loop-status-disabled']">{{ selectedLoop.enabled ? t('loop.enabled') : t('loop.disabled') }}</span>
|
|
1815
1841
|
</div>
|
|
1816
1842
|
<div class="loop-detail-actions">
|
|
1817
|
-
<button class="loop-action-btn" @click="startEditingLoop(selectedLoop); selectedLoop = null">
|
|
1818
|
-
<button class="loop-action-btn loop-action-run" @click="runNow(selectedLoop.id)" :disabled="isLoopRunning(selectedLoop.id)">
|
|
1819
|
-
<button class="loop-action-btn" @click="toggleLoop(selectedLoop.id)">{{ selectedLoop.enabled ? '
|
|
1843
|
+
<button class="loop-action-btn" @click="startEditingLoop(selectedLoop); selectedLoop = null">{{ t('loop.edit') }}</button>
|
|
1844
|
+
<button class="loop-action-btn loop-action-run" @click="runNow(selectedLoop.id)" :disabled="isLoopRunning(selectedLoop.id)">{{ t('loop.runNow') }}</button>
|
|
1845
|
+
<button class="loop-action-btn" @click="toggleLoop(selectedLoop.id)">{{ selectedLoop.enabled ? t('loop.disable') : t('loop.enable') }}</button>
|
|
1820
1846
|
</div>
|
|
1821
1847
|
</div>
|
|
1822
1848
|
|
|
1823
1849
|
<div class="loop-detail-prompt-section">
|
|
1824
|
-
<div class="loop-detail-prompt-label">
|
|
1850
|
+
<div class="loop-detail-prompt-label">{{ t('loop.prompt') }}</div>
|
|
1825
1851
|
<div class="loop-detail-prompt-text">{{ selectedLoop.prompt }}</div>
|
|
1826
1852
|
</div>
|
|
1827
1853
|
|
|
1828
1854
|
<div class="loop-exec-history-section">
|
|
1829
|
-
<div class="loop-exec-history-header">
|
|
1855
|
+
<div class="loop-exec-history-header">{{ t('loop.executionHistory') }}</div>
|
|
1830
1856
|
<div v-if="loadingExecutions" class="loop-loading">
|
|
1831
1857
|
<div class="history-loading-spinner"></div>
|
|
1832
|
-
<span>
|
|
1858
|
+
<span>{{ t('loop.loadingExecutions') }}</span>
|
|
1833
1859
|
</div>
|
|
1834
|
-
<div v-else-if="executionHistory.length === 0" class="loop-exec-empty">
|
|
1860
|
+
<div v-else-if="executionHistory.length === 0" class="loop-exec-empty">{{ t('loop.noExecutions') }}</div>
|
|
1835
1861
|
<div v-else class="loop-exec-list">
|
|
1836
1862
|
<div v-for="exec in executionHistory" :key="exec.id" class="loop-exec-item">
|
|
1837
1863
|
<div class="loop-exec-item-left">
|
|
@@ -1843,21 +1869,21 @@ const App = {
|
|
|
1843
1869
|
<template v-else>?</template>
|
|
1844
1870
|
</span>
|
|
1845
1871
|
<span class="loop-exec-time">{{ formatExecTime(exec.startedAt) }}</span>
|
|
1846
|
-
<span v-if="exec.status === 'running'" class="loop-exec-running-label">
|
|
1872
|
+
<span v-if="exec.status === 'running'" class="loop-exec-running-label">{{ t('loop.running') }}</span>
|
|
1847
1873
|
<span v-else-if="exec.durationMs" class="loop-exec-duration">{{ formatDuration(exec.durationMs) }}</span>
|
|
1848
1874
|
<span v-if="exec.error" class="loop-exec-error-text" :title="exec.error">{{ exec.error.length > 40 ? exec.error.slice(0, 40) + '...' : exec.error }}</span>
|
|
1849
|
-
<span v-if="exec.trigger === 'manual'" class="loop-exec-trigger-badge">
|
|
1875
|
+
<span v-if="exec.trigger === 'manual'" class="loop-exec-trigger-badge">{{ t('loop.manualBadge') }}</span>
|
|
1850
1876
|
</div>
|
|
1851
1877
|
<div class="loop-exec-item-right">
|
|
1852
|
-
<button v-if="exec.status === 'running'" class="loop-action-btn" @click="viewExecution(selectedLoop.id, exec.id)">
|
|
1853
|
-
<button v-if="exec.status === 'running'" class="loop-action-btn loop-action-cancel" @click="cancelLoopExecution(selectedLoop.id)">
|
|
1854
|
-
<button v-if="exec.status !== 'running'" class="loop-action-btn" @click="viewExecution(selectedLoop.id, exec.id)">
|
|
1878
|
+
<button v-if="exec.status === 'running'" class="loop-action-btn" @click="viewExecution(selectedLoop.id, exec.id)">{{ t('loop.view') }}</button>
|
|
1879
|
+
<button v-if="exec.status === 'running'" class="loop-action-btn loop-action-cancel" @click="cancelLoopExecution(selectedLoop.id)">{{ t('loop.cancelExec') }}</button>
|
|
1880
|
+
<button v-if="exec.status !== 'running'" class="loop-action-btn" @click="viewExecution(selectedLoop.id, exec.id)">{{ t('loop.view') }}</button>
|
|
1855
1881
|
</div>
|
|
1856
1882
|
</div>
|
|
1857
1883
|
<!-- Load more executions -->
|
|
1858
1884
|
<div v-if="hasMoreExecutions && !loadingExecutions" class="loop-load-more">
|
|
1859
1885
|
<button class="loop-action-btn" :disabled="loadingMoreExecutions" @click="loadMoreExecutions()">
|
|
1860
|
-
{{ loadingMoreExecutions ? '
|
|
1886
|
+
{{ loadingMoreExecutions ? t('filePanel.loading') : t('loop.loadMore') }}
|
|
1861
1887
|
</button>
|
|
1862
1888
|
</div>
|
|
1863
1889
|
</div>
|
|
@@ -1870,13 +1896,13 @@ const App = {
|
|
|
1870
1896
|
<div class="team-create-inner">
|
|
1871
1897
|
<div class="team-create-header">
|
|
1872
1898
|
<svg viewBox="0 0 24 24" width="28" height="28"><path fill="currentColor" opacity="0.5" d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 0 0 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
|
|
1873
|
-
<h2>{{ editingLoopId ? '
|
|
1899
|
+
<h2>{{ editingLoopId ? t('loop.editLoop') : t('loop.createLoop') }}</h2>
|
|
1874
1900
|
</div>
|
|
1875
|
-
<p class="team-create-desc">
|
|
1901
|
+
<p class="team-create-desc">{{ t('loop.createDesc') }}</p>
|
|
1876
1902
|
|
|
1877
1903
|
<!-- Template cards -->
|
|
1878
1904
|
<div v-if="!editingLoopId" class="team-examples-section" style="margin-top: 0;">
|
|
1879
|
-
<div class="team-examples-header">
|
|
1905
|
+
<div class="team-examples-header">{{ t('loop.templates') }}</div>
|
|
1880
1906
|
<div class="team-examples-list">
|
|
1881
1907
|
<div v-for="key in LOOP_TEMPLATE_KEYS" :key="key"
|
|
1882
1908
|
:class="['team-example-card', { 'loop-template-selected': loopSelectedTemplate === key }]"
|
|
@@ -1890,50 +1916,50 @@ const App = {
|
|
|
1890
1916
|
<div class="team-example-title">{{ LOOP_TEMPLATES[key].label }}</div>
|
|
1891
1917
|
<div class="team-example-text">{{ LOOP_TEMPLATES[key].description }}</div>
|
|
1892
1918
|
</div>
|
|
1893
|
-
<button class="team-example-try" @click="selectLoopTemplate(key)">
|
|
1919
|
+
<button class="team-example-try" @click="selectLoopTemplate(key)">{{ t('team.tryIt') }}</button>
|
|
1894
1920
|
</div>
|
|
1895
1921
|
</div>
|
|
1896
1922
|
</div>
|
|
1897
1923
|
|
|
1898
1924
|
<!-- Name field -->
|
|
1899
1925
|
<div class="team-tpl-section">
|
|
1900
|
-
<label class="team-tpl-label">
|
|
1926
|
+
<label class="team-tpl-label">{{ t('loop.name') }}</label>
|
|
1901
1927
|
<input
|
|
1902
1928
|
v-model="loopName"
|
|
1903
1929
|
type="text"
|
|
1904
1930
|
class="loop-name-input"
|
|
1905
|
-
placeholder="
|
|
1931
|
+
:placeholder="t('loop.namePlaceholder')"
|
|
1906
1932
|
/>
|
|
1907
1933
|
</div>
|
|
1908
1934
|
|
|
1909
1935
|
<!-- Prompt field -->
|
|
1910
1936
|
<div class="team-tpl-section">
|
|
1911
|
-
<label class="team-tpl-label">
|
|
1937
|
+
<label class="team-tpl-label">{{ t('loop.prompt') }}</label>
|
|
1912
1938
|
<textarea
|
|
1913
1939
|
v-model="loopPrompt"
|
|
1914
1940
|
class="team-create-textarea"
|
|
1915
|
-
placeholder="
|
|
1941
|
+
:placeholder="t('loop.promptPlaceholder')"
|
|
1916
1942
|
rows="5"
|
|
1917
1943
|
></textarea>
|
|
1918
1944
|
</div>
|
|
1919
1945
|
|
|
1920
1946
|
<!-- Schedule selector -->
|
|
1921
1947
|
<div class="team-tpl-section">
|
|
1922
|
-
<label class="team-tpl-label">
|
|
1948
|
+
<label class="team-tpl-label">{{ t('loop.schedule') }}</label>
|
|
1923
1949
|
<div class="loop-schedule-options">
|
|
1924
1950
|
<label class="loop-schedule-radio">
|
|
1925
1951
|
<input type="radio" v-model="loopScheduleType" value="manual" />
|
|
1926
|
-
<span>
|
|
1927
|
-
<span v-if="loopScheduleType === 'manual'" class="loop-schedule-detail" style="opacity:0.6">
|
|
1952
|
+
<span>{{ t('loop.manual') }}</span>
|
|
1953
|
+
<span v-if="loopScheduleType === 'manual'" class="loop-schedule-detail" style="opacity:0.6">{{ t('loop.manualDetail') }}</span>
|
|
1928
1954
|
</label>
|
|
1929
1955
|
<label class="loop-schedule-radio">
|
|
1930
1956
|
<input type="radio" v-model="loopScheduleType" value="hourly" />
|
|
1931
|
-
<span>
|
|
1957
|
+
<span>{{ t('loop.everyHour') }}</span>
|
|
1932
1958
|
<span v-if="loopScheduleType === 'hourly'" class="loop-schedule-detail">at minute {{ padTwo(loopScheduleMinute) }}</span>
|
|
1933
1959
|
</label>
|
|
1934
1960
|
<label class="loop-schedule-radio">
|
|
1935
1961
|
<input type="radio" v-model="loopScheduleType" value="daily" />
|
|
1936
|
-
<span>
|
|
1962
|
+
<span>{{ t('loop.everyDay') }}</span>
|
|
1937
1963
|
<span v-if="loopScheduleType === 'daily'" class="loop-schedule-detail">
|
|
1938
1964
|
at
|
|
1939
1965
|
<input type="number" v-model.number="loopScheduleHour" min="0" max="23" class="loop-time-input" />
|
|
@@ -1943,7 +1969,7 @@ const App = {
|
|
|
1943
1969
|
</label>
|
|
1944
1970
|
<label class="loop-schedule-radio">
|
|
1945
1971
|
<input type="radio" v-model="loopScheduleType" value="cron" />
|
|
1946
|
-
<span>
|
|
1972
|
+
<span>{{ t('loop.advancedCron') }}</span>
|
|
1947
1973
|
<span v-if="loopScheduleType === 'cron'" class="loop-schedule-detail">
|
|
1948
1974
|
<input type="text" v-model="loopCronExpr" class="loop-cron-input" placeholder="0 9 * * *" />
|
|
1949
1975
|
</span>
|
|
@@ -1954,14 +1980,14 @@ const App = {
|
|
|
1954
1980
|
<!-- Action buttons -->
|
|
1955
1981
|
<div class="team-create-actions">
|
|
1956
1982
|
<button v-if="editingLoopId" class="team-create-launch" :disabled="!loopName.trim() || !loopPrompt.trim()" @click="saveLoopEdits()">
|
|
1957
|
-
|
|
1983
|
+
{{ t('loop.saveChanges') }}
|
|
1958
1984
|
</button>
|
|
1959
1985
|
<button v-else class="team-create-launch" :disabled="!loopName.trim() || !loopPrompt.trim()" @click="createLoopFromPanel()">
|
|
1960
1986
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 0 0 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
|
|
1961
|
-
|
|
1987
|
+
{{ t('loop.createLoopBtn') }}
|
|
1962
1988
|
</button>
|
|
1963
|
-
<button v-if="editingLoopId" class="team-create-cancel" @click="cancelEditingLoop()">
|
|
1964
|
-
<button class="team-create-cancel" @click="backToChat()">
|
|
1989
|
+
<button v-if="editingLoopId" class="team-create-cancel" @click="cancelEditingLoop()">{{ t('loop.cancel') }}</button>
|
|
1990
|
+
<button class="team-create-cancel" @click="backToChat()">{{ t('loop.backToChat') }}</button>
|
|
1965
1991
|
</div>
|
|
1966
1992
|
|
|
1967
1993
|
<!-- Error message -->
|
|
@@ -1973,7 +1999,7 @@ const App = {
|
|
|
1973
1999
|
|
|
1974
2000
|
<!-- Active Loops list -->
|
|
1975
2001
|
<div v-if="loopsList.length > 0" class="loop-active-section">
|
|
1976
|
-
<div class="loop-active-header">
|
|
2002
|
+
<div class="loop-active-header">{{ t('loop.activeLoops') }}</div>
|
|
1977
2003
|
<div class="loop-active-list">
|
|
1978
2004
|
<div v-for="l in loopsList" :key="l.id" class="loop-active-item">
|
|
1979
2005
|
<div class="loop-active-item-info" @click="viewLoop(l.id)">
|
|
@@ -1986,14 +2012,14 @@ const App = {
|
|
|
1986
2012
|
<span v-if="l.lastExecution" class="loop-active-item-last">
|
|
1987
2013
|
Last: {{ loopLastRunDisplay(l) }}
|
|
1988
2014
|
</span>
|
|
1989
|
-
<span v-if="isLoopRunning(l.id)" class="loop-exec-running-label">
|
|
2015
|
+
<span v-if="isLoopRunning(l.id)" class="loop-exec-running-label">{{ t('loop.running') }}</span>
|
|
1990
2016
|
</div>
|
|
1991
2017
|
</div>
|
|
1992
2018
|
<div class="loop-active-item-actions">
|
|
1993
|
-
<button class="loop-action-btn loop-action-sm" @click="startEditingLoop(l)" title="
|
|
1994
|
-
<button class="loop-action-btn loop-action-sm loop-action-run" @click="runNow(l.id)" :disabled="isLoopRunning(l.id)" title="
|
|
1995
|
-
<button class="loop-action-btn loop-action-sm" @click="toggleLoop(l.id)" :title="l.enabled ? '
|
|
1996
|
-
<button v-if="!l.enabled" class="loop-action-btn loop-action-sm loop-action-delete" @click="requestDeleteLoop(l)" title="
|
|
2019
|
+
<button class="loop-action-btn loop-action-sm" @click="startEditingLoop(l)" :title="t('loop.edit')">{{ t('loop.edit') }}</button>
|
|
2020
|
+
<button class="loop-action-btn loop-action-sm loop-action-run" @click="runNow(l.id)" :disabled="isLoopRunning(l.id)" :title="t('loop.runNow')">{{ t('loop.run') }}</button>
|
|
2021
|
+
<button class="loop-action-btn loop-action-sm" @click="toggleLoop(l.id)" :title="l.enabled ? t('loop.disable') : t('loop.enable')">{{ l.enabled ? t('loop.pause') : t('loop.resume') }}</button>
|
|
2022
|
+
<button v-if="!l.enabled" class="loop-action-btn loop-action-sm loop-action-delete" @click="requestDeleteLoop(l)" :title="t('loop.deleteLoop')">{{ t('loop.del') }}</button>
|
|
1997
2023
|
</div>
|
|
1998
2024
|
</div>
|
|
1999
2025
|
</div>
|
|
@@ -2004,18 +2030,18 @@ const App = {
|
|
|
2004
2030
|
<!-- Running Loop notification banner -->
|
|
2005
2031
|
<div v-if="hasRunningLoop && !selectedLoop" class="loop-running-banner">
|
|
2006
2032
|
<span class="loop-running-banner-dot"></span>
|
|
2007
|
-
<span>{{ firstRunningLoop.name }}
|
|
2008
|
-
<button class="loop-action-btn loop-action-sm" @click="viewLoop(firstRunningLoop.loopId)">
|
|
2033
|
+
<span>{{ firstRunningLoop.name }} {{ t('loop.isRunning') }}</span>
|
|
2034
|
+
<button class="loop-action-btn loop-action-sm" @click="viewLoop(firstRunningLoop.loopId)">{{ t('loop.view') }}</button>
|
|
2009
2035
|
</div>
|
|
2010
2036
|
|
|
2011
2037
|
<!-- Loop delete confirm dialog -->
|
|
2012
2038
|
<div v-if="loopDeleteConfirmOpen" class="modal-overlay" @click.self="cancelDeleteLoop()">
|
|
2013
2039
|
<div class="modal-dialog">
|
|
2014
|
-
<div class="modal-title">
|
|
2015
|
-
<div class="modal-body"
|
|
2040
|
+
<div class="modal-title">{{ t('loop.deleteLoop') }}</div>
|
|
2041
|
+
<div class="modal-body" v-html="t('loop.deleteConfirm', { name: loopDeleteConfirmName })"></div>
|
|
2016
2042
|
<div class="modal-actions">
|
|
2017
|
-
<button class="modal-confirm-btn" @click="confirmDeleteLoop()">
|
|
2018
|
-
<button class="modal-cancel-btn" @click="cancelDeleteLoop()">
|
|
2043
|
+
<button class="modal-confirm-btn" @click="confirmDeleteLoop()">{{ t('loop.delete') }}</button>
|
|
2044
|
+
<button class="modal-cancel-btn" @click="cancelDeleteLoop()">{{ t('loop.cancel') }}</button>
|
|
2019
2045
|
</div>
|
|
2020
2046
|
</div>
|
|
2021
2047
|
</div>
|
|
@@ -2029,25 +2055,25 @@ const App = {
|
|
|
2029
2055
|
<div class="empty-state-icon">
|
|
2030
2056
|
<svg viewBox="0 0 24 24" width="48" height="48"><path fill="currentColor" opacity="0.4" d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
|
|
2031
2057
|
</div>
|
|
2032
|
-
<p>
|
|
2058
|
+
<p>{{ t('chat.connectedTo') }} <strong>{{ agentName }}</strong></p>
|
|
2033
2059
|
<p class="muted">{{ workDir }}</p>
|
|
2034
|
-
<p class="muted" style="margin-top: 0.5rem;">
|
|
2060
|
+
<p class="muted" style="margin-top: 0.5rem;">{{ t('chat.sendToStart') }}</p>
|
|
2035
2061
|
</div>
|
|
2036
2062
|
|
|
2037
2063
|
<div v-if="loadingHistory" class="history-loading">
|
|
2038
2064
|
<div class="history-loading-spinner"></div>
|
|
2039
|
-
<span>
|
|
2065
|
+
<span>{{ t('chat.loadingHistory') }}</span>
|
|
2040
2066
|
</div>
|
|
2041
2067
|
|
|
2042
2068
|
<div v-if="hasMoreMessages" class="load-more-wrapper">
|
|
2043
|
-
<button class="load-more-btn" @click="loadMoreMessages">
|
|
2069
|
+
<button class="load-more-btn" @click="loadMoreMessages">{{ t('chat.loadEarlier') }}</button>
|
|
2044
2070
|
</div>
|
|
2045
2071
|
|
|
2046
2072
|
<div v-for="(msg, msgIdx) in visibleMessages" :key="msg.id" :class="['message', 'message-' + msg.role]">
|
|
2047
2073
|
|
|
2048
2074
|
<!-- User message -->
|
|
2049
2075
|
<template v-if="msg.role === 'user'">
|
|
2050
|
-
<div class="message-role-label user-label">
|
|
2076
|
+
<div class="message-role-label user-label">{{ t('chat.you') }}</div>
|
|
2051
2077
|
<div class="message-bubble user-bubble" :title="formatTimestamp(msg.timestamp)">
|
|
2052
2078
|
<div class="message-content">{{ msg.content }}</div>
|
|
2053
2079
|
<div v-if="msg.attachments && msg.attachments.length" class="message-attachments">
|
|
@@ -2064,10 +2090,10 @@ const App = {
|
|
|
2064
2090
|
|
|
2065
2091
|
<!-- Assistant message (markdown) -->
|
|
2066
2092
|
<template v-else-if="msg.role === 'assistant'">
|
|
2067
|
-
<div v-if="!isPrevAssistant(msgIdx)" class="message-role-label assistant-label">
|
|
2093
|
+
<div v-if="!isPrevAssistant(msgIdx)" class="message-role-label assistant-label">{{ t('chat.claude') }}</div>
|
|
2068
2094
|
<div :class="['message-bubble', 'assistant-bubble', { streaming: msg.isStreaming }]" :title="formatTimestamp(msg.timestamp)">
|
|
2069
2095
|
<div class="message-actions">
|
|
2070
|
-
<button class="icon-btn" @click="copyMessage(msg)" :title="msg.copied ? '
|
|
2096
|
+
<button class="icon-btn" @click="copyMessage(msg)" :title="msg.copied ? t('chat.copied') : t('chat.copy')">
|
|
2071
2097
|
<svg v-if="!msg.copied" viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
|
|
2072
2098
|
<svg v-else viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
|
|
2073
2099
|
</button>
|
|
@@ -2093,7 +2119,7 @@ const App = {
|
|
|
2093
2119
|
<div v-show="msg.expanded" class="tool-expand team-agent-tool-expand">
|
|
2094
2120
|
<pre v-if="msg.toolInput" class="tool-block">{{ msg.toolInput }}</pre>
|
|
2095
2121
|
<div v-if="msg.toolOutput" class="team-agent-tool-result">
|
|
2096
|
-
<div class="team-agent-tool-result-label">
|
|
2122
|
+
<div class="team-agent-tool-result-label">{{ t('team.agentResult') }}</div>
|
|
2097
2123
|
<div class="team-agent-tool-result-content markdown-body" v-html="getRenderedContent({ role: 'assistant', content: msg.toolOutput })"></div>
|
|
2098
2124
|
</div>
|
|
2099
2125
|
</div>
|
|
@@ -2143,7 +2169,7 @@ const App = {
|
|
|
2143
2169
|
<input
|
|
2144
2170
|
type="text"
|
|
2145
2171
|
v-model="msg.customTexts[qi]"
|
|
2146
|
-
placeholder="
|
|
2172
|
+
:placeholder="t('chat.customResponse')"
|
|
2147
2173
|
@input="msg.selectedAnswers[qi] = q.multiSelect ? [] : null"
|
|
2148
2174
|
@keydown.enter="hasQuestionAnswer(msg) && submitQuestionAnswer(msg)"
|
|
2149
2175
|
/>
|
|
@@ -2151,7 +2177,7 @@ const App = {
|
|
|
2151
2177
|
</div>
|
|
2152
2178
|
<div class="ask-question-actions">
|
|
2153
2179
|
<button class="ask-question-submit" :disabled="!hasQuestionAnswer(msg)" @click="submitQuestionAnswer(msg)">
|
|
2154
|
-
|
|
2180
|
+
{{ t('chat.submit') }}
|
|
2155
2181
|
</button>
|
|
2156
2182
|
</div>
|
|
2157
2183
|
</div>
|
|
@@ -2165,8 +2191,8 @@ const App = {
|
|
|
2165
2191
|
<div v-else-if="msg.role === 'context-summary'" class="context-summary-wrapper">
|
|
2166
2192
|
<div class="context-summary-bar" @click="toggleContextSummary(msg)">
|
|
2167
2193
|
<svg class="context-summary-icon" viewBox="0 0 16 16" width="14" height="14"><path fill="currentColor" d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></svg>
|
|
2168
|
-
<span class="context-summary-label">
|
|
2169
|
-
<span class="context-summary-toggle">{{ msg.contextExpanded ? '
|
|
2194
|
+
<span class="context-summary-label">{{ t('chat.contextContinued') }}</span>
|
|
2195
|
+
<span class="context-summary-toggle">{{ msg.contextExpanded ? t('chat.hide') : t('chat.show') }}</span>
|
|
2170
2196
|
</div>
|
|
2171
2197
|
<div v-if="msg.contextExpanded" class="context-summary-body">
|
|
2172
2198
|
<div class="markdown-body" v-html="getRenderedContent({ role: 'assistant', content: msg.content })"></div>
|
|
@@ -2211,7 +2237,7 @@ const App = {
|
|
|
2211
2237
|
<svg viewBox="0 0 24 24" width="11" height="11"><path fill="currentColor" d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></svg>
|
|
2212
2238
|
{{ qm.attachments.length }}
|
|
2213
2239
|
</span>
|
|
2214
|
-
<button class="queue-item-remove" @click="removeQueuedMessage(qm.id)" title="
|
|
2240
|
+
<button class="queue-item-remove" @click="removeQueuedMessage(qm.id)" :title="t('input.removeFromQueue')">×</button>
|
|
2215
2241
|
</div>
|
|
2216
2242
|
</div>
|
|
2217
2243
|
<div v-if="usageStats" class="usage-bar">{{ formatUsage(usageStats) }}</div>
|
|
@@ -2228,7 +2254,7 @@ const App = {
|
|
|
2228
2254
|
@input="autoResize"
|
|
2229
2255
|
@paste="handlePaste"
|
|
2230
2256
|
:disabled="status !== 'Connected' || isCompacting"
|
|
2231
|
-
:placeholder="isCompacting ? '
|
|
2257
|
+
:placeholder="isCompacting ? t('input.compacting') : t('input.placeholder')"
|
|
2232
2258
|
rows="1"
|
|
2233
2259
|
></textarea>
|
|
2234
2260
|
<div v-if="attachments.length > 0" class="attachment-bar">
|
|
@@ -2241,17 +2267,17 @@ const App = {
|
|
|
2241
2267
|
<div class="attachment-name">{{ att.name }}</div>
|
|
2242
2268
|
<div class="attachment-size">{{ formatFileSize(att.size) }}</div>
|
|
2243
2269
|
</div>
|
|
2244
|
-
<button class="attachment-remove" @click="removeAttachment(i)" title="
|
|
2270
|
+
<button class="attachment-remove" @click="removeAttachment(i)" :title="t('input.remove')">×</button>
|
|
2245
2271
|
</div>
|
|
2246
2272
|
</div>
|
|
2247
2273
|
<div class="input-bottom-row">
|
|
2248
|
-
<button class="attach-btn" @click="triggerFileInput" :disabled="status !== 'Connected' || isCompacting || attachments.length >= 5" title="
|
|
2274
|
+
<button class="attach-btn" @click="triggerFileInput" :disabled="status !== 'Connected' || isCompacting || attachments.length >= 5" :title="t('input.attachFiles')">
|
|
2249
2275
|
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5a2.5 2.5 0 0 0 5 0V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/></svg>
|
|
2250
2276
|
</button>
|
|
2251
|
-
<button v-if="isProcessing && !hasInput" @click="cancelExecution" class="send-btn stop-btn" title="
|
|
2277
|
+
<button v-if="isProcessing && !hasInput" @click="cancelExecution" class="send-btn stop-btn" :title="t('input.stopGeneration')">
|
|
2252
2278
|
<svg viewBox="0 0 24 24" width="14" height="14"><rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor"/></svg>
|
|
2253
2279
|
</button>
|
|
2254
|
-
<button v-else @click="sendMessage" :disabled="!canSend" class="send-btn" title="
|
|
2280
|
+
<button v-else @click="sendMessage" :disabled="!canSend" class="send-btn" :title="t('input.send')">
|
|
2255
2281
|
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
|
2256
2282
|
</button>
|
|
2257
2283
|
</div>
|
|
@@ -2267,21 +2293,21 @@ const App = {
|
|
|
2267
2293
|
@touchstart="filePreview.onResizeStart($event)"></div>
|
|
2268
2294
|
<div class="preview-panel-header">
|
|
2269
2295
|
<span class="preview-panel-filename" :title="previewFile?.filePath">
|
|
2270
|
-
{{ previewFile?.fileName || '
|
|
2296
|
+
{{ previewFile?.fileName || t('preview.preview') }}
|
|
2271
2297
|
</span>
|
|
2272
2298
|
<button v-if="previewFile?.content && filePreview.isMarkdownFile(previewFile.fileName)"
|
|
2273
2299
|
class="preview-md-toggle" :class="{ active: previewMarkdownRendered }"
|
|
2274
2300
|
@click="previewMarkdownRendered = !previewMarkdownRendered"
|
|
2275
|
-
:title="previewMarkdownRendered ? '
|
|
2301
|
+
:title="previewMarkdownRendered ? t('preview.showSource') : t('preview.renderMarkdown')">
|
|
2276
2302
|
<svg viewBox="0 0 16 16" width="14" height="14"><path fill="currentColor" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"/></svg>
|
|
2277
2303
|
</button>
|
|
2278
2304
|
<span v-if="previewFile" class="preview-panel-size">
|
|
2279
2305
|
{{ filePreview.formatFileSize(previewFile.totalSize) }}
|
|
2280
2306
|
</span>
|
|
2281
|
-
<button class="preview-panel-close" @click="filePreview.closePreview()" title="
|
|
2307
|
+
<button class="preview-panel-close" @click="filePreview.closePreview()" :title="t('preview.closePreview')">×</button>
|
|
2282
2308
|
</div>
|
|
2283
2309
|
<div class="preview-panel-body">
|
|
2284
|
-
<div v-if="previewLoading" class="preview-loading">
|
|
2310
|
+
<div v-if="previewLoading" class="preview-loading">{{ t('preview.loading') }}</div>
|
|
2285
2311
|
<div v-else-if="previewFile?.error" class="preview-error">
|
|
2286
2312
|
{{ previewFile.error }}
|
|
2287
2313
|
</div>
|
|
@@ -2296,14 +2322,14 @@ const App = {
|
|
|
2296
2322
|
<div v-else-if="previewFile?.content" class="preview-text-container">
|
|
2297
2323
|
<pre class="preview-code"><code v-html="filePreview.highlightCode(previewFile.content, previewFile.fileName)"></code></pre>
|
|
2298
2324
|
<div v-if="previewFile.truncated" class="preview-truncated-notice">
|
|
2299
|
-
|
|
2325
|
+
{{ t('preview.fileTruncated', { size: filePreview.formatFileSize(previewFile.totalSize) }) }}
|
|
2300
2326
|
</div>
|
|
2301
2327
|
</div>
|
|
2302
2328
|
<div v-else-if="previewFile && !previewFile.content && !previewFile.error" class="preview-binary-info">
|
|
2303
2329
|
<div class="preview-binary-icon">
|
|
2304
2330
|
<svg viewBox="0 0 24 24" width="48" height="48"><path fill="currentColor" opacity="0.4" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/></svg>
|
|
2305
2331
|
</div>
|
|
2306
|
-
<p>
|
|
2332
|
+
<p>{{ t('preview.binaryFile') }}</p>
|
|
2307
2333
|
<p class="preview-binary-meta">{{ previewFile.mimeType }}</p>
|
|
2308
2334
|
<p class="preview-binary-meta">{{ filePreview.formatFileSize(previewFile.totalSize) }}</p>
|
|
2309
2335
|
</div>
|
|
@@ -2317,20 +2343,19 @@ const App = {
|
|
|
2317
2343
|
<div class="folder-picker-overlay" v-if="folderPickerOpen" @click.self="folderPickerOpen = false">
|
|
2318
2344
|
<div class="folder-picker-dialog">
|
|
2319
2345
|
<div class="folder-picker-header">
|
|
2320
|
-
<span>
|
|
2346
|
+
<span>{{ t('folderPicker.title') }}</span>
|
|
2321
2347
|
<button class="folder-picker-close" @click="folderPickerOpen = false">×</button>
|
|
2322
2348
|
</div>
|
|
2323
2349
|
<div class="folder-picker-nav">
|
|
2324
|
-
<button class="folder-picker-up" @click="folderPickerNavigateUp" :disabled="!folderPickerPath" title="
|
|
2350
|
+
<button class="folder-picker-up" @click="folderPickerNavigateUp" :disabled="!folderPickerPath" :title="t('folderPicker.parentDir')">
|
|
2325
2351
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
|
|
2326
2352
|
</button>
|
|
2327
|
-
<input class="folder-picker-path-input" type="text" v-model="folderPickerPath" @keydown.enter="folderPickerGoToPath" placeholder="
|
|
2353
|
+
<input class="folder-picker-path-input" type="text" v-model="folderPickerPath" @keydown.enter="folderPickerGoToPath" :placeholder="t('folderPicker.pathPlaceholder')" spellcheck="false" />
|
|
2328
2354
|
</div>
|
|
2329
2355
|
<div class="folder-picker-list">
|
|
2330
2356
|
<div v-if="folderPickerLoading" class="folder-picker-loading">
|
|
2331
2357
|
<div class="history-loading-spinner"></div>
|
|
2332
|
-
<span>
|
|
2333
|
-
</div>
|
|
2358
|
+
<span>{{ t('preview.loading') }}</span> </div>
|
|
2334
2359
|
<template v-else>
|
|
2335
2360
|
<div
|
|
2336
2361
|
v-for="entry in folderPickerEntries" :key="entry.name"
|
|
@@ -2341,12 +2366,12 @@ const App = {
|
|
|
2341
2366
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>
|
|
2342
2367
|
<span>{{ entry.name }}</span>
|
|
2343
2368
|
</div>
|
|
2344
|
-
<div v-if="folderPickerEntries.length === 0" class="folder-picker-empty">
|
|
2369
|
+
<div v-if="folderPickerEntries.length === 0" class="folder-picker-empty">{{ t('folderPicker.noSubdirs') }}</div>
|
|
2345
2370
|
</template>
|
|
2346
2371
|
</div>
|
|
2347
2372
|
<div class="folder-picker-footer">
|
|
2348
|
-
<button class="folder-picker-cancel" @click="folderPickerOpen = false">
|
|
2349
|
-
<button class="folder-picker-confirm" @click="confirmFolderPicker" :disabled="!folderPickerPath">
|
|
2373
|
+
<button class="folder-picker-cancel" @click="folderPickerOpen = false">{{ t('folderPicker.cancel') }}</button>
|
|
2374
|
+
<button class="folder-picker-confirm" @click="confirmFolderPicker" :disabled="!folderPickerPath">{{ t('folderPicker.open') }}</button>
|
|
2350
2375
|
</div>
|
|
2351
2376
|
</div>
|
|
2352
2377
|
</div>
|
|
@@ -2354,15 +2379,15 @@ const App = {
|
|
|
2354
2379
|
<!-- Delete Session Confirmation Dialog -->
|
|
2355
2380
|
<div class="folder-picker-overlay" v-if="deleteConfirmOpen" @click.self="cancelDeleteSession">
|
|
2356
2381
|
<div class="delete-confirm-dialog">
|
|
2357
|
-
<div class="delete-confirm-header">
|
|
2382
|
+
<div class="delete-confirm-header">{{ t('dialog.deleteSession') }}</div>
|
|
2358
2383
|
<div class="delete-confirm-body">
|
|
2359
|
-
<p>
|
|
2384
|
+
<p>{{ t('dialog.deleteSessionConfirm') }}</p>
|
|
2360
2385
|
<p class="delete-confirm-title">{{ deleteConfirmTitle }}</p>
|
|
2361
|
-
<p class="delete-confirm-warning">
|
|
2386
|
+
<p class="delete-confirm-warning">{{ t('dialog.cannotUndo') }}</p>
|
|
2362
2387
|
</div>
|
|
2363
2388
|
<div class="delete-confirm-footer">
|
|
2364
|
-
<button class="folder-picker-cancel" @click="cancelDeleteSession">
|
|
2365
|
-
<button class="delete-confirm-btn" @click="confirmDeleteSession">
|
|
2389
|
+
<button class="folder-picker-cancel" @click="cancelDeleteSession">{{ t('dialog.cancel') }}</button>
|
|
2390
|
+
<button class="delete-confirm-btn" @click="confirmDeleteSession">{{ t('dialog.delete') }}</button>
|
|
2366
2391
|
</div>
|
|
2367
2392
|
</div>
|
|
2368
2393
|
</div>
|
|
@@ -2370,15 +2395,15 @@ const App = {
|
|
|
2370
2395
|
<!-- Delete Team Confirmation Dialog -->
|
|
2371
2396
|
<div class="folder-picker-overlay" v-if="deleteTeamConfirmOpen" @click.self="cancelDeleteTeam">
|
|
2372
2397
|
<div class="delete-confirm-dialog">
|
|
2373
|
-
<div class="delete-confirm-header">
|
|
2398
|
+
<div class="delete-confirm-header">{{ t('dialog.deleteTeam') }}</div>
|
|
2374
2399
|
<div class="delete-confirm-body">
|
|
2375
|
-
<p>
|
|
2400
|
+
<p>{{ t('dialog.deleteTeamConfirm') }}</p>
|
|
2376
2401
|
<p class="delete-confirm-title">{{ deleteTeamConfirmTitle }}</p>
|
|
2377
|
-
<p class="delete-confirm-warning">
|
|
2402
|
+
<p class="delete-confirm-warning">{{ t('dialog.cannotUndo') }}</p>
|
|
2378
2403
|
</div>
|
|
2379
2404
|
<div class="delete-confirm-footer">
|
|
2380
|
-
<button class="folder-picker-cancel" @click="cancelDeleteTeam">
|
|
2381
|
-
<button class="delete-confirm-btn" @click="confirmDeleteTeam">
|
|
2405
|
+
<button class="folder-picker-cancel" @click="cancelDeleteTeam">{{ t('dialog.cancel') }}</button>
|
|
2406
|
+
<button class="delete-confirm-btn" @click="confirmDeleteTeam">{{ t('dialog.delete') }}</button>
|
|
2382
2407
|
</div>
|
|
2383
2408
|
</div>
|
|
2384
2409
|
</div>
|
|
@@ -2388,23 +2413,23 @@ const App = {
|
|
|
2388
2413
|
<div class="auth-dialog">
|
|
2389
2414
|
<div class="auth-dialog-header">
|
|
2390
2415
|
<svg viewBox="0 0 24 24" width="22" height="22"><path fill="currentColor" d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>
|
|
2391
|
-
<span>
|
|
2416
|
+
<span>{{ t('auth.sessionProtected') }}</span>
|
|
2392
2417
|
</div>
|
|
2393
2418
|
<div class="auth-dialog-body">
|
|
2394
|
-
<p>
|
|
2419
|
+
<p>{{ t('auth.passwordRequired') }}</p>
|
|
2395
2420
|
<input
|
|
2396
2421
|
type="password"
|
|
2397
2422
|
class="auth-password-input"
|
|
2398
2423
|
v-model="authPassword"
|
|
2399
2424
|
@keydown.enter="submitPassword"
|
|
2400
|
-
placeholder="
|
|
2425
|
+
:placeholder="t('auth.passwordPlaceholder')"
|
|
2401
2426
|
autofocus
|
|
2402
2427
|
/>
|
|
2403
2428
|
<p v-if="authError" class="auth-error">{{ authError }}</p>
|
|
2404
2429
|
<p v-if="authAttempts" class="auth-attempts">{{ authAttempts }}</p>
|
|
2405
2430
|
</div>
|
|
2406
2431
|
<div class="auth-dialog-footer">
|
|
2407
|
-
<button class="auth-submit-btn" @click="submitPassword" :disabled="!authPassword.trim()">
|
|
2432
|
+
<button class="auth-submit-btn" @click="submitPassword" :disabled="!authPassword.trim()">{{ t('auth.unlock') }}</button>
|
|
2408
2433
|
</div>
|
|
2409
2434
|
</div>
|
|
2410
2435
|
</div>
|
|
@@ -2414,11 +2439,11 @@ const App = {
|
|
|
2414
2439
|
<div class="auth-dialog auth-dialog-locked">
|
|
2415
2440
|
<div class="auth-dialog-header">
|
|
2416
2441
|
<svg viewBox="0 0 24 24" width="22" height="22"><path fill="currentColor" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/></svg>
|
|
2417
|
-
<span>
|
|
2442
|
+
<span>{{ t('auth.accessLocked') }}</span>
|
|
2418
2443
|
</div>
|
|
2419
2444
|
<div class="auth-dialog-body">
|
|
2420
2445
|
<p>{{ authError }}</p>
|
|
2421
|
-
<p class="auth-locked-hint">
|
|
2446
|
+
<p class="auth-locked-hint">{{ t('auth.tryAgainLater') }}</p>
|
|
2422
2447
|
</div>
|
|
2423
2448
|
</div>
|
|
2424
2449
|
</div>
|
|
@@ -2427,7 +2452,7 @@ const App = {
|
|
|
2427
2452
|
<Transition name="fade">
|
|
2428
2453
|
<div v-if="workdirSwitching" class="workdir-switching-overlay">
|
|
2429
2454
|
<div class="workdir-switching-spinner"></div>
|
|
2430
|
-
<div class="workdir-switching-text">
|
|
2455
|
+
<div class="workdir-switching-text">{{ t('workdir.switching') }}</div>
|
|
2431
2456
|
</div>
|
|
2432
2457
|
</Transition>
|
|
2433
2458
|
|
|
@@ -2439,15 +2464,15 @@ const App = {
|
|
|
2439
2464
|
>
|
|
2440
2465
|
<div class="file-context-item" @click="fileBrowser.askClaudeRead()">
|
|
2441
2466
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM5 15h14v2H5zm0-4h14v2H5zm0-4h14v2H5z"/></svg>
|
|
2442
|
-
|
|
2467
|
+
{{ t('contextMenu.askClaudeRead') }}
|
|
2443
2468
|
</div>
|
|
2444
2469
|
<div class="file-context-item" @click="fileBrowser.copyPath()">
|
|
2445
2470
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
|
|
2446
|
-
{{ fileContextMenu.copied ? '
|
|
2471
|
+
{{ fileContextMenu.copied ? t('contextMenu.copied') : t('contextMenu.copyPath') }}
|
|
2447
2472
|
</div>
|
|
2448
2473
|
<div class="file-context-item" @click="fileBrowser.insertPath()">
|
|
2449
2474
|
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/></svg>
|
|
2450
|
-
|
|
2475
|
+
{{ t('contextMenu.insertPath') }}
|
|
2451
2476
|
</div>
|
|
2452
2477
|
</div>
|
|
2453
2478
|
</div>
|