Core Server-Driven UI

SessionTask UI System

Build reactive, AI-powered interfaces using pure Kotlin. Stream content, manage complex tasks, and create interactive human-in-the-loop workflows.

Reality Check: Kotlin to Browser

⚙️ analyze_task.kt
fun analyze(task: SessionTask) {
    task.header("Analysis Results", level = 2)
    val results = task.newTask() // Sub-task
    task.add("Scanning repository...")
    
    // Stream findings
    findings.forEach { f ->
        results.add("<div class='finding'>${f.msg}</div>")
    }
    
    results.complete()
    task.add("✓ Done")
    task.complete()
}
🖥️ Live Preview

Analysis Results

src/main/App.kt
Unused import detected
Scanning repository...
✓ Done

UI Component Library

📋

SessionTask

The primary canvas. Manages HTML buffers, unique message IDs, and lifecycle states (spinner to complete).

📑

TabbedDisplay

Dynamic tabbed interface. Supports programmatically adding, updating, or streaming content into tabs.

💬

Discussable

Human-in-the-loop workflow. Generate, review, and revise content with a blocking acceptance loop.

♻️

Retryable

Extends TabbedDisplay for LLM operations. Adds a "Recycle" button to re-run generations in new tabs.

🛠️

Agent Patterns

Utilities like displayMapInTabs and AddApplyFileDiffLinks for rapid agent UI development.

📁

File Management

Save files to session directories, generate download links, and create live-updating log streams.

API Reference

kotlin
// Basic content
task.add("Hello **World**!", markdown = true)
task.header("Title", level = 2)
task.echo("User prompt style")

// Specialized output
task.hideable("Dismissible message")
task.verbose("Debug info (hidden by default)")
task.error(exception) // Renders red box + stack trace
task.image(bufferedImage) // Auto-saves and renders <img>

// Markdown & Mermaid
val html = MarkdownUtil.renderMarkdown(raw, ui = task.ui)
task.add(html)
💡 Best Practices

Lifecycle Management

Always call task.complete(). Failing to do so leaves the loading spinner active indefinitely, degrading user experience.

Thread Safety

SessionTask methods are thread-safe. Use task.ui.pool for heavy computations to avoid blocking the UI thread.

Dynamic Updates

Use the StringBuilder returned by add() and call task.update() to refresh existing elements.

Blocking Operations

Discussable is blocking. Use task.ui.scheduledThreadPoolExecutor for delayed or periodic execution.

Advanced Workflow Patterns

💬 Discussable: Human-in-the-Loop
val finalResult = Discussable(
    task = task,
    heading = "Drafting Email",
    userMessage = { "Draft an email" },
    initialResponse = { prompt ->
        MyObject(llm.generate(prompt))
    },
    outputFn = { design ->
        design.toHtml()
    },
    reviseResponse = { history ->
        llm.chat(history)
    }
).call() // Blocks until Accept

Flow: Generate → Review → Revise → Accept

♻️ Retryable: LLM Regeneration
val retryable = Retryable(task) { buffer ->
    // Runs on init and each recycle
    val result = performExpensiveOperation()
    result // Return HTML content
}
// Each retry creates a new tab
// User clicks ♻ to regenerate

Use case: LLM generations that may need multiple attempts