codexmate 0.0.31 → 0.0.33
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/README.md +92 -308
- package/README.zh.md +94 -318
- package/cli/local-bridge.js +227 -0
- package/cli/update.js +162 -0
- package/cli.js +357 -112
- package/lib/cli-sessions.js +16 -6
- package/lib/win-tray.js +119 -0
- package/package.json +2 -2
- package/web-ui/app.js +4 -0
- package/web-ui/logic.sessions.mjs +17 -1
- package/web-ui/modules/app.computed.session.mjs +51 -315
- package/web-ui/modules/app.methods.agents.mjs +19 -0
- package/web-ui/modules/app.methods.claude-config.mjs +71 -2
- package/web-ui/modules/app.methods.codex-config.mjs +20 -0
- package/web-ui/modules/app.methods.providers.mjs +53 -7
- package/web-ui/modules/app.methods.session-actions.mjs +1 -1
- package/web-ui/modules/app.methods.session-browser.mjs +29 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
- package/web-ui/modules/i18n.dict.mjs +21 -3
- package/web-ui/partials/index/layout-header.html +1 -2
- package/web-ui/partials/index/modal-config-template-agents.html +12 -1
- package/web-ui/partials/index/modals-basic.html +14 -3
- package/web-ui/partials/index/panel-config-claude.html +57 -85
- package/web-ui/partials/index/panel-config-codex.html +60 -226
- package/web-ui/partials/index/panel-dashboard.html +0 -33
- package/web-ui/partials/index/panel-docs.html +21 -53
- package/web-ui/partials/index/panel-sessions.html +37 -20
- package/web-ui/partials/index/panel-trash.html +33 -38
- package/web-ui/partials/index/panel-usage.html +71 -304
- package/web-ui/styles/controls-forms.css +11 -0
- package/web-ui/styles/docs-panel.css +57 -83
- package/web-ui/styles/layout-shell.css +26 -24
- package/web-ui/styles/modals-core.css +33 -0
- package/web-ui/styles/responsive.css +5 -67
- package/web-ui/styles/sessions-list.css +274 -8
- package/web-ui/styles/sessions-toolbar-trash.css +185 -15
- package/web-ui/styles/sessions-usage.css +336 -788
|
@@ -6,303 +6,84 @@
|
|
|
6
6
|
role="tabpanel"
|
|
7
7
|
:aria-labelledby="'tab-usage'">
|
|
8
8
|
<div class="usage-toolbar">
|
|
9
|
-
<
|
|
10
|
-
<span class="selector-title">{{ t('usage.overview') }}</span>
|
|
11
|
-
</div>
|
|
9
|
+
<span class="usage-toolbar-title">{{ t('usage.overview') }}</span>
|
|
12
10
|
<div class="usage-range-group" role="group" :aria-label="t('usage.range.aria')">
|
|
13
11
|
<button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '7d' }" @click="setSessionsUsageTimeRange('7d')">{{ t('usage.range.7d') }}</button>
|
|
14
12
|
<button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '30d' }" @click="setSessionsUsageTimeRange('30d')">{{ t('usage.range.30d') }}</button>
|
|
15
13
|
<button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === 'all' }" @click="setSessionsUsageTimeRange('all')">{{ t('usage.range.all') }}</button>
|
|
16
|
-
<button
|
|
17
|
-
|
|
18
|
-
class="
|
|
19
|
-
:class="{ active: sessionsUsageCompareEnabled }"
|
|
20
|
-
@click="toggleSessionsUsageCompare"
|
|
21
|
-
:disabled="sessionsUsageTimeRange === 'all'">
|
|
22
|
-
{{ t('usage.compare.toggle') }}
|
|
14
|
+
<button type="button" class="usage-range-btn usage-range-btn-compare" :class="{ active: sessionsUsageCompareEnabled }" @click="toggleSessionsUsageCompare" :disabled="sessionsUsageTimeRange === 'all'">{{ t('usage.compare.toggle') }}</button>
|
|
15
|
+
<button type="button" class="usage-range-btn usage-range-btn-icon" @click="loadSessionsUsage({ forceRefresh: true, range: sessionsUsageTimeRange })" :disabled="sessionsUsageLoading" :title="t('usage.refresh')">
|
|
16
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="btn-icon-sm"><path d="M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
|
|
23
17
|
</button>
|
|
24
|
-
<button type="button" class="usage-range-btn" @click="loadSessionsUsage({ forceRefresh: true, range: sessionsUsageTimeRange })" :disabled="sessionsUsageLoading">{{ sessionsUsageLoading ? t('usage.refreshing') : t('usage.refresh') }}</button>
|
|
25
|
-
<button type="button" class="usage-range-btn" @click="switchMainTab('dashboard')">{{ t('dashboard.doctor.title') }}</button>
|
|
26
18
|
</div>
|
|
27
19
|
</div>
|
|
28
20
|
|
|
29
21
|
<div v-if="sessionsUsageLoading && !sessionsUsageList.length" class="session-empty">{{ t('usage.loading') }}</div>
|
|
30
|
-
<div v-if="sessionsUsageLoading && !sessionsUsageList.length" class="usage-loading-skeleton" aria-busy="true">
|
|
31
|
-
<div class="usage-summary-grid">
|
|
32
|
-
<div v-for="n in 8" :key="'usage-skeleton-card-' + n" class="usage-summary-card usage-skeleton-card">
|
|
33
|
-
<div class="usage-skeleton-line w-40"></div>
|
|
34
|
-
<div class="usage-skeleton-line w-70 h-lg"></div>
|
|
35
|
-
<div class="usage-skeleton-line w-55"></div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="usage-chart-grid">
|
|
39
|
-
<section class="usage-card usage-card-wide usage-skeleton-block"></section>
|
|
40
|
-
<section class="usage-card usage-skeleton-block"></section>
|
|
41
|
-
<section class="usage-card usage-skeleton-block"></section>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
22
|
<div v-else-if="sessionsUsageError && !sessionsUsageList.length" class="usage-empty">{{ sessionsUsageError }}</div>
|
|
45
23
|
<div v-else-if="!sessionsUsageList.length" class="usage-empty">{{ t('usage.empty') }}</div>
|
|
46
24
|
<template v-else>
|
|
47
25
|
<div class="usage-content" :class="{ 'usage-content-loading': sessionsUsageLoading }">
|
|
48
26
|
<div v-if="sessionsUsageLoading" class="usage-content-overlay" aria-live="polite">
|
|
49
27
|
<span class="usage-spinner" aria-hidden="true"></span>
|
|
50
|
-
{{ t('usage.refreshOverlay') }}
|
|
51
28
|
</div>
|
|
52
29
|
|
|
53
30
|
<div v-if="usageCurrentSessionStats" class="usage-current-session-bar">
|
|
31
|
+
<span class="usage-current-session-dot"></span>
|
|
54
32
|
<span class="usage-current-session-label">{{ usageCurrentSessionStats.label }}</span>
|
|
55
|
-
<span class="usage-current-session-
|
|
56
|
-
<span class="usage-current-session-
|
|
57
|
-
<span class="usage-current-session-
|
|
33
|
+
<span class="usage-current-session-stat">{{ usageCurrentSessionStats.tokenLabel }} tokens</span>
|
|
34
|
+
<span class="usage-current-session-stat">{{ usageCurrentSessionStats.apiDurationLabel }} API</span>
|
|
35
|
+
<span class="usage-current-session-stat">{{ usageCurrentSessionStats.totalDurationLabel }} total</span>
|
|
58
36
|
</div>
|
|
59
37
|
|
|
60
38
|
<div class="usage-summary-grid">
|
|
61
|
-
<div
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class="usage-summary-card usage-copyable"
|
|
65
|
-
role="button"
|
|
66
|
-
tabindex="0"
|
|
67
|
-
:title="(card.title || card.value) ? t('usage.copyTitle', { value: (card.title || card.value) }) : null"
|
|
68
|
-
@click="(String(card.title || card.value || '').trim())
|
|
69
|
-
? ((typeof fallbackCopyText === 'function' && fallbackCopyText(String(card.title || card.value || '').trim()))
|
|
70
|
-
? showMessage(t('usage.copySuccess', { label: card.label }), 'success')
|
|
71
|
-
: showMessage(t('usage.copyFail'), 'error'))
|
|
72
|
-
: showMessage(t('usage.copyNone'), 'info')"
|
|
73
|
-
@keydown.enter.prevent="(String(card.title || card.value || '').trim())
|
|
74
|
-
? ((typeof fallbackCopyText === 'function' && fallbackCopyText(String(card.title || card.value || '').trim()))
|
|
75
|
-
? showMessage(t('usage.copySuccess', { label: card.label }), 'success')
|
|
76
|
-
: showMessage(t('usage.copyFail'), 'error'))
|
|
77
|
-
: showMessage(t('usage.copyNone'), 'info')"
|
|
78
|
-
@keydown.space.prevent="(String(card.title || card.value || '').trim())
|
|
79
|
-
? ((typeof fallbackCopyText === 'function' && fallbackCopyText(String(card.title || card.value || '').trim()))
|
|
80
|
-
? showMessage(t('usage.copySuccess', { label: card.label }), 'success')
|
|
81
|
-
: showMessage(t('usage.copyFail'), 'error'))
|
|
82
|
-
: showMessage(t('usage.copyNone'), 'info')">
|
|
83
|
-
<div class="usage-summary-label">{{ card.label }}</div>
|
|
84
|
-
<div class="usage-summary-value" :title="card.title || null">{{ card.value }}</div>
|
|
85
|
-
<div v-if="card.note" class="usage-summary-note">{{ card.note }}</div>
|
|
39
|
+
<div v-for="card in sessionUsageSummaryCards" :key="card.key" class="usage-summary-card" :title="card.title || ''">
|
|
40
|
+
<div class="usage-summary-card-value">{{ card.value }}</div>
|
|
41
|
+
<div class="usage-summary-card-label">{{ card.label }}</div>
|
|
86
42
|
</div>
|
|
87
43
|
</div>
|
|
88
44
|
|
|
89
|
-
<
|
|
90
|
-
<section class="usage-card usage-card-daydetail">
|
|
45
|
+
<section v-if="sessionUsageDaily && sessionUsageDaily.rows.length" class="usage-card">
|
|
91
46
|
<div class="usage-card-head">
|
|
92
|
-
<div>
|
|
93
|
-
|
|
94
|
-
<div class="usage-card-subtitle">{{ t('usage.dayDetail.subtitle') }}</div>
|
|
95
|
-
</div>
|
|
96
|
-
<div class="usage-daydetail-controls">
|
|
97
|
-
<select
|
|
98
|
-
class="usage-daydetail-select"
|
|
99
|
-
:value="sessionsUsageSelectedDayKey || (sessionUsageDailyTableRows[0] && sessionUsageDailyTableRows[0].key) || ''"
|
|
100
|
-
@change="selectSessionsUsageDay(($event.target && $event.target.value) ? String($event.target.value) : '')">
|
|
101
|
-
<option v-for="day in sessionUsageDailyTableRows" :key="'day-select-' + day.key" :value="day.key">{{ day.key }}</option>
|
|
102
|
-
</select>
|
|
103
|
-
<button
|
|
104
|
-
type="button"
|
|
105
|
-
class="btn-tool btn-tool-compact"
|
|
106
|
-
:disabled="!sessionsUsageSelectedDaySummary"
|
|
107
|
-
@click="clearSessionsUsageDay">{{ t('usage.dayDetail.clear') }}</button>
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
<template v-if="sessionsUsageSelectedDaySummary">
|
|
111
|
-
<div class="usage-daydetail-grid">
|
|
112
|
-
<div class="usage-daydetail-metric">
|
|
113
|
-
<div class="usage-daydetail-label">{{ t('usage.table.sessions') }}</div>
|
|
114
|
-
<div class="usage-daydetail-value">{{ sessionsUsageSelectedDaySummary.sessionCount }}</div>
|
|
115
|
-
</div>
|
|
116
|
-
<div class="usage-daydetail-metric">
|
|
117
|
-
<div class="usage-daydetail-label">{{ t('usage.table.messages') }}</div>
|
|
118
|
-
<div class="usage-daydetail-value">{{ sessionsUsageSelectedDaySummary.messageCount }}</div>
|
|
119
|
-
</div>
|
|
120
|
-
<div class="usage-daydetail-metric">
|
|
121
|
-
<div class="usage-daydetail-label">{{ t('usage.table.tokens') }}</div>
|
|
122
|
-
<div class="usage-daydetail-value mono">{{ sessionsUsageSelectedDaySummary.tokenLabel }}</div>
|
|
123
|
-
<div v-if="sessionsUsageSelectedDaySummary.compareEnabled" class="usage-daydetail-sub mono">
|
|
124
|
-
{{ t('usage.compare.prev') }} {{ sessionsUsageSelectedDaySummary.prevTokenLabel }} · {{ t('usage.compare.delta') }} {{ sessionsUsageSelectedDaySummary.deltaTokenLabel }}
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
<div class="usage-daydetail-metric">
|
|
128
|
-
<div class="usage-daydetail-label">{{ t('usage.table.cost') }}</div>
|
|
129
|
-
<div class="usage-daydetail-value mono">{{ sessionsUsageSelectedDaySummary.costLabel }}</div>
|
|
130
|
-
<div v-if="sessionsUsageSelectedDaySummary.compareEnabled" class="usage-daydetail-sub mono">
|
|
131
|
-
{{ t('usage.compare.prev') }} {{ sessionsUsageSelectedDaySummary.prevCostLabel }} · {{ t('usage.compare.delta') }} {{ sessionsUsageSelectedDaySummary.deltaCostLabel }}
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
<div class="usage-daydetail-section">
|
|
136
|
-
<div class="usage-daydetail-section-title">{{ t('usage.dayDetail.topSessions') }}</div>
|
|
137
|
-
<div class="usage-daydetail-list">
|
|
138
|
-
<div v-for="item in sessionsUsageSelectedDaySummary.topSessions" :key="item.key" class="usage-daydetail-row">
|
|
139
|
-
<div class="usage-daydetail-row-title">{{ item.title }}</div>
|
|
140
|
-
<div class="usage-daydetail-row-meta mono">{{ item.messageCountLabel }}</div>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
<div v-if="sessionsUsageSelectedDaySummary.topModels.length" class="usage-daydetail-section">
|
|
145
|
-
<div class="usage-daydetail-section-title">{{ t('usage.dayDetail.topModels') }}</div>
|
|
146
|
-
<div class="usage-daydetail-models">
|
|
147
|
-
<span v-for="model in sessionsUsageSelectedDaySummary.topModels" :key="model.key" class="usage-daydetail-model">{{ model.label }}</span>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</template>
|
|
151
|
-
<div v-else class="usage-daydetail-empty">
|
|
152
|
-
{{ t('usage.dayDetail.empty') }}
|
|
153
|
-
</div>
|
|
154
|
-
</section>
|
|
155
|
-
|
|
156
|
-
<section class="usage-card">
|
|
157
|
-
<div class="usage-card-title">{{ t('usage.trend.sessions') }}</div>
|
|
158
|
-
<div class="usage-legend">
|
|
159
|
-
<span><span class="usage-legend-dot" style="background:#4f8cff"></span>{{ t('usage.legend.codex') }}</span>
|
|
160
|
-
<span><span class="usage-legend-dot" style="background:#b277ff"></span>{{ t('usage.legend.claude') }}</span>
|
|
161
|
-
</div>
|
|
162
|
-
<div class="usage-bars">
|
|
163
|
-
<div v-for="bucket in sessionUsageCharts.buckets" :key="bucket.key" class="usage-bar-group">
|
|
164
|
-
<div class="usage-bar-stack">
|
|
165
|
-
<div class="usage-bar codex" :style="{ height: ((bucket.codex / Math.max(sessionUsageCharts.maxSessionBucket, 1)) * 100) + '%' }" :title="t('usage.trend.sessions.codexTitle', { count: bucket.codex })"></div>
|
|
166
|
-
<div class="usage-bar claude" :style="{ height: ((bucket.claude / Math.max(sessionUsageCharts.maxSessionBucket, 1)) * 100) + '%' }" :title="t('usage.trend.sessions.claudeTitle', { count: bucket.claude })"></div>
|
|
167
|
-
</div>
|
|
168
|
-
<div class="usage-bar-label">{{ bucket.label }}</div>
|
|
169
|
-
</div>
|
|
170
|
-
</div>
|
|
171
|
-
</section>
|
|
172
|
-
|
|
173
|
-
<section class="usage-card">
|
|
174
|
-
<div class="usage-card-title">{{ t('usage.trend.messages') }}</div>
|
|
175
|
-
<div class="usage-bars">
|
|
176
|
-
<div v-for="bucket in sessionUsageCharts.buckets" :key="bucket.key + '-messages'" class="usage-bar-group">
|
|
177
|
-
<div class="usage-bar-stack">
|
|
178
|
-
<div class="usage-bar codex" style="width:100%" :style="{ height: ((bucket.totalMessages / Math.max(sessionUsageCharts.maxMessageBucket, 1)) * 100) + '%' }" :title="t('usage.trend.messages.barTitle', { count: bucket.totalMessages })"></div>
|
|
179
|
-
</div>
|
|
180
|
-
<div class="usage-bar-label">{{ bucket.label }}</div>
|
|
181
|
-
</div>
|
|
47
|
+
<div class="usage-card-title">{{ t('usage.daily.title') }}</div>
|
|
48
|
+
<div class="usage-card-subtitle">{{ t('usage.daily.subtitle') }}</div>
|
|
182
49
|
</div>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<div v-for="item in sessionUsageCharts.hourActivity" :key="item.key" class="usage-mini-bar-group">
|
|
189
|
-
<div class="usage-mini-bar-track" :title="t('usage.hour.title', { hour: item.label, count: item.count })">
|
|
190
|
-
<div class="usage-mini-bar-fill" :style="{ height: item.percent + '%' }"></div>
|
|
50
|
+
<div class="usage-daily-chart">
|
|
51
|
+
<div v-for="day in sessionUsageDaily.rows" :key="day.key" class="usage-daily-bar-group" @click="selectSessionsUsageDay(day.key)" :class="{ active: sessionsUsageSelectedDay === day.key }">
|
|
52
|
+
<div class="usage-daily-bar-stack">
|
|
53
|
+
<div class="usage-daily-bar-fill" :style="{ height: day.tokenPercent + '%' }"></div>
|
|
54
|
+
<div v-if="day.compareEnabled" class="usage-daily-bar-prev" :style="{ height: day.prevTokenPercent + '%' }"></div>
|
|
191
55
|
</div>
|
|
192
|
-
<div class="usage-
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
</section>
|
|
196
|
-
|
|
197
|
-
<section class="usage-card">
|
|
198
|
-
<div class="usage-card-title">{{ t('usage.trend.sources') }}</div>
|
|
199
|
-
<div class="usage-list">
|
|
200
|
-
<div v-for="item in sessionUsageCharts.sourceShare" :key="item.key" class="usage-list-row">
|
|
201
|
-
<div class="usage-list-label">{{ item.label }}</div>
|
|
202
|
-
<div class="usage-progress"><div class="usage-progress-fill" :style="{ width: item.percent + '%' }"></div></div>
|
|
203
|
-
<div class="usage-list-value">{{ item.percent }}%</div>
|
|
204
|
-
<div class="usage-list-subvalue">{{ t('usage.source.row', { sessions: item.value, messages: item.messageTotal, avg: item.avgMessages }) }}</div>
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
207
|
-
</section>
|
|
208
|
-
|
|
209
|
-
<section class="usage-card usage-card-models">
|
|
210
|
-
<div class="usage-card-head">
|
|
211
|
-
<div>
|
|
212
|
-
<div class="usage-card-title">{{ t('usage.models.title') }}</div>
|
|
213
|
-
<div class="usage-card-subtitle">{{ t('usage.models.subtitle') }}</div>
|
|
214
|
-
</div>
|
|
215
|
-
<div class="usage-card-kicker">{{ t('usage.models.kicker', { modeled: sessionUsageCharts.modelCoverage.modeledSessions, total: sessionUsageCharts.modelCoverage.totalSessions }) }}</div>
|
|
216
|
-
</div>
|
|
217
|
-
<div class="usage-model-coverage-strip">
|
|
218
|
-
<div class="usage-model-coverage-item">
|
|
219
|
-
<strong>{{ sessionUsageCharts.usedModels.length }}</strong>
|
|
220
|
-
<span>{{ t('usage.models.count') }}</span>
|
|
221
|
-
</div>
|
|
222
|
-
<div class="usage-model-coverage-item">
|
|
223
|
-
<strong>{{ sessionUsageCharts.modelCoverage.coveragePercent }}%</strong>
|
|
224
|
-
<span>{{ t('usage.models.coverage') }}</span>
|
|
225
|
-
</div>
|
|
226
|
-
<div class="usage-model-coverage-item">
|
|
227
|
-
<strong>{{ sessionUsageCharts.modelCoverage.missingModelSessions }}</strong>
|
|
228
|
-
<span>{{ t('usage.models.missing') }}</span>
|
|
56
|
+
<div class="usage-daily-bar-label">{{ day.label }}</div>
|
|
229
57
|
</div>
|
|
230
58
|
</div>
|
|
231
|
-
<div
|
|
232
|
-
<
|
|
233
|
-
<
|
|
234
|
-
{{ t('usage.models.noneBody', { total: sessionUsageCharts.modelCoverage.totalSessions }) }}
|
|
235
|
-
</div>
|
|
236
|
-
<div v-if="sessionUsageCharts.modelCoverage.providerOnlySessions > 0" class="usage-diagnostic-meta">
|
|
237
|
-
{{ t('usage.models.providerOnly', { count: sessionUsageCharts.modelCoverage.providerOnlySessions }) }}
|
|
238
|
-
</div>
|
|
59
|
+
<div class="usage-daily-legend">
|
|
60
|
+
<span class="usage-daily-legend-item"><span class="usage-daily-legend-swatch current"></span>{{ t('usage.legend.current') }}</span>
|
|
61
|
+
<span v-if="sessionsUsageCompareEnabled" class="usage-daily-legend-item"><span class="usage-daily-legend-swatch prev"></span>{{ t('usage.compare.prev') }}</span>
|
|
239
62
|
</div>
|
|
240
|
-
<template v-
|
|
241
|
-
<div
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{{ t('usage.models.missingNote', { count: sessionUsageCharts.modelCoverage.missingModelSessions }) }}
|
|
246
|
-
</div>
|
|
247
|
-
<div v-if="sessionUsageCharts.modelCoverage.missingModelSessionsPreview.length" class="usage-diagnostic-list">
|
|
248
|
-
<div class="usage-diagnostic-list-title">{{ t('usage.models.missingListTitle') }}</div>
|
|
249
|
-
<div
|
|
250
|
-
v-for="item in sessionUsageCharts.modelCoverage.missingModelSessionsPreview"
|
|
251
|
-
:key="item.key"
|
|
252
|
-
class="usage-diagnostic-row">
|
|
253
|
-
<div class="usage-diagnostic-row-main">
|
|
254
|
-
<div class="usage-diagnostic-row-title">{{ item.title }}</div>
|
|
255
|
-
<div class="usage-diagnostic-row-meta">
|
|
256
|
-
<span v-if="item.sessionId">{{ item.sessionId }}</span>
|
|
257
|
-
<span v-else-if="item.updatedAtLabel">{{ item.updatedAtLabel }}</span>
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
<div class="usage-diagnostic-row-side">
|
|
261
|
-
<span v-if="item.provider" class="usage-inline-tag">{{ item.provider }}</span>
|
|
262
|
-
<span class="usage-inline-stat">{{ item.reasonLabel }}</span>
|
|
263
|
-
</div>
|
|
63
|
+
<template v-if="sessionsUsageSelectedDaySummary">
|
|
64
|
+
<div class="usage-daydetail">
|
|
65
|
+
<div class="usage-daydetail-header">
|
|
66
|
+
<span class="usage-daydetail-date">{{ sessionsUsageSelectedDaySummary.dayKey }}</span>
|
|
67
|
+
<span class="usage-daydetail-stats">{{ sessionsUsageSelectedDaySummary.sessionCount }} sessions · {{ sessionsUsageSelectedDaySummary.tokenLabel }} tokens</span>
|
|
264
68
|
</div>
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
<div
|
|
268
|
-
v-for="item in sessionUsageCharts.usedModels"
|
|
269
|
-
:key="item.key"
|
|
270
|
-
class="usage-model-chip"
|
|
271
|
-
:title="t('usage.models.chipTitle', { model: item.model, sessions: item.count, messages: item.messageTotal, tokens: (item.tokenTotal ? (' · ' + item.tokenTotal + ' token') : '') })">
|
|
272
|
-
<div class="usage-model-name">{{ item.model }}</div>
|
|
273
|
-
<div class="usage-model-meta">{{ t('usage.models.meta', { sessions: item.count, messages: item.messageTotal, tokens: (item.tokenTotal ? (' · ' + item.tokenTotal.toLocaleString('en-US') + ' token') : '') }) }}</div>
|
|
69
|
+
<div v-if="sessionsUsageSelectedDaySummary.compareEnabled" class="usage-daydetail-compare">
|
|
70
|
+
{{ t('usage.compare.prev') }} {{ sessionsUsageSelectedDaySummary.prevTokenLabel }} tokens · {{ t('usage.compare.delta') }} {{ sessionsUsageSelectedDaySummary.deltaTokenLabel }}
|
|
274
71
|
</div>
|
|
275
72
|
</div>
|
|
276
73
|
</template>
|
|
277
74
|
</section>
|
|
278
75
|
|
|
279
|
-
<
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
<div
|
|
283
|
-
<div class="
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
<section class="usage-card usage-card-hourly-heatmap">
|
|
291
|
-
<div class="usage-card-title">{{ t('usage.hourlyHeatmap.title') }}</div>
|
|
292
|
-
<div class="usage-card-subtitle">{{ t('usage.hourlyHeatmap.subtitle') }}</div>
|
|
293
|
-
<div class="hourly-heatmap-wrapper">
|
|
294
|
-
<div class="hourly-heatmap-header">
|
|
295
|
-
<div class="hourly-heatmap-corner"></div>
|
|
296
|
-
<div v-for="h in sessionUsageHourlyHeatmap.hourLabels" :key="'hh-' + h" class="hourly-heatmap-hour-label">{{ h }}</div>
|
|
297
|
-
</div>
|
|
298
|
-
<div v-for="(row, dayIndex) in sessionUsageHourlyHeatmap.rows" :key="'hd-' + dayIndex" class="hourly-heatmap-row">
|
|
299
|
-
<div class="hourly-heatmap-weekday-label">{{ row.weekday }}</div>
|
|
300
|
-
<div
|
|
301
|
-
v-for="(cell, hourIndex) in row.cells"
|
|
302
|
-
:key="'hc-' + dayIndex + '-' + hourIndex"
|
|
303
|
-
:class="['hourly-heatmap-cell', 'level-' + cell.level]"
|
|
304
|
-
:title="cell.tooltip"
|
|
305
|
-
:aria-label="cell.tooltip">
|
|
76
|
+
<div class="usage-chart-grid">
|
|
77
|
+
<section class="usage-card usage-card-hourly-heatmap">
|
|
78
|
+
<div class="usage-card-title">{{ t('usage.hourlyHeatmap.title') }}</div>
|
|
79
|
+
<div class="hourly-heatmap-wrapper">
|
|
80
|
+
<div class="hourly-heatmap-header">
|
|
81
|
+
<div class="hourly-heatmap-corner"></div>
|
|
82
|
+
<div v-for="h in 24" :key="'hdr-' + h" class="hourly-heatmap-hour-label">{{ h - 1 }}</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div v-for="row in sessionUsageHeatmap.rows" :key="row.weekday" class="hourly-heatmap-row">
|
|
85
|
+
<div class="hourly-heatmap-weekday-label">{{ row.weekday }}</div>
|
|
86
|
+
<div v-for="cell in row.cells" :key="'cell-' + row.weekday + '-' + cell.hour" :class="['hourly-heatmap-cell', 'level-' + cell.level]" :title="cell.tooltip"></div>
|
|
306
87
|
</div>
|
|
307
88
|
</div>
|
|
308
89
|
<div class="hourly-heatmap-legend">
|
|
@@ -314,58 +95,44 @@
|
|
|
314
95
|
<span class="hourly-heatmap-cell level-4"></span>
|
|
315
96
|
<span class="hourly-heatmap-legend-label">{{ t('usage.hourlyHeatmap.legend.more') }}</span>
|
|
316
97
|
</div>
|
|
317
|
-
</
|
|
318
|
-
</section>
|
|
98
|
+
</section>
|
|
319
99
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
100
|
+
<section class="usage-card">
|
|
101
|
+
<div class="usage-card-title">{{ t('usage.paths.title') }}</div>
|
|
102
|
+
<div v-if="!sessionUsageCharts.topPaths.length" class="usage-list-value">{{ t('usage.paths.empty') }}</div>
|
|
103
|
+
<div v-else class="usage-list">
|
|
104
|
+
<div v-for="item in sessionUsageCharts.topPaths" :key="item.path" class="usage-list-row">
|
|
105
|
+
<div class="usage-list-path" :title="item.path">{{ item.path }}</div>
|
|
106
|
+
<div class="usage-list-stat">{{ item.count }}</div>
|
|
107
|
+
<div class="usage-progress"><div class="usage-progress-fill" :style="{ width: ((item.count / Math.max((sessionUsageCharts.topPaths.length ? sessionUsageCharts.topPaths[0].count : 1), 1)) * 100) + '%' }"></div></div>
|
|
108
|
+
</div>
|
|
329
109
|
</div>
|
|
330
|
-
</
|
|
331
|
-
</section>
|
|
110
|
+
</section>
|
|
332
111
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
<div class="usage-
|
|
340
|
-
<
|
|
341
|
-
</div>
|
|
342
|
-
<div class="usage-session-meta">
|
|
343
|
-
<span>{{ t('usage.sessions.messages', { count: item.messageCount }) }}</span>
|
|
344
|
-
<span v-if="item.updatedAtLabel">{{ item.updatedAtLabel }}</span>
|
|
112
|
+
<section class="usage-card">
|
|
113
|
+
<div class="usage-card-title">{{ t('usage.sessions.topDensity') }}</div>
|
|
114
|
+
<div v-if="!sessionUsageCharts.topSessionsByMessages.length" class="usage-list-value">{{ t('usage.sessions.empty') }}</div>
|
|
115
|
+
<div v-else class="usage-list">
|
|
116
|
+
<div v-for="item in sessionUsageCharts.topSessionsByMessages" :key="item.key + '-dense'" class="usage-list-row">
|
|
117
|
+
<div class="usage-list-title" :title="item.title">{{ item.title }}</div>
|
|
118
|
+
<div class="usage-list-stat">{{ item.messageCount }}</div>
|
|
119
|
+
<div class="usage-list-meta">{{ item.sourceLabel }} · {{ item.updatedAtLabel }}</div>
|
|
345
120
|
</div>
|
|
346
|
-
<div v-if="item.cwd" class="usage-session-path" :title="item.cwd">{{ item.cwd }}</div>
|
|
347
121
|
</div>
|
|
348
|
-
</
|
|
349
|
-
</section>
|
|
122
|
+
</section>
|
|
350
123
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
<
|
|
358
|
-
<div class="usage-
|
|
359
|
-
</div>
|
|
360
|
-
<div class="usage-session-meta">
|
|
361
|
-
<span>{{ item.sourceLabel }}</span>
|
|
362
|
-
<span v-if="item.updatedAtLabel">{{ item.updatedAtLabel }}</span>
|
|
124
|
+
<section class="usage-card">
|
|
125
|
+
<div class="usage-card-title">{{ t('usage.recent.title') }}</div>
|
|
126
|
+
<div v-if="!sessionUsageCharts.recentSessions.length" class="usage-list-value">{{ t('usage.sessions.empty') }}</div>
|
|
127
|
+
<div v-else class="usage-list">
|
|
128
|
+
<div v-for="item in sessionUsageCharts.recentSessions" :key="item.key" class="usage-list-row">
|
|
129
|
+
<div class="usage-list-title" :title="item.title">{{ item.title }}</div>
|
|
130
|
+
<span :class="['pill', item.source === 'codex' ? 'configured' : 'empty']">{{ item.sourceLabel }}</span>
|
|
131
|
+
<div class="usage-list-meta">{{ item.messageCount }} msgs · {{ item.updatedAtLabel }}</div>
|
|
363
132
|
</div>
|
|
364
|
-
<div v-if="item.cwd" class="usage-session-path" :title="item.cwd">{{ item.cwd }}</div>
|
|
365
133
|
</div>
|
|
366
|
-
</
|
|
367
|
-
</section>
|
|
134
|
+
</section>
|
|
368
135
|
</div>
|
|
369
136
|
</div>
|
|
370
137
|
</template>
|
|
371
|
-
</div>
|
|
138
|
+
</div>
|