implement markdown-based hint system and add MarkdownHint component
This commit is contained in:
@@ -170,6 +170,9 @@ dependencies {
|
||||
//noinspection UseTomlInstead
|
||||
implementation("com.pierfrancescosoffritti.androidyoutubeplayer:core:13.0.0")
|
||||
|
||||
// Markdown rendering
|
||||
implementation(libs.compose.markdown)
|
||||
|
||||
// Compression
|
||||
testImplementation (libs.zstd.jni)
|
||||
implementation(libs.zstd.jni.get().toString() + "@aar")
|
||||
|
||||
86
app/src/main/assets/hints/example_hint.md
Normal file
86
app/src/main/assets/hints/example_hint.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# How to Scan for AI Models
|
||||
|
||||
This guide explains how to use the **Scan** feature to discover and add AI models to your app.
|
||||
|
||||
## How Scanning Works
|
||||
|
||||
The scan feature searches for available AI models on your device or network.
|
||||
|
||||
> **Note:** Results depend on your API key permissions.
|
||||
|
||||
### Key Points
|
||||
|
||||
- Only public models are shown by default
|
||||
- Private models require additional setup
|
||||
- Try again if no models are found
|
||||
|
||||
## Why Some Models Are Missing
|
||||
|
||||
Some models may not appear in the scan results due to:
|
||||
|
||||
| Reason | Description | Icon |
|
||||
|--------|-------------|------|
|
||||
| Restricted access | Model requires special permissions | 🔒 |
|
||||
| Not suitable | Model type not supported | ⚠️ |
|
||||
| Text only | Only text-based models are supported | ✓ |
|
||||
|
||||
### Model Tiers
|
||||
|
||||
We recommend these tiers for optimal performance:
|
||||
|
||||
- **Nano** - Fastest, for simple tasks
|
||||
- **Mini** - Balanced speed and capability
|
||||
- **Small** - Good for most tasks
|
||||
- **Medium** - More capable, slower
|
||||
- **Large** - Most capable, paid only
|
||||
|
||||
## Tips for Success
|
||||
|
||||
1. **Verify your API key** is active and has correct permissions
|
||||
2. **Select the correct organization** from your account
|
||||
3. **Type model names manually** if scanning doesn't find them
|
||||
4. **Prefer instruct or chat models** for text generation
|
||||
|
||||
```kotlin
|
||||
// Example: Manual model addition
|
||||
val model = Model(
|
||||
name = "llama3.2",
|
||||
type = ModelType.TEXT,
|
||||
provider = "ollama"
|
||||
)
|
||||
```
|
||||
|
||||
## Visual Guide
|
||||
|
||||
### Step 1: Initiate Scan
|
||||
|
||||
Click the scan button to search for available models.
|
||||
|
||||
### Step 2: Select Model Type
|
||||
|
||||
Choose between different model categories:
|
||||
|
||||
- **Text Chat** - For conversational AI
|
||||
- **Instruct** - For direct instructions
|
||||
- **Complete** - For text completion
|
||||
|
||||
### Step 3: Add & Validate
|
||||
|
||||
Add the selected model and validate it works correctly.
|
||||
|
||||
---
|
||||
|
||||
## Can't Find Your Model?
|
||||
|
||||
If your model doesn't appear in the scan results:
|
||||
|
||||
1. Check if the model is running locally or accessible via API
|
||||
2. Verify network connectivity
|
||||
3. Try adding it manually by entering the model details
|
||||
|
||||
> **Pro Tip:** You can always add models manually by clicking the "+" button in the models screen.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2024-01-15*
|
||||
*For more help, visit our documentation website.*
|
||||
@@ -21,6 +21,11 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object ApiKeyHint: LegacyHint() {
|
||||
override val titleRes: Int = R.string.hint_how_to_connect_to_an_ai
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object CategoryHint : LegacyHint() {
|
||||
override val titleRes: Int = R.string.category_hint_intro
|
||||
|
||||
|
||||
@@ -26,6 +26,11 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object CategoryHintScreen : LegacyHint() {
|
||||
override val titleRes: Int = R.string.category_hint_intro
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package eu.gaudian.translator.view.hints
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import eu.gaudian.translator.R
|
||||
|
||||
/**
|
||||
* Example of a migrated hint using the new markdown-based approach.
|
||||
*
|
||||
* This demonstrates how to migrate from the old LegacyHint format to the new
|
||||
* markdown-based format.
|
||||
*
|
||||
* Benefits:
|
||||
* - Easier to manage and translate (no code changes needed)
|
||||
* - Better separation of concerns
|
||||
* - Consistent styling across all hints
|
||||
* - Support for rich formatting (tables, code blocks, etc.)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example 1: Loading markdown content from string (simple).
|
||||
*/
|
||||
@Composable
|
||||
fun getApiKeyMarkdownHint(): String {
|
||||
return """
|
||||
# How to Connect to an AI Model
|
||||
|
||||
This guide explains how to connect your app to an AI model using an API key.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To use AI models in your app, you need to provide a valid API key.
|
||||
|
||||
> **Note:** Keep your API key secure and never share it publicly.
|
||||
|
||||
## Key Status Indicators
|
||||
|
||||
| Status | Icon | Meaning |
|
||||
|--------|------|---------|
|
||||
| Active | ✅ | Key is valid and working |
|
||||
| Missing | ⚠️ | Key is not set |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. Verify the key is correct
|
||||
2. Ensure proper permissions
|
||||
3. Check your quota
|
||||
|
||||
```json
|
||||
{
|
||||
"api_key": "sk-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ApiKeyMarkdownHint() {
|
||||
val content = getApiKeyMarkdownHint()
|
||||
MarkdownHint(
|
||||
markdownContent = content,
|
||||
title = stringResource(R.string.hint_how_to_connect_to_an_ai)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Example 2: Loading markdown from assets file (recommended for production).
|
||||
*/
|
||||
@Composable
|
||||
fun loadMarkdownFromAssets(fileName: String): String {
|
||||
val context = LocalContext.current
|
||||
return try {
|
||||
context.assets.open("hints/$fileName").bufferedReader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
"Error loading markdown file: ${e.message}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for programmatic hint loading.
|
||||
*/
|
||||
data class MarkdownHintDefinition(
|
||||
val fileName: String,
|
||||
val titleRes: Int
|
||||
) {
|
||||
fun loadContent(context: Context): String {
|
||||
return context.assets.open("hints/$fileName").bufferedReader().use { it.readText() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-defined hints ready for migration.
|
||||
*/
|
||||
object MarkdownHints {
|
||||
val API_KEY = MarkdownHintDefinition(
|
||||
fileName = "api_key_hint.md",
|
||||
titleRes = R.string.hint_how_to_connect_to_an_ai
|
||||
)
|
||||
val CATEGORY = MarkdownHintDefinition(
|
||||
fileName = "category_hint.md",
|
||||
titleRes = R.string.category_hint_intro
|
||||
)
|
||||
val LEARNING_STAGES = MarkdownHintDefinition(
|
||||
fileName = "learning_stages_hint.md",
|
||||
titleRes = R.string.learning_stages_title
|
||||
)
|
||||
val SORTING = MarkdownHintDefinition(
|
||||
fileName = "sorting_hint.md",
|
||||
titleRes = R.string.sorting_hint_title
|
||||
)
|
||||
val VOCABULARY_PROGRESS = MarkdownHintDefinition(
|
||||
fileName = "vocabulary_progress_hint.md",
|
||||
titleRes = R.string.hint_vocabulary_progress_hint_title
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview for the migrated API Key hint.
|
||||
*/
|
||||
@Preview
|
||||
@Composable
|
||||
fun ApiKeyMarkdownHintPreview() {
|
||||
MaterialTheme {
|
||||
ApiKeyMarkdownHint()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview for loading from assets.
|
||||
*/
|
||||
@Preview
|
||||
@Composable
|
||||
fun LoadFromAssetsPreview() {
|
||||
MaterialTheme {
|
||||
val content = loadMarkdownFromAssets("example_hint.md")
|
||||
MarkdownHint(
|
||||
markdownContent = content,
|
||||
title = stringResource(R.string.hint_title_hints_overview)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,11 @@ private data class LearningStage(
|
||||
val interval: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object LearningStagesHint : LegacyHint() {
|
||||
override val titleRes: Int = R.string.learning_stages_title
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package eu.gaudian.translator.view.hints
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.jeziellago.compose.markdowntext.MarkdownText
|
||||
import eu.gaudian.translator.R
|
||||
|
||||
/**
|
||||
* Markdown-styled hint content using the jeziellago compose-markdown library.
|
||||
* This provides beautiful, consistent rendering of markdown content with
|
||||
* support for headings, lists, tables, code blocks, and more.
|
||||
*
|
||||
* Usage:
|
||||
* - Create a .md file in assets/hints/ (e.g., "api_key_hint.md")
|
||||
* - Load it using context.assets.open() or pass raw markdown string
|
||||
* - Use MarkdownHint composable for styled rendering
|
||||
*
|
||||
* Supported markdown:
|
||||
* - Headings (# ## ###)
|
||||
* - Bold (**text**) and italic (*text*)
|
||||
* - Lists (- item, 1. item)
|
||||
* - Tables (| col | col |)
|
||||
* - Code blocks (```code```)
|
||||
* - Blockquotes (> quote)
|
||||
* - Links ([text](url))
|
||||
*/
|
||||
@Composable
|
||||
fun MarkdownHint(
|
||||
markdownContent: String,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
// Optional title with icon header
|
||||
title?.let {
|
||||
HeaderWithIcon(
|
||||
title = it,
|
||||
subtitle = null
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
// Render markdown
|
||||
MarkdownText(
|
||||
markdown = markdownContent,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview for MarkdownHint with sample content.
|
||||
*/
|
||||
@Preview
|
||||
@Composable
|
||||
fun MarkdownHintPreview() {
|
||||
val sampleMarkdown = """
|
||||
# Welcome to the Hint System
|
||||
|
||||
This is a **markdown-based** hint that provides _beautiful_ and consistent styling.
|
||||
|
||||
## Features
|
||||
|
||||
- **Rich text formatting** - bold, italic, strikethrough
|
||||
- **Headings** - H1 through H6 levels
|
||||
- **Lists** - ordered and unordered
|
||||
- **Code blocks** - with syntax highlighting
|
||||
- **Tables** - for structured data
|
||||
- **Links** - styled clickable references
|
||||
- **Blockquotes** - for highlighted content
|
||||
|
||||
## How to Use
|
||||
|
||||
1. Create a `.md` file in `assets/hints/`
|
||||
2. Load it using `context.assets.open("hints/your_hint.md")`
|
||||
3. Pass the content to `MarkdownHint()`
|
||||
|
||||
> **Tip:** You can also embed UI elements by combining markdown with HintElements!
|
||||
|
||||
## Code Example
|
||||
|
||||
```kotlin
|
||||
val markdown = loadMarkdownFromAssets("hints/scan_hint.md")
|
||||
MarkdownHint(
|
||||
markdownContent = markdown,
|
||||
title = "Scan Hint"
|
||||
)
|
||||
```
|
||||
|
||||
## Table Example
|
||||
|
||||
| Feature | Status | Priority |
|
||||
|---------|--------|----------|
|
||||
| Headings | ✅ | High |
|
||||
| Lists | ✅ | High |
|
||||
| Tables | ✅ | Medium |
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2024-01-15*
|
||||
""".trimIndent()
|
||||
|
||||
MaterialTheme {
|
||||
MarkdownHint(
|
||||
markdownContent = sampleMarkdown,
|
||||
title = stringResource(R.string.hint_title_hints_overview)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for loading markdown content from assets.
|
||||
*/
|
||||
data class MarkdownHintData(
|
||||
val fileName: String,
|
||||
val titleRes: Int
|
||||
) {
|
||||
/**
|
||||
* Load and return the markdown content as a string.
|
||||
*/
|
||||
fun loadContent(androidContext: android.content.Context): String {
|
||||
return androidContext.assets.open("hints/$fileName")
|
||||
.bufferedReader()
|
||||
.use { it.readText() }
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,11 @@ import eu.gaudian.translator.view.composable.AppButton
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
import eu.gaudian.translator.view.composable.AppOutlinedTextField
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object SortingScreenHint : LegacyHint() {
|
||||
override val titleRes: Int = R.string.sorting_hint_title
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
|
||||
/**
|
||||
* TODO: Migrate to markdown-based hint format.
|
||||
* Create a .md file in assets/hints/ and use MarkdownHint composable instead.
|
||||
* See MarkdownHint.kt for implementation details.
|
||||
*/
|
||||
object VocabularyProgressHint : LegacyHint() {
|
||||
override val titleRes: Int = R.string.hint_vocabulary_progress_hint_title
|
||||
|
||||
|
||||
Reference in New Issue
Block a user