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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions desktop-shared/src/main/kotlin/io/askimo/ui/chat/ChatInputField.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Send
Expand Down Expand Up @@ -70,6 +71,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
Expand Down Expand Up @@ -101,6 +103,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.java.KoinJavaComponent
import java.awt.Cursor
import java.io.File
import java.time.Instant
import java.util.UUID
import kotlin.collections.minus
Expand Down Expand Up @@ -240,7 +243,7 @@ fun chatInputField(
if (paths.isNotEmpty()) {
try {
val maxFileSizeBytes = AppConfig.indexing.maxFileBytes
val files = paths.map { java.io.File(it) }
val files = paths.map { File(it) }
val invalidFiles = files.filter { it.length() > maxFileSizeBytes }

if (invalidFiles.isNotEmpty()) {
Expand Down Expand Up @@ -1105,7 +1108,7 @@ private fun fileAttachmentItem(
isLoadingPreview = true
previewContent = withContext(Dispatchers.IO) {
try {
val file = attachment.filePath?.let { java.io.File(it) }
val file = attachment.filePath?.let { File(it) }
if (file != null && file.exists() && file.length() < 512 * 1024) {
// Read up to 200 lines for preview
file.bufferedReader().use { reader ->
Expand Down Expand Up @@ -1233,15 +1236,18 @@ private fun fileAttachmentItem(

previewContent != null -> {
val scrollState = rememberScrollState()
androidx.compose.foundation.text.selection.SelectionContainer {
Text(
text = previewContent!!,
style = MaterialTheme.typography.labelSmall.copy(
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace,
),
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.verticalScroll(scrollState),
)
Box(
modifier = Modifier.verticalScroll(scrollState),
) {
SelectionContainer {
Text(
text = previewContent!!,
style = MaterialTheme.typography.labelSmall.copy(
fontFamily = FontFamily.Monospace,
),
color = MaterialTheme.colorScheme.onSurface,
)
}
}
}
}
Expand Down
59 changes: 31 additions & 28 deletions desktop-shared/src/main/kotlin/io/askimo/ui/chat/ChatView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
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.ChatDirective
import io.askimo.core.chat.domain.Project
import io.askimo.core.chat.dto.ChatMessageDTO
Expand Down Expand Up @@ -755,7 +756,7 @@ fun chatView(
}
},
onClick = {
uriHandler.openUri("https://askimo.chat/docs/desktop/directives/")
uriHandler.openUri("https://$DOMAIN/docs/desktop/directives/")
directiveDropdownExpanded = false
},
modifier = Modifier.pointerHoverIcon(PointerIcon.Hand),
Expand Down Expand Up @@ -1147,33 +1148,35 @@ fun chatView(
} // end centered Box
} // End of main chat Column

// Project Side Panel (right side)
projectSidePanelSlot(
project,
ragIndexingStatus,
ragIndexingPercentage,
sidePanelExpanded,
{ sidePanelExpanded = it },
{ filePaths ->
val newAttachments = filePaths.map { path ->
val file = File(path)
FileAttachmentDTO(
id = randomUUID().toString(),
messageId = "",
sessionId = sessionId ?: "",
fileName = file.name,
mimeType = file.extension,
size = file.length(),
createdAt = Instant.now(),
content = null,
filePath = file.absolutePath,
)
}
val existingPaths = attachments.mapNotNull { it.filePath }.toSet()
val toAdd = newAttachments.filter { it.filePath !in existingPaths }
if (toAdd.isNotEmpty()) attachments = attachments + toAdd
},
)
// Project Side Panel (right side) — only shown when session belongs to a project
if (project != null) {
projectSidePanelSlot(
project,
ragIndexingStatus,
ragIndexingPercentage,
sidePanelExpanded,
{ sidePanelExpanded = it },
{ filePaths ->
val newAttachments = filePaths.map { path ->
val file = File(path)
FileAttachmentDTO(
id = randomUUID().toString(),
messageId = "",
sessionId = sessionId ?: "",
fileName = file.name,
mimeType = file.extension,
size = file.length(),
createdAt = Instant.now(),
content = null,
filePath = file.absolutePath,
)
}
val existingPaths = attachments.mapNotNull { it.filePath }.toSet()
val toAdd = newAttachments.filter { it.filePath !in existingPaths }
if (toAdd.isNotEmpty()) attachments = attachments + toAdd
},
)
}
} // End of Row

// AI Message Edit Dialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -591,29 +591,29 @@ private fun aiMessageBubble(
}
}

SelectionContainer {
if (searchQuery.isNotBlank()) {
if (searchQuery.isNotBlank()) {
SelectionContainer {
Text(
text = highlightSearchText(
text = markdownToPlainText(message.content),
query = searchQuery,
highlightColor = Color(0xFFFFD54F), // amber-300 — visible on any bg
highlightColor = Color(0xFFFFD54F),
isActiveResult = isActiveSearchResult,
activeHighlightColor = Color(0xFFFF8F00), // amber-800 — bold active match
activeHighlightColor = Color(0xFFFF8F00),
),
modifier = Modifier.padding(start = 12.dp, end = 48.dp, top = 12.dp, bottom = 12.dp),
style = MaterialTheme.typography.bodyMedium,
)
} else {
markdownText(
markdown = message.content,
modifier = Modifier.padding(start = 12.dp, end = 48.dp, top = 12.dp, bottom = 12.dp),
viewportTopY = viewportTopY,
isStreaming = isStreaming,
onRunRequest = { cmd, lang -> pendingRunRequest = Pair(cmd, lang) },
messageId = message.id,
)
}
} else {
markdownText(
markdown = message.content,
modifier = Modifier.padding(start = 12.dp, end = 48.dp, top = 12.dp, bottom = 12.dp),
viewportTopY = viewportTopY,
isStreaming = isStreaming,
onRunRequest = { cmd, lang -> pendingRunRequest = Pair(cmd, lang) },
messageId = message.id,
)
}

if (isOutdatedMessage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ 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.unit.dp
import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.chat.domain.ChatSession
import io.askimo.core.chat.repository.ChatSessionRepository
import io.askimo.core.chat.repository.ProjectRepository
Expand Down Expand Up @@ -319,28 +320,28 @@ private fun exploreFeaturesSection() {
icon = { Icon(Icons.Default.Extension, contentDescription = null, modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant) },
title = stringResource("discover.explore.mcp.title"),
description = stringResource("discover.explore.mcp.desc"),
url = "https://askimo.chat/docs/desktop/mcp-integration/",
url = "https://$DOMAIN/docs/desktop/mcp-integration/",
modifier = Modifier.weight(1f),
)
exploreCard(
icon = { Icon(Icons.AutoMirrored.Filled.LibraryBooks, contentDescription = null, modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant) },
title = stringResource("discover.explore.rag.title"),
description = stringResource("discover.explore.rag.desc"),
url = "https://askimo.chat/docs/desktop/rag/",
url = "https://$DOMAIN/docs/desktop/rag/",
modifier = Modifier.weight(1f),
)
exploreCard(
icon = { Icon(Icons.Default.PlayCircle, contentDescription = null, modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant) },
title = stringResource("discover.explore.plans.title"),
description = stringResource("discover.explore.plans.desc"),
url = "https://askimo.chat/docs/desktop/plans/",
url = "https://$DOMAIN/docs/desktop/plans/",
modifier = Modifier.weight(1f),
)
exploreCard(
icon = { Icon(Icons.Default.Extension, contentDescription = null, modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant) },
title = stringResource("discover.explore.skills.title"),
description = stringResource("discover.explore.skills.desc"),
url = "https://askimo.chat/docs/desktop/skills/",
url = "https://$DOMAIN/docs/desktop/skills/",
modifier = Modifier.weight(1f),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.i18n.LocalizationManager
import io.askimo.ui.common.components.primaryButton
import io.askimo.ui.common.components.secondaryButton
Expand Down Expand Up @@ -490,7 +491,7 @@ private fun onboardingStepAnalytics(
}

TextButton(
onClick = { uriHandler.openUri("https://askimo.chat/security/") },
onClick = { uriHandler.openUri("https://$DOMAIN/security/") },
modifier = Modifier.pointerHoverIcon(PointerIcon.Hand),
) {
Text(
Expand Down Expand Up @@ -650,7 +651,7 @@ private fun onboardingStepDirectives() {

onboardingDocLink(
label = stringResource("onboarding.step.directives.link"),
url = "https://askimo.chat/docs/desktop/directives/",
url = "https://$DOMAIN/docs/desktop/directives/",
)
}
}
Expand Down Expand Up @@ -836,12 +837,12 @@ private fun onboardingStepReady() {
) {
onboardingLinkItem(
title = stringResource("onboarding.step.ready.link.docs"),
onClick = { uriHandler.openUri("https://askimo.chat/docs/") },
onClick = { uriHandler.openUri("https://$DOMAIN/docs/") },
)
HorizontalDivider()
onboardingLinkItem(
title = stringResource("onboarding.step.ready.link.providers"),
onClick = { uriHandler.openUri("https://askimo.chat/docs/desktop/ai-providers/") },
onClick = { uriHandler.openUri("https://$DOMAIN/docs/desktop/ai-providers/") },
)
HorizontalDivider()
onboardingLinkItem(
Expand Down
22 changes: 9 additions & 13 deletions desktop-shared/src/main/kotlin/io/askimo/ui/plan/PlanDetailView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -795,12 +795,10 @@ private fun agenticStepRow(
when (event) {
is PlanStepEvent.Completed -> {
if (!suppressOutput && !copyableOutput.isNullOrBlank() && outputExpanded) {
SelectionContainer {
markdownText(
markdown = copyableOutput,
modifier = Modifier.padding(top = 2.dp).fillMaxWidth(),
)
}
markdownText(
markdown = copyableOutput,
modifier = Modifier.padding(top = 2.dp).fillMaxWidth(),
)
}
}

Expand Down Expand Up @@ -1171,13 +1169,11 @@ private fun resultPanel(
}
}
HorizontalDivider(modifier = Modifier.padding(bottom = Spacing.medium))
SelectionContainer {
markdownText(
markdown = if (isStreaming) "$displayedOutput▍" else displayedOutput,
modifier = Modifier.fillMaxWidth(),
messageId = executionId,
)
}
markdownText(
markdown = if (isStreaming) "$displayedOutput▍" else displayedOutput,
modifier = Modifier.fillMaxWidth(),
messageId = executionId,
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.plan.domain.PlanDef
import io.askimo.ui.common.i18n.stringResource
import io.askimo.ui.common.theme.AppComponents
Expand Down Expand Up @@ -112,7 +113,7 @@ fun plansGalleryView(
IconButton(
onClick = {
runCatching {
Desktop.getDesktop().browse(URI("https://askimo.chat/docs/desktop/plans/"))
Desktop.getDesktop().browse(URI("https://$DOMAIN/docs/desktop/plans/"))
}
},
modifier = Modifier.pointerHoverIcon(PointerIcon.Hand),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.skills.SkillImporter
import io.askimo.core.skills.SkillRepository
import io.askimo.core.skills.agent.ExternalAgentLoader
Expand Down Expand Up @@ -442,7 +443,7 @@ private fun skillsMainContent(
}
themedTooltip(text = stringResource("skills.view.docs.tooltip")) {
IconButton(
onClick = { runCatching { Desktop.getDesktop().browse(URI("https://askimo.chat/docs/desktop/skills/")) } },
onClick = { runCatching { Desktop.getDesktop().browse(URI("https://$DOMAIN/docs/desktop/skills/")) } },
modifier = Modifier.pointerHoverIcon(PointerIcon.Hand),
) {
Icon(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package io.askimo.ui.shell

import androidx.compose.ui.window.FrameWindowScope
import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.config.AppConfig
import io.askimo.core.i18n.LocalizationManager
import io.askimo.ui.common.theme.ThemeMode
Expand Down Expand Up @@ -448,7 +449,7 @@ object NativeMenuBar {
val docsItem = MenuItem(LocalizationManager.getString("menu.documentation"))
docsItem.addActionListener(
ActionListener {
runCatching { if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(URI("https://askimo.chat/docs/")) }
runCatching { if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(URI("https://$DOMAIN/docs/")) }
},
)
helpMenu.add(docsItem)
Expand All @@ -468,7 +469,7 @@ object NativeMenuBar {
val releaseNotesItem = MenuItem(LocalizationManager.getString("menu.help.release.notes"))
releaseNotesItem.addActionListener(
ActionListener {
runCatching { if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(URI("https://askimo.chat/docs/changelogs/")) }
runCatching { if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(URI("https://$DOMAIN/docs/changelogs/")) }
},
)
helpMenu.add(releaseNotesItem)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
package io.askimo.ui.shell

import io.askimo.core.AppConstants.DOMAIN
import io.askimo.core.i18n.LocalizationManager
import java.awt.Desktop
import java.net.URI
Expand Down Expand Up @@ -32,7 +33,7 @@ object ShareUtils {
if (!Desktop.isDesktopSupported()) return
val desktop = Desktop.getDesktop()
val encoded = URLEncoder.encode(shareText(), "UTF-8")
val url = URLEncoder.encode("https://askimo.chat", "UTF-8")
val url = URLEncoder.encode("https://$DOMAIN", "UTF-8")
val uri = when (target) {
ShareTarget.X -> "https://x.com/intent/tweet?text=$encoded"
ShareTarget.LINKEDIN -> "https://www.linkedin.com/sharing/share-offsite/?url=$url"
Expand Down
Loading
Loading