Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs-site/src/content/docs/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,23 @@ one column and model/effort controls share another. On narrower screens, control
labels in the same reading order. Long version labels are shortened visually; hover the version
badge or the version value to read the full value.

### Local monitor

The **Usage → Menu bar settings** section (`#usage/monitor`) displays the optional menu-bar
monitor’s usage and settings inside the existing dashboard, without a separate native window.
The section follows **Coverage breakdown** and has **Usage** and **Providers** tabs, using the dashboard’s shared tab and select styles.
Display settings appear directly below the Usage graph; provider visibility controls live in Providers.
The full JSON editor is collapsed under Display settings until expanded.
The panel refreshes every five seconds while the tab is visible and shows token history, compact
account and provider quota bars, and summary totals. If the monitor is unavailable, the rest of
the OpenCodex dashboard continues to work.

Use the graph, provider-visibility and model-filter controls, or edit the complete configuration
as JSON under **Monitor settings**. This includes the
chart style and grouping, time range, bucket size, aggregation, token metric, model filters,
provider visibility, display text, colours, and graph dimensions. Saving uses the configuration
revision currently shown by the monitor; if somebody else changed it first, discard the stale draft and reapply your edits. Chart regeneration happens asynchronously after a successful save.

## What you can do

| Area | What it does |
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions gui/.eslint/i18n-allowlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const TECHNICAL_UNITS = new Set([
"ms",
"k",
"1M",
"M", // Decimal token unit used by the native monitor.
"c",
"w",
"v",
Expand All @@ -53,6 +54,8 @@ const TECHNICAL_UNITS = new Set([

/** Non-UI technical strings (API paths, CSS, shell, headers, debug fields). */
export function isTechnicalLiteral(value: string): boolean {
// Literal template field suffixes, not translatable UI copy.
if ([".raw", ".compact", ".formatted"].includes(value)) return true;
const trimmed = value.trim();
if (!trimmed) return true;

Expand Down
5 changes: 5 additions & 0 deletions gui/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function readPageFromHash(hash?: string): Page {
const raw = normalizeHashPath(
hash ?? (typeof window !== "undefined" ? window.location.hash : ""),
);
if (raw === "monitor" || raw.startsWith("monitor/")) return "usage";
// Sub-views use a "/" suffix (e.g. #logs/debug); the first segment is the page id.
const pageId = raw.split("/")[0] as Page;
// Legacy: Debug used to be a standalone page; it now lives as a tab on Logs.
Expand Down Expand Up @@ -109,6 +110,7 @@ export const INTEGRATION_TAB_HASHES = [

export function hashBelongsToPage(rawHash: string, page: Page): boolean {
return rawHash === page
|| (page === "usage" && ["usage/monitor", "usage/monitor/providers", "usage/monitor/settings"].includes(rawHash))
|| (page === "logs" && rawHash === "logs/debug")
|| (page === "codex-set" && rawHash === "codex-set/prompt")
|| (page === "models" && (MODELS_TAB_HASHES as readonly string[]).includes(rawHash))
Expand All @@ -132,6 +134,9 @@ export type AppHashChangeAction = {
*/
export function resolveAppHashChange(rawHash: string): AppHashChangeAction {
const nextPage = readPageFromHash(rawHash);
if (rawHash === "monitor" || rawHash.startsWith("monitor/")) {
return { page: "usage", replaceTo: rawHash === "monitor/providers" ? "usage/monitor/providers" : "usage/monitor" };
}

// Legacy: Debug used to be a standalone page.
if (rawHash === "debug" || rawHash.startsWith("debug/")) {
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,49 @@ import type { TKey } from "./en";
* German i18n catalog, generated from en.ts. Must match the `TKey` set (compile-checked).
*/
export const de: Record<TKey, string> = {
"monitor.menuBarSettings": "Menüleisteneinstellungen",
"monitor.averageNote": "Values are per-series request averages/maxima; stacked height is not the average/maximum of all requests.",
"nav.monitor": "Graphs · accounts · display",
"monitor.showToday": "Today’s usage",
"monitor.showAccounts": "Account and provider usage",
"monitor.showModels": "Today’s model list",
"monitor.showCost": "Estimated cost",
"monitor.modelFilter": "Graph model filter",
"monitor.allModels": "All models (include new models)",
"monitor.displaySettings": "Anzeigeeinstellungen",
"monitor.title": "Local monitor",
"monitor.graph": "Token usage graph",
"monitor.empty": "No usage recorded for this range.",
"monitor.shortQuota": "Short window",
"monitor.weeklyQuota": "Weekly",
"monitor.settings": "Monitor settings",
"monitor.config": "Configuration JSON",
"monitor.unavailable": "The local monitor is unavailable.",
"monitor.invalidConfig": "Configuration must be valid JSON.",
"monitor.stale": "Settings changed elsewhere. Refresh and save again.",
"monitor.saveFailed": "Could not save monitor settings.",
"monitor.chartStyle": "Chart style",
"monitor.line": "Line",
"monitor.stackedBar": "Stacked bars",
"monitor.grouping": "Group by",
"monitor.model": "Model",
"monitor.modelAccount": "Model and account",
"monitor.hours": "History",
"monitor.bucket": "Bucket minutes",
"monitor.aggregation": "Aggregation",
"monitor.sum": "Sum",
"monitor.average": "Average",
"monitor.max": "Maximum",
"monitor.metric": "Token metric",
"monitor.total": "Total",
"monitor.input": "Input",
"monitor.output": "Output",
"monitor.cached": "Cached",
"monitor.display": "Show graph",
"monitor.on": "On",
"monitor.off": "Off",
"monitor.tokensAxis": "Tokens",
"monitor.timeAxis": "Time",
"usage.incomplete": "Einige Nutzungsdatensätze konnten nicht berücksichtigt werden. Anzahlen, Datumsangaben und Ranglisten beruhen nur auf lesbaren Datensätzen.",
"models.pickerOrder.usageIncomplete": "Die Reihenfolge nach Nutzung kann wegen unvollständiger Nutzungsdaten nicht gespeichert werden. Wählen Sie eine andere Reihenfolge oder reparieren Sie zuerst den Verlauf.",
"api.attribution.noRecordedUse": "Keine Nutzung in lesbaren Datensätzen",
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,49 @@
* `{var}` are plain interpolations.
*/
export const en = {
"monitor.menuBarSettings": "Menu bar settings",
"monitor.averageNote": "Values are per-series request averages/maxima; stacked height is not the average/maximum of all requests.",
"nav.monitor": "Graphs · accounts · display",
"monitor.showToday": "Today’s usage",
"monitor.showAccounts": "Account and provider usage",
"monitor.showModels": "Today’s model list",
"monitor.showCost": "Estimated cost",
"monitor.modelFilter": "Graph model filter",
"monitor.allModels": "All models (include new models)",
"monitor.displaySettings": "Display settings",
"monitor.title": "Local monitor",
"monitor.graph": "Token usage graph",
"monitor.empty": "No usage recorded for this range.",
"monitor.shortQuota": "Short window",
"monitor.weeklyQuota": "Weekly",
"monitor.settings": "Monitor settings",
"monitor.config": "Configuration JSON",
"monitor.unavailable": "The local monitor is unavailable.",
"monitor.invalidConfig": "Configuration must be valid JSON.",
"monitor.stale": "Settings changed elsewhere. Refresh and save again.",
"monitor.saveFailed": "Could not save monitor settings.",
"monitor.chartStyle": "Chart style",
"monitor.line": "Line",
"monitor.stackedBar": "Stacked bars",
"monitor.grouping": "Group by",
"monitor.model": "Model",
"monitor.modelAccount": "Model and account",
"monitor.hours": "History",
"monitor.bucket": "Bucket minutes",
"monitor.aggregation": "Aggregation",
"monitor.sum": "Sum",
"monitor.average": "Average",
"monitor.max": "Maximum",
"monitor.metric": "Token metric",
"monitor.total": "Total",
"monitor.input": "Input",
"monitor.output": "Output",
"monitor.cached": "Cached",
"monitor.display": "Show graph",
"monitor.on": "On",
"monitor.off": "Off",
"monitor.tokensAxis": "Tokens",
"monitor.timeAxis": "Time",
"usage.incomplete": "Some usage records could not be included. Counts, dates, and rankings reflect readable records only.",
"models.pickerOrder.usageIncomplete": "Cannot save most-used order because usage history is incomplete. Choose another order or repair the history first.",
"api.attribution.noRecordedUse": "No use in readable records",
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@ import type { TKey } from "./en";
* French i18n catalog. Must match the `TKey` set.
*/
export const fr: Record<TKey, string> = {
"monitor.menuBarSettings": "Paramètres de la barre des menus",
"monitor.averageNote": "Les valeurs sont des moyennes ou maxima par requête et par série ; la hauteur empilée ne représente pas la moyenne ou le maximum de toutes les requêtes.",
"nav.monitor": "Graphiques, comptes et affichage",
"monitor.showToday": "Utilisation du jour",
"monitor.showAccounts": "Utilisation des comptes et fournisseurs",
"monitor.showModels": "Modèles utilisés aujourd’hui",
"monitor.showCost": "Coût estimé",
"monitor.modelFilter": "Filtrer les modèles du graphique",
"monitor.allModels": "Tous les modèles, y compris les nouveaux",
"monitor.displaySettings": "Paramètres d’affichage",
"monitor.title": "Moniteur local",
"monitor.graph": "Graphique des jetons utilisés",
"monitor.empty": "Aucune utilisation enregistrée sur cette période.",
"monitor.shortQuota": "Période courte",
"monitor.weeklyQuota": "Hebdomadaire",
"monitor.settings": "Paramètres du moniteur",
"monitor.config": "Configuration au format JSON",
"monitor.unavailable": "Le moniteur local est indisponible.",
"monitor.invalidConfig": "La configuration doit être un document JSON valide.",
"monitor.stale": "Les paramètres ont été modifiés ailleurs. Actualisez puis enregistrez à nouveau.",
"monitor.saveFailed": "Impossible d’enregistrer les paramètres du moniteur.",
"monitor.chartStyle": "Type de graphique",
"monitor.line": "Courbe",
"monitor.stackedBar": "Barres empilées",
"monitor.grouping": "Regrouper par",
"monitor.model": "Modèle",
"monitor.modelAccount": "Modèle et compte",
"monitor.hours": "Historique",
"monitor.bucket": "Intervalle en minutes",
"monitor.aggregation": "Méthode de calcul",
"monitor.sum": "Somme",
"monitor.average": "Moyenne",
"monitor.max": "Valeur maximale",
"monitor.metric": "Mesure des jetons",
"monitor.total": "Total des jetons",
"monitor.input": "Entrée",
"monitor.output": "Sortie",
"monitor.cached": "En cache",
"monitor.display": "Afficher le graphique",
"monitor.on": "Activé",
"monitor.off": "Désactivé",
"monitor.tokensAxis": "Jetons",
"monitor.timeAxis": "Temps",
"usage.incomplete": "Certains enregistrements d’utilisation n’ont pas pu être inclus. Les totaux, dates et classements reposent uniquement sur les enregistrements lisibles.",
"models.pickerOrder.usageIncomplete": "Impossible d’enregistrer l’ordre par utilisation : l’historique est incomplet. Choisissez un autre ordre ou réparez d’abord l’historique.",
"api.attribution.noRecordedUse": "Aucune utilisation dans les enregistrements lisibles",
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@ import type { TKey } from "./en";
* Japanese i18n catalog; must match the `TKey` set (compile-checked).
*/
export const ja: Record<TKey, string> = {
"monitor.menuBarSettings": "メニューバー設定",
"monitor.averageNote": "Values are per-series request averages/maxima; stacked height is not the average/maximum of all requests.",
"nav.monitor": "Graphs · accounts · display",
"monitor.showToday": "Today’s usage",
"monitor.showAccounts": "Account and provider usage",
"monitor.showModels": "Today’s model list",
"monitor.showCost": "Estimated cost",
"monitor.modelFilter": "Graph model filter",
"monitor.allModels": "All models (include new models)",
"monitor.displaySettings": "表示設定",
"monitor.title": "Local monitor",
"monitor.graph": "Token usage graph",
"monitor.empty": "No usage recorded for this range.",
"monitor.shortQuota": "Short window",
"monitor.weeklyQuota": "Weekly",
"monitor.settings": "Monitor settings",
"monitor.config": "Configuration JSON",
"monitor.unavailable": "The local monitor is unavailable.",
"monitor.invalidConfig": "Configuration must be valid JSON.",
"monitor.stale": "Settings changed elsewhere. Refresh and save again.",
"monitor.saveFailed": "Could not save monitor settings.",
"monitor.chartStyle": "Chart style",
"monitor.line": "Line",
"monitor.stackedBar": "Stacked bars",
"monitor.grouping": "Group by",
"monitor.model": "Model",
"monitor.modelAccount": "Model and account",
"monitor.hours": "History",
"monitor.bucket": "Bucket minutes",
"monitor.aggregation": "Aggregation",
"monitor.sum": "Sum",
"monitor.average": "Average",
"monitor.max": "Maximum",
"monitor.metric": "Token metric",
"monitor.total": "Total",
"monitor.input": "Input",
"monitor.output": "Output",
"monitor.cached": "Cached",
"monitor.display": "Show graph",
"monitor.on": "On",
"monitor.off": "Off",
"monitor.tokensAxis": "Tokens",
"monitor.timeAxis": "Time",
"usage.incomplete": "一部の使用履歴を集計できませんでした。回数、日付、順位は読み取れる記録のみを反映しています。",
"models.pickerOrder.usageIncomplete": "使用履歴が不完全なため、使用回数順を保存できません。別の順序を選ぶか、履歴を修復してください。",
"api.attribution.noRecordedUse": "読み取れる記録に使用履歴なし",
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@ import type { TKey } from "./en";
* Korean i18n catalog; must match the `TKey` set (compile-checked).
*/
export const ko: Record<TKey, string> = {
"monitor.menuBarSettings": "상단바 설정",
"monitor.averageNote": "각 범례의 요청당 평균/최대입니다. 누적 막대 전체 높이는 전체 요청의 평균/최대가 아닙니다.",
"nav.monitor": "그래프·계정·표시 설정",
"monitor.showToday": "오늘 사용량",
"monitor.showAccounts": "계정·프로바이더 사용량",
"monitor.showModels": "오늘 모델별 목록",
"monitor.showCost": "추정 비용",
"monitor.modelFilter": "그래프 모델 필터",
"monitor.allModels": "모든 모델 (새 모델 자동 포함)",
"monitor.displaySettings": "표시 설정",
"monitor.title": "로컬 모니터",
"monitor.graph": "토큰 사용량 그래프",
"monitor.empty": "이 기간에는 기록된 사용량이 없습니다.",
"monitor.shortQuota": "세션",
"monitor.weeklyQuota": "주간",
"monitor.settings": "그래프 · 계정 · 표시 설정",
"monitor.config": "문구 · 색상 · 크기 등 전체 설정 (JSON)",
"monitor.unavailable": "메뉴 막대의 OpenCodex Monitor를 실행하면 연결됩니다.",
"monitor.invalidConfig": "구성은 유효한 JSON이어야 합니다.",
"monitor.stale": "다른 곳에서 설정이 바뀌었습니다. 새로 고친 뒤 다시 저장하세요.",
"monitor.saveFailed": "모니터 설정을 저장하지 못했습니다.",
"monitor.chartStyle": "그래프 형태",
"monitor.line": "선",
"monitor.stackedBar": "누적 막대",
"monitor.grouping": "범례",
"monitor.model": "모델별",
"monitor.modelAccount": "모델 + 계정별",
"monitor.hours": "조회 범위 (시간)",
"monitor.bucket": "집계 간격 (분)",
"monitor.aggregation": "계산 방식",
"monitor.sum": "토큰 합계",
"monitor.average": "요청당 평균",
"monitor.max": "요청당 최대",
"monitor.metric": "토큰 종류",
"monitor.total": "전체",
"monitor.input": "입력",
"monitor.output": "출력",
"monitor.cached": "캐시 읽기",
"monitor.display": "그래프 표시",
"monitor.on": "켜기",
"monitor.off": "끄기",
"monitor.tokensAxis": "토큰 수",
"monitor.timeAxis": "시간",
"usage.incomplete": "일부 사용량 기록을 집계하지 못했습니다. 횟수, 날짜, 순위는 읽을 수 있는 기록만 반영합니다.",
"models.pickerOrder.usageIncomplete": "사용량 이력이 불완전해 많이 사용한 순서를 저장할 수 없습니다. 다른 순서를 선택하거나 이력을 복구하세요.",
"api.attribution.noRecordedUse": "읽을 수 있는 기록에 사용 내역 없음",
Expand Down
43 changes: 43 additions & 0 deletions gui/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@ import type { TKey } from "./en";
* Russian i18n catalog; must match the `TKey` set (compile-checked).
*/
export const ru: Record<TKey, string> = {
"monitor.menuBarSettings": "Настройки строки меню",
"monitor.averageNote": "Values are per-series request averages/maxima; stacked height is not the average/maximum of all requests.",
"nav.monitor": "Graphs · accounts · display",
"monitor.showToday": "Today’s usage",
"monitor.showAccounts": "Account and provider usage",
"monitor.showModels": "Today’s model list",
"monitor.showCost": "Estimated cost",
"monitor.modelFilter": "Graph model filter",
"monitor.allModels": "All models (include new models)",
"monitor.displaySettings": "Настройки отображения",
"monitor.title": "Local monitor",
"monitor.graph": "Token usage graph",
"monitor.empty": "No usage recorded for this range.",
"monitor.shortQuota": "Short window",
"monitor.weeklyQuota": "Weekly",
"monitor.settings": "Monitor settings",
"monitor.config": "Configuration JSON",
"monitor.unavailable": "The local monitor is unavailable.",
"monitor.invalidConfig": "Configuration must be valid JSON.",
"monitor.stale": "Settings changed elsewhere. Refresh and save again.",
"monitor.saveFailed": "Could not save monitor settings.",
"monitor.chartStyle": "Chart style",
"monitor.line": "Line",
"monitor.stackedBar": "Stacked bars",
"monitor.grouping": "Group by",
"monitor.model": "Model",
"monitor.modelAccount": "Model and account",
"monitor.hours": "History",
"monitor.bucket": "Bucket minutes",
"monitor.aggregation": "Aggregation",
"monitor.sum": "Sum",
"monitor.average": "Average",
"monitor.max": "Maximum",
"monitor.metric": "Token metric",
"monitor.total": "Total",
"monitor.input": "Input",
"monitor.output": "Output",
"monitor.cached": "Cached",
"monitor.display": "Show graph",
"monitor.on": "On",
"monitor.off": "Off",
"monitor.tokensAxis": "Tokens",
"monitor.timeAxis": "Time",
"usage.incomplete": "Часть записей об использовании не удалось учесть. Счётчики, даты и рейтинги основаны только на читаемых записях.",
"models.pickerOrder.usageIncomplete": "Нельзя сохранить порядок по частоте использования: история неполная. Выберите другой порядок или сначала восстановите историю.",
"api.attribution.noRecordedUse": "В читаемых записях использование не найдено",
Expand Down
Loading
Loading