From a1f27058d5e61c2869373313cb39377d2360b5e2 Mon Sep 17 00:00:00 2001 From: Gabriel-Dev <292659667+gabriel-dev725@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:41:53 -0700 Subject: [PATCH 1/2] feat(discover): add token usage statistics and card - Introduces token usage metrics in the Discover view. - Allows users to enable/disable the token usage card via preferences. - Provides internationalization for new token usage elements. - Updates application entry point to support token usage card state. --- .../preferences/ApplicationPreferences.kt | 4 + .../io/askimo/ui/discover/DiscoverView.kt | 471 ++++++++++++------ .../main/resources/i18n/messages.properties | 8 + .../resources/i18n/messages_de.properties | 8 + .../resources/i18n/messages_es.properties | 8 + .../resources/i18n/messages_fr.properties | 8 + .../resources/i18n/messages_ja_JP.properties | 8 + .../resources/i18n/messages_ko_KR.properties | 8 + .../resources/i18n/messages_pt_BR.properties | 8 + .../resources/i18n/messages_vi_VN.properties | 8 + .../resources/i18n/messages_zh_CN.properties | 8 + .../resources/i18n/messages_zh_TW.properties | 8 + .../src/main/kotlin/io/askimo/desktop/Main.kt | 17 + 13 files changed, 416 insertions(+), 156 deletions(-) diff --git a/desktop-shared/src/main/kotlin/io/askimo/ui/common/preferences/ApplicationPreferences.kt b/desktop-shared/src/main/kotlin/io/askimo/ui/common/preferences/ApplicationPreferences.kt index 3c1e767bd..97bce410d 100644 --- a/desktop-shared/src/main/kotlin/io/askimo/ui/common/preferences/ApplicationPreferences.kt +++ b/desktop-shared/src/main/kotlin/io/askimo/ui/common/preferences/ApplicationPreferences.kt @@ -74,6 +74,7 @@ object ApplicationPreferences { private const val SHOW_PLANS_IN_SIDEBAR_KEY = "ui.show_plans_in_sidebar" private const val SHOW_SKILLS_IN_SIDEBAR_KEY = "ui.show_skills_in_sidebar" private const val SHOW_PROJECTS_IN_SIDEBAR_KEY = "ui.show_projects_in_sidebar" + private const val SHOW_TOKEN_USAGE_CARD_KEY = "ui.show_token_usage_card" fun getProjectSidePanelWidth(): Int = safeGetInt(PROJECT_SIDE_PANEL_WIDTH_KEY, DEFAULT_PROJECT_SIDE_PANEL_WIDTH) fun setProjectSidePanelWidth(width: Int) = safePutInt(PROJECT_SIDE_PANEL_WIDTH_KEY, width) @@ -120,6 +121,9 @@ object ApplicationPreferences { fun getShowProjectsInSidebar(): Boolean = safeGetBoolean(SHOW_PROJECTS_IN_SIDEBAR_KEY, true) fun setShowProjectsInSidebar(show: Boolean) = safePutBoolean(SHOW_PROJECTS_IN_SIDEBAR_KEY, show) + fun getShowTokenUsageCard(): Boolean = safeGetBoolean(SHOW_TOKEN_USAGE_CARD_KEY, true) + fun setShowTokenUsageCard(show: Boolean) = safePutBoolean(SHOW_TOKEN_USAGE_CARD_KEY, show) + // ============================================================ // LIFECYCLE // ============================================================ diff --git a/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt b/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt index 5a0856780..e5c1e4ed1 100644 --- a/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt +++ b/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt @@ -26,36 +26,48 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollbarAdapter +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.automirrored.filled.LibraryBooks +import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ChatBubbleOutline import androidx.compose.material.icons.filled.Extension import androidx.compose.material.icons.filled.FolderOpen import androidx.compose.material.icons.filled.PlayCircle import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.Token +import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.askimo.core.AppConstants.DOMAIN import io.askimo.core.chat.domain.ChatSession import io.askimo.core.config.FeatureFlags +import io.askimo.core.i18n.LocalizationManager +import io.askimo.core.telemetry.TelemetryMetrics import io.askimo.core.user.domain.UserProfile import io.askimo.core.util.TimeUtil import io.askimo.ui.common.components.clickableCard @@ -68,6 +80,18 @@ import java.awt.Desktop import java.net.URI import java.time.LocalTime +/** + * Abbreviates a token count to a compact, locale-aware string. + * + * Examples (en-US): 1_234_567 → "1.2M", 45_000 → "45.0K", 999 → "999" + * Examples (de-DE): → "1,2M", → "45,0K", 999 → "999" + */ +private fun abbreviateTokens(value: Long): String = when { + value >= 1_000_000 -> "${LocalizationManager.formatDouble(value / 1_000_000.0, 1)}M" + value >= 1_000 -> "${LocalizationManager.formatDouble(value / 1_000.0, 1)}K" + else -> LocalizationManager.formatNumber(value) +} + @Composable fun discoverView( userProfile: UserProfile?, @@ -80,15 +104,17 @@ fun discoverView( onNavigateToPlans: () -> Unit, onNavigateToSkills: () -> Unit, onNavigateToMcpSettings: () -> Unit, + showTokenUsageCard: Boolean, + onToggleTokenUsageCard: (Boolean) -> Unit, + onOpenSystemDiagnostics: () -> Unit, + telemetryMetrics: TelemetryMetrics, modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() Box(modifier = modifier.fillMaxSize()) { Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(scrollState), + modifier = Modifier.fillMaxSize().verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally, ) { Column( @@ -101,6 +127,8 @@ fun discoverView( headerSection( userProfile = userProfile, onNewChat = onNewChat, + showTokenUsageCard = showTokenUsageCard, + onToggleTokenUsageCard = onToggleTokenUsageCard, ) statCardsSection( @@ -118,6 +146,13 @@ fun discoverView( exploreFeaturesSection() + if (showTokenUsageCard) { + tokenUsageSection( + telemetryMetrics = telemetryMetrics, + onOpenSystemDiagnostics = onOpenSystemDiagnostics, + ) + } + recentSessionsSection( sessions = recentSessions, onResumeSession = onResumeSession, @@ -133,10 +168,14 @@ fun discoverView( } } +// ── Header ──────────────────────────────────────────────────────────────────── + @Composable private fun headerSection( userProfile: UserProfile?, onNewChat: () -> Unit, + showTokenUsageCard: Boolean, + onToggleTokenUsageCard: (Boolean) -> Unit, ) { val hour = LocalTime.now().hour val greetingKey = when { @@ -145,32 +184,78 @@ private fun headerSection( else -> "discover.greeting.evening" } val firstName = userProfile?.name?.split(" ")?.firstOrNull() ?: stringResource("user.profile.default_name") + var showCustomizeMenu by remember { mutableStateOf(false) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - Column(verticalArrangement = Arrangement.spacedBy(Spacing.extraSmall)) { - Text( - text = stringResource(greetingKey, firstName), - style = MaterialTheme.typography.headlineMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onBackground, - ) - } + Text( + text = stringResource(greetingKey, firstName), + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onBackground, + ) - Button( - onClick = onNewChat, - modifier = Modifier.pointerHoverIcon(PointerIcon.Hand), + Row( + horizontalArrangement = Arrangement.spacedBy(Spacing.small), + verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Default.Add, contentDescription = null, modifier = Modifier.size(18.dp)) - Spacer(modifier = Modifier.size(6.dp)) - Text(stringResource("chat.new")) + Box { + IconButton( + onClick = { showCustomizeMenu = true }, + modifier = Modifier.pointerHoverIcon(PointerIcon.Hand), + ) { + Icon( + Icons.Default.Tune, + contentDescription = stringResource("discover.customize"), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + AppComponents.dropdownMenu( + expanded = showCustomizeMenu, + onDismissRequest = { showCustomizeMenu = false }, + ) { + DropdownMenuItem( + text = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource("discover.customize.show_token_usage"), + style = MaterialTheme.typography.bodyMedium, + ) + Spacer(modifier = Modifier.width(Spacing.large)) + Switch( + checked = showTokenUsageCard, + onCheckedChange = { onToggleTokenUsageCard(it) }, + modifier = Modifier.pointerHoverIcon(PointerIcon.Hand), + ) + } + }, + onClick = { onToggleTokenUsageCard(!showTokenUsageCard) }, + modifier = Modifier.pointerHoverIcon(PointerIcon.Hand), + ) + } + } + + Button( + onClick = onNewChat, + modifier = Modifier.pointerHoverIcon(PointerIcon.Hand), + ) { + Icon(Icons.Default.Add, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(modifier = Modifier.size(6.dp)) + Text(stringResource("chat.new")) + } } } } +// ── Stat cards ──────────────────────────────────────────────────────────────── + @Composable private fun statCardsSection( totalChats: Int?, @@ -190,7 +275,7 @@ private fun statCardsSection( ) { statCard( label = stringResource("discover.stat.chats"), - value = totalChats?.toString() ?: "—", + value = totalChats?.let { LocalizationManager.formatNumber(it) } ?: "—", icon = { Icon(Icons.Default.ChatBubbleOutline, contentDescription = null, modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = onNavigateToSessions, modifier = Modifier.weight(1f), @@ -199,7 +284,7 @@ private fun statCardsSection( if (FeatureFlags.projectsEnabled) { statCard( label = stringResource("discover.stat.projects"), - value = totalProjects?.toString() ?: "—", + value = totalProjects?.let { LocalizationManager.formatNumber(it) } ?: "—", icon = { Icon(Icons.Default.FolderOpen, contentDescription = null, modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = onNavigateToProjects, modifier = Modifier.weight(1f), @@ -209,7 +294,7 @@ private fun statCardsSection( if (FeatureFlags.mcpIntegrationEnabled) { statCard( label = stringResource("discover.stat.mcp"), - value = totalMcpServers.toString(), + value = LocalizationManager.formatNumber(totalMcpServers), icon = { Icon(Icons.Default.Settings, contentDescription = null, modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = onNavigateToMcpSettings, modifier = Modifier.weight(1f), @@ -219,7 +304,7 @@ private fun statCardsSection( if (FeatureFlags.plansEnabled) { statCard( label = stringResource("discover.stat.plans"), - value = totalPlans?.toString() ?: "—", + value = totalPlans?.let { LocalizationManager.formatNumber(it) } ?: "—", icon = { Icon(Icons.Default.PlayCircle, contentDescription = null, modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = onNavigateToPlans, modifier = Modifier.weight(1f), @@ -229,7 +314,7 @@ private fun statCardsSection( if (FeatureFlags.skillsEnabled) { statCard( label = stringResource("discover.stat.skills"), - value = totalSkills?.toString() ?: "—", + value = totalSkills?.let { LocalizationManager.formatNumber(it) } ?: "—", icon = { Icon(Icons.Default.Extension, contentDescription = null, modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = onNavigateToSkills, modifier = Modifier.weight(1f), @@ -246,31 +331,12 @@ private fun statCard( modifier: Modifier = Modifier, onClick: (() -> Unit)? = null, ) { - val colors = AppComponents.primaryCardColors() - - clickableCard( - onClick = onClick, - modifier = modifier, - colors = colors, - ) { + clickableCard(onClick = onClick, modifier = modifier, colors = AppComponents.primaryCardColors()) { Column( - modifier = Modifier - .fillMaxWidth() - .padding(Spacing.large), + modifier = Modifier.fillMaxWidth().padding(Spacing.large), verticalArrangement = Arrangement.spacedBy(Spacing.medium), ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = Modifier.size(40.dp), - contentAlignment = Alignment.Center, - ) { - icon() - } - } + Box(modifier = Modifier.size(40.dp), contentAlignment = Alignment.Center) { icon() } Text( text = value, style = MaterialTheme.typography.headlineLarge, @@ -286,63 +352,208 @@ private fun statCard( } } +// ── Token Usage section ─────────────────────────────────────────────────────── + /** - * Data class for explore feature card configuration. + * Full-width section with a horizontal bar chart of the top 5 models by token + * usage. All numbers are formatted via [LocalizationManager] so grouping separators + * and decimal marks follow the active locale. Abbreviated counts (K/M) also use + * [LocalizationManager.formatDouble] for locale-correct decimal separators. */ -private data class ExploreCardData( - val icon: ImageVector, - val titleKey: String, - val descKey: String, - val url: String, -) +@Composable +private fun tokenUsageSection( + telemetryMetrics: TelemetryMetrics, + onOpenSystemDiagnostics: () -> Unit, +) { + val totalTokens = telemetryMetrics.totalTokensUsed + val topModels = telemetryMetrics.llmTokensByProvider + .entries + .sortedByDescending { it.value } + .take(5) + + Column(verticalArrangement = Arrangement.spacedBy(Spacing.medium)) { + // ── Section header ───────────────────────────────────────── + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(Spacing.small), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + Icons.Default.Token, + contentDescription = null, + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.onBackground, + ) + Text( + text = stringResource("discover.tokens.title"), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.onBackground, + ) + } + + if (totalTokens > 0) { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + // abbreviateTokens uses LocalizationManager internally + text = stringResource("discover.tokens.total", abbreviateTokens(totalTokens)), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + IconButton( + onClick = onOpenSystemDiagnostics, + modifier = Modifier.size(24.dp).pointerHoverIcon(PointerIcon.Hand), + ) { + Icon( + Icons.AutoMirrored.Filled.OpenInNew, + contentDescription = stringResource("discover.tokens.view_details"), + modifier = Modifier.size(14.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + } + + // ── Chart card ───────────────────────────────────────────── + Surface( + shape = MaterialTheme.shapes.large, + tonalElevation = 1.dp, + modifier = Modifier.fillMaxWidth(), + ) { + if (totalTokens == 0L) { + Box( + modifier = Modifier.fillMaxWidth().padding(Spacing.large), + contentAlignment = Alignment.Center, + ) { + Text( + text = stringResource("discover.tokens.empty"), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } else { + Column( + modifier = Modifier.fillMaxWidth().padding(Spacing.large), + verticalArrangement = Arrangement.spacedBy(Spacing.medium), + ) { + topModels.forEachIndexed { index, (key, tokens) -> + val parts = key.split(":", limit = 2) + val provider = parts.getOrElse(0) { key } + .replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } + val model = parts.getOrElse(1) { "" }.ifBlank { provider } + val fraction = tokens.toFloat() / totalTokens.toFloat() + // Percentage: locale-aware integer, e.g. "62 %" in fr vs "62%" in en + val pctFormatted = LocalizationManager.formatNumber((fraction * 100).toInt()) + "%" + + tokenBarRow( + provider = provider, + model = model, + tokens = tokens, + fraction = fraction, + pctFormatted = pctFormatted, + ) + + if (index < topModels.lastIndex) { + HorizontalDivider( + color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.3f), + ) + } + } + } + } + } + } +} @Composable -private fun exploreFeaturesSection() { - // Build list of enabled explore cards based on feature flags - val enabledCards = buildList { - if (FeatureFlags.mcpIntegrationEnabled) { - add( - ExploreCardData( - icon = Icons.Default.Extension, - titleKey = "discover.explore.mcp.title", - descKey = "discover.explore.mcp.desc", - url = "https://$DOMAIN/docs/desktop/mcp-integration/", - ), +private fun tokenBarRow( + provider: String, + model: String, + tokens: Long, + fraction: Float, + pctFormatted: String, +) { + Row( + modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(Spacing.medium), + ) { + // Model + provider name + Column(modifier = Modifier.width(160.dp)) { + Text( + text = model, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) - } - if (FeatureFlags.ragEnabled) { - add( - ExploreCardData( - icon = Icons.AutoMirrored.Filled.LibraryBooks, - titleKey = "discover.explore.rag.title", - descKey = "discover.explore.rag.desc", - url = "https://$DOMAIN/docs/desktop/rag/", - ), + Text( + text = provider, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) } - if (FeatureFlags.plansEnabled) { - add( - ExploreCardData( - icon = Icons.Default.PlayCircle, - titleKey = "discover.explore.plans.title", - descKey = "discover.explore.plans.desc", - url = "https://$DOMAIN/docs/desktop/plans/", - ), + + // Horizontal bar (track + fill) + Box(modifier = Modifier.weight(1f).height(8.dp)) { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surfaceVariant, RoundedCornerShape(4.dp)), ) - } - if (FeatureFlags.skillsEnabled) { - add( - ExploreCardData( - icon = Icons.Default.Extension, - titleKey = "discover.explore.skills.title", - descKey = "discover.explore.skills.desc", - url = "https://$DOMAIN/docs/desktop/skills/", - ), + // Clamp to 2% min so tiny values stay visible + Box( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth(fraction.coerceAtLeast(0.02f)) + .background(MaterialTheme.colorScheme.primary, RoundedCornerShape(4.dp)), ) } + + // Abbreviated token count — locale-aware decimal separator via abbreviateTokens() + Text( + text = abbreviateTokens(tokens), + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.End, + modifier = Modifier.width(52.dp), + ) + + // Percentage — locale-aware via LocalizationManager.formatNumber + Text( + text = pctFormatted, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.End, + modifier = Modifier.width(40.dp), + ) } +} - // Don't show section if no cards are enabled +// ── Explore features ────────────────────────────────────────────────────────── + +private data class ExploreCardData(val icon: ImageVector, val titleKey: String, val descKey: String, val url: String) + +@Composable +private fun exploreFeaturesSection() { + val enabledCards = buildList { + if (FeatureFlags.mcpIntegrationEnabled) add(ExploreCardData(Icons.Default.Extension, "discover.explore.mcp.title", "discover.explore.mcp.desc", "https://$DOMAIN/docs/desktop/mcp-integration/")) + if (FeatureFlags.ragEnabled) add(ExploreCardData(Icons.AutoMirrored.Filled.LibraryBooks, "discover.explore.rag.title", "discover.explore.rag.desc", "https://$DOMAIN/docs/desktop/rag/")) + if (FeatureFlags.plansEnabled) add(ExploreCardData(Icons.Default.PlayCircle, "discover.explore.plans.title", "discover.explore.plans.desc", "https://$DOMAIN/docs/desktop/plans/")) + if (FeatureFlags.skillsEnabled) add(ExploreCardData(Icons.Default.Extension, "discover.explore.skills.title", "discover.explore.skills.desc", "https://$DOMAIN/docs/desktop/skills/")) + } if (enabledCards.isEmpty()) return Column(verticalArrangement = Arrangement.spacedBy(Spacing.medium)) { @@ -353,21 +564,12 @@ private fun exploreFeaturesSection() { color = MaterialTheme.colorScheme.onBackground, ) Row( - modifier = Modifier - .fillMaxWidth() - .height(IntrinsicSize.Max), + modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max), horizontalArrangement = Arrangement.spacedBy(Spacing.large), ) { enabledCards.forEach { card -> exploreCard( - icon = { - Icon( - card.icon, - contentDescription = null, - modifier = Modifier.size(22.dp), - tint = MaterialTheme.colorScheme.onSecondaryContainer, - ) - }, + icon = { Icon(card.icon, contentDescription = null, modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.onSecondaryContainer) }, title = stringResource(card.titleKey), description = stringResource(card.descKey), url = card.url, @@ -392,10 +594,7 @@ private fun exploreCard( colors = AppComponents.secondaryCardColors(), ) { Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .padding(Spacing.large), + modifier = Modifier.fillMaxWidth().fillMaxHeight().padding(Spacing.large), verticalArrangement = Arrangement.spacedBy(Spacing.medium), ) { Row( @@ -404,28 +603,16 @@ private fun exploreCard( verticalAlignment = Alignment.Top, ) { icon() - Icon( - Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = null, - modifier = Modifier.size(14.dp), - tint = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.5f), - ) + Icon(Icons.AutoMirrored.Filled.ArrowForward, contentDescription = null, modifier = Modifier.size(14.dp), tint = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.5f)) } - Text( - text = title, - style = MaterialTheme.typography.titleSmall, - fontWeight = FontWeight.SemiBold, - color = MaterialTheme.colorScheme.onSecondaryContainer, - ) - Text( - text = description, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.82f), - ) + Text(text = title, style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSecondaryContainer) + Text(text = description, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.82f)) } } } +// ── Recent sessions ─────────────────────────────────────────────────────────── + @Composable private fun recentSessionsSection( sessions: List, @@ -447,21 +634,12 @@ private fun recentSessionsSection( modifier = Modifier.padding(vertical = 8.dp), ) } else { - Surface( - shape = MaterialTheme.shapes.large, - tonalElevation = 1.dp, - modifier = Modifier.fillMaxWidth(), - ) { + Surface(shape = MaterialTheme.shapes.large, tonalElevation = 1.dp, modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) { sessions.forEachIndexed { index, session -> - recentSessionRow( - session = session, - onResumeSession = onResumeSession, - ) + recentSessionRow(session = session, onResumeSession = onResumeSession) if (index < sessions.lastIndex) { - HorizontalDivider( - color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.4f), - ) + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.4f)) } } } @@ -489,11 +667,7 @@ private fun recentSessionRow( MaterialTheme.colorScheme.surface.copy(alpha = 0f) }, ) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = { onResumeSession(session.id) }, - ) + .clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { onResumeSession(session.id) } .pointerHoverIcon(PointerIcon.Hand) .padding(horizontal = Spacing.large, vertical = 14.dp), horizontalArrangement = Arrangement.SpaceBetween, @@ -504,27 +678,12 @@ private fun recentSessionRow( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f), ) { - Icon( - Icons.Default.ChatBubbleOutline, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.size(16.dp), - ) + Icon(Icons.Default.ChatBubbleOutline, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(16.dp)) sessionTooltip(session = session) { - Text( - text = session.title, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + Text(text = session.title, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface, maxLines = 1, overflow = TextOverflow.Ellipsis) } } Spacer(modifier = Modifier.width(16.dp)) - Text( - text = TimeUtil.formatDisplay(session.updatedAt), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + Text(text = TimeUtil.formatDisplay(session.updatedAt), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) } } diff --git a/desktop-shared/src/main/resources/i18n/messages.properties b/desktop-shared/src/main/resources/i18n/messages.properties index dd0891c8c..5cdd8e415 100644 --- a/desktop-shared/src/main/resources/i18n/messages.properties +++ b/desktop-shared/src/main/resources/i18n/messages.properties @@ -107,6 +107,14 @@ discover.stat.projects=Projects discover.stat.mcp=MCP Servers discover.stat.plans=Plans discover.stat.skills=Skills +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Explore Features discover.explore.mcp.title=MCP Servers discover.explore.mcp.desc=Extend Askimo with external tools and data sources via the Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_de.properties b/desktop-shared/src/main/resources/i18n/messages_de.properties index 591145127..c0c50062f 100644 --- a/desktop-shared/src/main/resources/i18n/messages_de.properties +++ b/desktop-shared/src/main/resources/i18n/messages_de.properties @@ -107,6 +107,14 @@ discover.stat.projects=Projekte discover.stat.mcp=MCP-Server discover.stat.plans=Pläne discover.stat.skills=Fähigkeiten +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Funktionen entdecken discover.explore.mcp.title=MCP-Server discover.explore.mcp.desc=Erweitern Sie Askimo mit externen Tools und Datenquellen über das Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_es.properties b/desktop-shared/src/main/resources/i18n/messages_es.properties index 90b73cc89..cf58f179a 100644 --- a/desktop-shared/src/main/resources/i18n/messages_es.properties +++ b/desktop-shared/src/main/resources/i18n/messages_es.properties @@ -108,6 +108,14 @@ discover.stat.projects=Proyectos discover.stat.mcp=Servidores MCP discover.stat.plans=Planes discover.stat.skills=Habilidades +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Explorar funciones discover.explore.mcp.title=Servidores MCP discover.explore.mcp.desc=Amplía Askimo con herramientas externas y fuentes de datos mediante el Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_fr.properties b/desktop-shared/src/main/resources/i18n/messages_fr.properties index 904c48fb8..f61497a1d 100644 --- a/desktop-shared/src/main/resources/i18n/messages_fr.properties +++ b/desktop-shared/src/main/resources/i18n/messages_fr.properties @@ -108,6 +108,14 @@ discover.stat.projects=Projets discover.stat.mcp=Serveurs MCP discover.stat.plans=Plans discover.stat.skills=Compétences +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Explorer les fonctionnalités discover.explore.mcp.title=Serveurs MCP discover.explore.mcp.desc=Étendez Askimo avec des outils externes et des sources de données via le Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties index 089ea4593..4be2a42c7 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties @@ -107,6 +107,14 @@ discover.stat.projects=プロジェクト discover.stat.mcp=MCPサーバー discover.stat.plans=プラン discover.stat.skills=スキル +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=機能を探索 discover.explore.mcp.title=MCPサーバー discover.explore.mcp.desc=Model Context Protocolを介して、外部ツールやデータソースでAskimoを拡張します。 diff --git a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties index 81f10aaa4..850bfdf13 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties @@ -108,6 +108,14 @@ discover.stat.projects=프로젝트 discover.stat.mcp=MCP 서버 discover.stat.plans=플랜 discover.stat.skills=스킬 +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=기능 살펴보기 discover.explore.mcp.title=MCP 서버 discover.explore.mcp.desc=Model Context Protocol을 통해 외부 도구와 데이터 소스로 Askimo를 확장하세요. diff --git a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties index 2688ae21b..abd604c2c 100644 --- a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties @@ -107,6 +107,14 @@ discover.stat.projects=Projetos discover.stat.mcp=Servidores MCP discover.stat.plans=Planos discover.stat.skills=Habilidades +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Explorar funcionalidades discover.explore.mcp.title=Servidores MCP discover.explore.mcp.desc=Expanda o Askimo com ferramentas externas e fontes de dados através do Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties index fc367de11..4310b81a9 100644 --- a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties @@ -117,6 +117,14 @@ discover.stat.projects=Dự án discover.stat.mcp=Máy chủ MCP discover.stat.plans=Kế hoạch discover.stat.skills=Kỹ năng +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=Khám phá tính năng discover.explore.mcp.title=Máy chủ MCP discover.explore.mcp.desc=Mở rộng Askimo bằng các công cụ bên ngoài và nguồn dữ liệu thông qua Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties index a9eb67253..6ca097ab2 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties @@ -107,6 +107,14 @@ discover.stat.projects=项目 discover.stat.mcp=MCP 服务器 discover.stat.plans=计划 discover.stat.skills=技能 +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=探索功能 discover.explore.mcp.title=MCP 服务器 discover.explore.mcp.desc=通过 Model Context Protocol,使用外部工具和数据源扩展 Askimo。 diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties index 5e9de8b45..10b291bb9 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties @@ -107,6 +107,14 @@ discover.stat.projects=專案 discover.stat.mcp=MCP 伺服器 discover.stat.plans=計劃 discover.stat.skills=技能 +discover.stat.tokens=Tokens Used +discover.stat.tokens.top_model=Top: {0} +discover.customize=Customize +discover.customize.show_token_usage=Show token usage card +discover.tokens.title=Token Usage +discover.tokens.total={0} total +discover.tokens.view_details=View details +discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. discover.explore.title=探索功能 discover.explore.mcp.title=MCP 伺服器 discover.explore.mcp.desc=透過 Model Context Protocol,使用外部工具和資料來源擴充 Askimo。 diff --git a/desktop/src/main/kotlin/io/askimo/desktop/Main.kt b/desktop/src/main/kotlin/io/askimo/desktop/Main.kt index da4b3d55a..20a680cc5 100644 --- a/desktop/src/main/kotlin/io/askimo/desktop/Main.kt +++ b/desktop/src/main/kotlin/io/askimo/desktop/Main.kt @@ -318,6 +318,7 @@ fun app(frameWindowScope: FrameWindowScope? = null, windowState: WindowState? = var showPlansInSidebar by remember { mutableStateOf(ApplicationPreferences.getShowPlansInSidebar()) } var showSkillsInSidebar by remember { mutableStateOf(ApplicationPreferences.getShowSkillsInSidebar()) } var showProjectsInSidebar by remember { mutableStateOf(ApplicationPreferences.getShowProjectsInSidebar()) } + var showTokenUsageCard by remember { mutableStateOf(ApplicationPreferences.getShowTokenUsageCard()) } var selectedProjectId by remember { mutableStateOf(null) } var sidebarWidthFraction by remember { mutableStateOf(ThemePreferences.getMainSidebarWidthFraction()) } var showQuitDialog by remember { mutableStateOf(false) } @@ -1140,6 +1141,14 @@ fun app(frameWindowScope: FrameWindowScope? = null, windowState: WindowState? = userAvatarPath = userProfile?.preferences?.get("avatarPath"), userProfile = userProfile, discoverViewModel = discoverViewModel, + showTokenUsageCard = showTokenUsageCard, + onToggleTokenUsageCard = { enabled -> + showTokenUsageCard = enabled + ApplicationPreferences.setShowTokenUsageCard(enabled) + }, + onOpenSystemDiagnostics = { + showSystemDiagnosticsDialog = true + }, onNavigateToMcpSettings = { settingsSection = SettingsSection.MCP_SERVERS previousView = currentView @@ -1961,7 +1970,11 @@ fun mainContent( userAvatarPath: String? = null, userProfile: UserProfile? = null, discoverViewModel: DiscoverViewModel, + showTokenUsageCard: Boolean = true, + onToggleTokenUsageCard: (Boolean) -> Unit = {}, + onOpenSystemDiagnostics: () -> Unit = {}, ) { + val discoverMetrics by appContext.telemetry.metricsFlow.collectAsState() Box( modifier = Modifier .fillMaxSize() @@ -1985,6 +1998,10 @@ fun mainContent( onNavigateToPlans = onNavigateToPlans, onNavigateToSkills = onNavigateToSkills, onNavigateToMcpSettings = onNavigateToMcpSettings, + showTokenUsageCard = showTokenUsageCard, + onToggleTokenUsageCard = onToggleTokenUsageCard, + onOpenSystemDiagnostics = onOpenSystemDiagnostics, + telemetryMetrics = discoverMetrics, modifier = Modifier.fillMaxSize(), ) From 8325cab51abb1ca985f9808bc9bfee22688dba50 Mon Sep 17 00:00:00 2001 From: Gabriel-Dev <292659667+gabriel-dev725@users.noreply.github.com> Date: Thu, 23 Jul 2026 20:35:31 -0700 Subject: [PATCH 2/2] Update per feedbacks --- .../io/askimo/ui/discover/DiscoverView.kt | 60 ++++++++++--------- .../main/resources/i18n/messages.properties | 4 +- .../resources/i18n/messages_de.properties | 14 ++--- .../resources/i18n/messages_es.properties | 14 ++--- .../resources/i18n/messages_fr.properties | 14 ++--- .../resources/i18n/messages_ja_JP.properties | 14 ++--- .../resources/i18n/messages_ko_KR.properties | 14 ++--- .../resources/i18n/messages_pt_BR.properties | 14 ++--- .../resources/i18n/messages_vi_VN.properties | 14 ++--- .../resources/i18n/messages_zh_CN.properties | 14 ++--- .../resources/i18n/messages_zh_TW.properties | 14 ++--- 11 files changed, 87 insertions(+), 103 deletions(-) diff --git a/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt b/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt index e5c1e4ed1..cd4cd879f 100644 --- a/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt +++ b/desktop-shared/src/main/kotlin/io/askimo/ui/discover/DiscoverView.kt @@ -75,6 +75,7 @@ import io.askimo.ui.common.i18n.stringResource import io.askimo.ui.common.theme.AppComponents import io.askimo.ui.common.theme.Spacing import io.askimo.ui.common.theme.ThemePreferences +import io.askimo.ui.common.ui.themedTooltip import io.askimo.ui.session.sessionTooltip import java.awt.Desktop import java.net.URI @@ -486,23 +487,25 @@ private fun tokenBarRow( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Spacing.medium), ) { - // Model + provider name - Column(modifier = Modifier.width(160.dp)) { - Text( - text = model, - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.onSurface, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - Text( - text = provider, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + // Model + provider name — tooltip shows full names if truncated + themedTooltip(text = "$model · $provider") { + Column(modifier = Modifier.width(160.dp)) { + Text( + text = model, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Text( + text = provider, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } } // Horizontal bar (track + fill) @@ -512,7 +515,6 @@ private fun tokenBarRow( .fillMaxSize() .background(MaterialTheme.colorScheme.surfaceVariant, RoundedCornerShape(4.dp)), ) - // Clamp to 2% min so tiny values stay visible Box( modifier = Modifier .fillMaxHeight() @@ -521,17 +523,19 @@ private fun tokenBarRow( ) } - // Abbreviated token count — locale-aware decimal separator via abbreviateTokens() - Text( - text = abbreviateTokens(tokens), - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.onSurface, - textAlign = TextAlign.End, - modifier = Modifier.width(52.dp), - ) + // Abbreviated token count — tooltip shows exact locale-formatted number + themedTooltip(text = LocalizationManager.formatNumber(tokens)) { + Text( + text = abbreviateTokens(tokens), + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.End, + modifier = Modifier.width(52.dp), + ) + } - // Percentage — locale-aware via LocalizationManager.formatNumber + // Percentage Text( text = pctFormatted, style = MaterialTheme.typography.labelSmall, diff --git a/desktop-shared/src/main/resources/i18n/messages.properties b/desktop-shared/src/main/resources/i18n/messages.properties index 5cdd8e415..9584f37cb 100644 --- a/desktop-shared/src/main/resources/i18n/messages.properties +++ b/desktop-shared/src/main/resources/i18n/messages.properties @@ -107,11 +107,9 @@ discover.stat.projects=Projects discover.stat.mcp=MCP Servers discover.stat.plans=Plans discover.stat.skills=Skills -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} discover.customize=Customize discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage +discover.tokens.title=Top Models by Token Usage discover.tokens.total={0} total discover.tokens.view_details=View details discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. diff --git a/desktop-shared/src/main/resources/i18n/messages_de.properties b/desktop-shared/src/main/resources/i18n/messages_de.properties index c0c50062f..b98c61e88 100644 --- a/desktop-shared/src/main/resources/i18n/messages_de.properties +++ b/desktop-shared/src/main/resources/i18n/messages_de.properties @@ -107,14 +107,12 @@ discover.stat.projects=Projekte discover.stat.mcp=MCP-Server discover.stat.plans=Pläne discover.stat.skills=Fähigkeiten -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=Anpassen +discover.customize.show_token_usage=Token-Nutzungskarte anzeigen +discover.tokens.title=Top-Modelle nach Token-Nutzung +discover.tokens.total={0} insgesamt +discover.tokens.view_details=Details anzeigen +discover.tokens.empty=Noch keine Token-Nutzung aufgezeichnet. Beginnen Sie zu chatten, um hier Statistiken zu sehen. discover.explore.title=Funktionen entdecken discover.explore.mcp.title=MCP-Server discover.explore.mcp.desc=Erweitern Sie Askimo mit externen Tools und Datenquellen über das Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_es.properties b/desktop-shared/src/main/resources/i18n/messages_es.properties index cf58f179a..48612b57d 100644 --- a/desktop-shared/src/main/resources/i18n/messages_es.properties +++ b/desktop-shared/src/main/resources/i18n/messages_es.properties @@ -108,14 +108,12 @@ discover.stat.projects=Proyectos discover.stat.mcp=Servidores MCP discover.stat.plans=Planes discover.stat.skills=Habilidades -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=Personalizar +discover.customize.show_token_usage=Mostrar tarjeta de uso de tokens +discover.tokens.title=Modelos principales por uso de tokens +discover.tokens.total={0} en total +discover.tokens.view_details=Ver detalles +discover.tokens.empty=Aún no se ha registrado el uso de tokens. Empiece a chatear para ver las estadísticas aquí. discover.explore.title=Explorar funciones discover.explore.mcp.title=Servidores MCP discover.explore.mcp.desc=Amplía Askimo con herramientas externas y fuentes de datos mediante el Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_fr.properties b/desktop-shared/src/main/resources/i18n/messages_fr.properties index f61497a1d..c9998d841 100644 --- a/desktop-shared/src/main/resources/i18n/messages_fr.properties +++ b/desktop-shared/src/main/resources/i18n/messages_fr.properties @@ -108,14 +108,12 @@ discover.stat.projects=Projets discover.stat.mcp=Serveurs MCP discover.stat.plans=Plans discover.stat.skills=Compétences -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=Personnaliser +discover.customize.show_token_usage=Afficher la carte d'utilisation des jetons +discover.tokens.title=Meilleurs modèles par utilisation de jetons +discover.tokens.total={0} au total +discover.tokens.view_details=Voir les détails +discover.tokens.empty=Aucune utilisation de jetons enregistrée pour le moment. Commencez à discuter pour voir les statistiques ici. discover.explore.title=Explorer les fonctionnalités discover.explore.mcp.title=Serveurs MCP discover.explore.mcp.desc=Étendez Askimo avec des outils externes et des sources de données via le Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties index 4be2a42c7..4b79a0080 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties @@ -107,14 +107,12 @@ discover.stat.projects=プロジェクト discover.stat.mcp=MCPサーバー discover.stat.plans=プラン discover.stat.skills=スキル -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=カスタマイズ +discover.customize.show_token_usage=トークン使用量カードを表示 +discover.tokens.title=トークン使用量別のトップモデル +discover.tokens.total=合計 {0} +discover.tokens.view_details=詳細を表示 +discover.tokens.empty=トークンの使用記録がまだありません。チャットを開始して統計を表示してください。 discover.explore.title=機能を探索 discover.explore.mcp.title=MCPサーバー discover.explore.mcp.desc=Model Context Protocolを介して、外部ツールやデータソースでAskimoを拡張します。 diff --git a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties index 850bfdf13..9001d8c98 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties @@ -108,14 +108,12 @@ discover.stat.projects=프로젝트 discover.stat.mcp=MCP 서버 discover.stat.plans=플랜 discover.stat.skills=스킬 -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=사용자 지정 +discover.customize.show_token_usage=토큰 사용량 카드 표시 +discover.tokens.title=토큰 사용량 기준 상위 모델 +discover.tokens.total=총 {0} +discover.tokens.view_details=세부 정보 보기 +discover.tokens.empty=아직 기록된 토큰 사용량이 없습니다. 채팅을 시작하여 여기에서 통계를 확인하세요. discover.explore.title=기능 살펴보기 discover.explore.mcp.title=MCP 서버 discover.explore.mcp.desc=Model Context Protocol을 통해 외부 도구와 데이터 소스로 Askimo를 확장하세요. diff --git a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties index abd604c2c..a6aeea888 100644 --- a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties @@ -107,14 +107,12 @@ discover.stat.projects=Projetos discover.stat.mcp=Servidores MCP discover.stat.plans=Planos discover.stat.skills=Habilidades -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=Personalizar +discover.customize.show_token_usage=Mostrar cartão de uso de tokens +discover.tokens.title=Principais modelos por uso de tokens +discover.tokens.total={0} no total +discover.tokens.view_details=Ver detalhes +discover.tokens.empty=Nenhum uso de token registrado ainda. Comece a conversar para ver as estatísticas aqui. discover.explore.title=Explorar funcionalidades discover.explore.mcp.title=Servidores MCP discover.explore.mcp.desc=Expanda o Askimo com ferramentas externas e fontes de dados através do Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties index 4310b81a9..e5df301f8 100644 --- a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties @@ -117,14 +117,12 @@ discover.stat.projects=Dự án discover.stat.mcp=Máy chủ MCP discover.stat.plans=Kế hoạch discover.stat.skills=Kỹ năng -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=Tùy chỉnh +discover.customize.show_token_usage=Hiển thị thẻ sử dụng token +discover.tokens.title=Các model hàng đầu theo mức sử dụng token +discover.tokens.total=Tổng {0} +discover.tokens.view_details=Xem chi tiết +discover.tokens.empty=Chưa có dữ liệu sử dụng token. Hãy bắt đầu trò chuyện để xem thống kê tại đây. discover.explore.title=Khám phá tính năng discover.explore.mcp.title=Máy chủ MCP discover.explore.mcp.desc=Mở rộng Askimo bằng các công cụ bên ngoài và nguồn dữ liệu thông qua Model Context Protocol. diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties index 6ca097ab2..e199022c7 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties @@ -107,14 +107,12 @@ discover.stat.projects=项目 discover.stat.mcp=MCP 服务器 discover.stat.plans=计划 discover.stat.skills=技能 -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=自定义 +discover.customize.show_token_usage=显示 Token 使用情况卡片 +discover.tokens.title=按 Token 使用量排名的模型 +discover.tokens.total=共 {0} +discover.tokens.view_details=查看详情 +discover.tokens.empty=暂无 Token 使用记录。开始聊天以在此处查看统计信息。 discover.explore.title=探索功能 discover.explore.mcp.title=MCP 服务器 discover.explore.mcp.desc=通过 Model Context Protocol,使用外部工具和数据源扩展 Askimo。 diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties index 10b291bb9..ab4bf799a 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties @@ -107,14 +107,12 @@ discover.stat.projects=專案 discover.stat.mcp=MCP 伺服器 discover.stat.plans=計劃 discover.stat.skills=技能 -discover.stat.tokens=Tokens Used -discover.stat.tokens.top_model=Top: {0} -discover.customize=Customize -discover.customize.show_token_usage=Show token usage card -discover.tokens.title=Token Usage -discover.tokens.total={0} total -discover.tokens.view_details=View details -discover.tokens.empty=No token usage recorded yet. Start chatting to see stats here. +discover.customize=自訂 +discover.customize.show_token_usage=顯示 Token 使用量卡片 +discover.tokens.title=按 Token 使用量排名的模型 +discover.tokens.total=共 {0} +discover.tokens.view_details=查看詳情 +discover.tokens.empty=暫無 Token 使用記錄。開始聊天以在此處查看統計資訊。 discover.explore.title=探索功能 discover.explore.mcp.title=MCP 伺服器 discover.explore.mcp.desc=透過 Model Context Protocol,使用外部工具和資料來源擴充 Askimo。