feat: add fullscreen TUI and project updates
- New TUI screens: Main Menu, Compile, Run, REPL, AI Generator, AI Settings, Help - AI configuration persisted in ~/.config/cljnim/config.json - Added illwill dependency for terminal UI - Updated experiments, examples, docs, and core modules
This commit is contained in:
+86
-2
@@ -2,7 +2,7 @@
|
||||
# Supports DeepSeek API and OpenAI-compatible APIs (Xiaomi MiMo, etc.)
|
||||
# API keys are read from environment variables — never hardcoded.
|
||||
|
||||
import std/[httpclient, json, os, strutils, uri]
|
||||
import std/[httpclient, json, os, strutils, uri, tables, times]
|
||||
|
||||
type
|
||||
AiProvider* = enum
|
||||
@@ -64,6 +64,35 @@ proc detectConfig*(): AiConfig =
|
||||
proc hasAiConfig*(): bool =
|
||||
detectConfig().apiKey.len > 0
|
||||
|
||||
type
|
||||
ErrorCacheEntry = object
|
||||
suggestion: string
|
||||
timestamp: float64
|
||||
|
||||
var errorCache = initTable[string, ErrorCacheEntry]()
|
||||
var errorCacheMaxSize = 50
|
||||
|
||||
proc errorCacheKey(errorMsg, fileName: string): string =
|
||||
result = fileName & "::" & errorMsg
|
||||
if result.len > 200:
|
||||
result = result[0..199]
|
||||
|
||||
proc cacheError*(errorMsg, fileName: string, suggestion: string) =
|
||||
if errorCache.len >= errorCacheMaxSize:
|
||||
errorCache.clear()
|
||||
errorCache[errorCacheKey(errorMsg, fileName)] = ErrorCacheEntry(
|
||||
suggestion: suggestion,
|
||||
timestamp: epochTime()
|
||||
)
|
||||
|
||||
proc getCachedError*(errorMsg, fileName: string): string =
|
||||
let key = errorCacheKey(errorMsg, fileName)
|
||||
if key in errorCache:
|
||||
let entry = errorCache[key]
|
||||
if epochTime() - entry.timestamp < 3600.0:
|
||||
return entry.suggestion
|
||||
return ""
|
||||
|
||||
proc buildErrorPrompt*(errorMsg, sourceCode, fileName: string): string =
|
||||
## Build a prompt for the AI to analyze a compiler error
|
||||
result = """You are an expert Clojure/Nim compiler assistant. The user got a compilation error.
|
||||
@@ -98,6 +127,43 @@ Requirements:
|
||||
- Return ONLY the Clojure code, no explanations
|
||||
"""
|
||||
|
||||
proc buildOptimizationPrompt*(code: string): string =
|
||||
## Build a prompt for AI optimization suggestions
|
||||
result = """You are an expert Clojure performance engineer. Analyze this Clojure code and suggest optimizations:
|
||||
|
||||
```clojure
|
||||
""" & code & """
|
||||
```
|
||||
|
||||
Consider:
|
||||
- SIMD/vectorization opportunities
|
||||
- loop/recur vs recursion
|
||||
- Persistent data structure usage
|
||||
- Transients for batch operations
|
||||
- Parallelization opportunities (pmap, reducers)
|
||||
|
||||
Keep response under 200 words. Return ONLY Clojure code suggestions, no explanations.
|
||||
"""
|
||||
|
||||
proc buildDebugPrompt*(code: string, evalResult: string): string =
|
||||
## Build a prompt for AI debugging analysis
|
||||
result = """You are an expert Clojure debugger. Analyze this Clojure expression and its result:
|
||||
|
||||
**Expression:**
|
||||
```clojure
|
||||
""" & code & """
|
||||
```
|
||||
|
||||
**Result:**
|
||||
```
|
||||
""" & evalResult & """
|
||||
```
|
||||
|
||||
Explain what happened step by step. If there's a bug or unexpected behavior, explain why.
|
||||
Keep response under 200 words.
|
||||
Respond in the same language as the user's source code comments (Bulgarian or English).
|
||||
"""
|
||||
|
||||
proc callAiApi*(config: AiConfig, prompt: string): AiResponse =
|
||||
## Call the AI API and return the response
|
||||
if config.apiKey.len == 0:
|
||||
@@ -139,9 +205,15 @@ proc callAiApi*(config: AiConfig, prompt: string): AiResponse =
|
||||
|
||||
proc explainError*(errorMsg, sourceCode, fileName: string): AiResponse =
|
||||
## High-level helper: explain a compiler error using AI
|
||||
let cached = getCachedError(errorMsg, fileName)
|
||||
if cached.len > 0:
|
||||
return AiResponse(ok: true, suggestion: cached)
|
||||
let config = detectConfig()
|
||||
let prompt = buildErrorPrompt(errorMsg, sourceCode, fileName)
|
||||
return callAiApi(config, prompt)
|
||||
let res = callAiApi(config, prompt)
|
||||
if res.ok:
|
||||
cacheError(errorMsg, fileName, res.suggestion)
|
||||
return res
|
||||
|
||||
proc generateCode*(description: string): AiResponse =
|
||||
## High-level helper: generate Clojure code from description
|
||||
@@ -149,6 +221,18 @@ proc generateCode*(description: string): AiResponse =
|
||||
let prompt = buildGenerationPrompt(description)
|
||||
return callAiApi(config, prompt)
|
||||
|
||||
proc optimizeCode*(code: string): AiResponse =
|
||||
## High-level helper: suggest optimizations for Clojure code
|
||||
let config = detectConfig()
|
||||
let prompt = buildOptimizationPrompt(code)
|
||||
return callAiApi(config, prompt)
|
||||
|
||||
proc debugCode*(code: string, evalResult: string): AiResponse =
|
||||
## High-level helper: debug a Clojure expression and its result
|
||||
let config = detectConfig()
|
||||
let prompt = buildDebugPrompt(code, evalResult)
|
||||
return callAiApi(config, prompt)
|
||||
|
||||
proc formatSuggestion*(response: AiResponse): string =
|
||||
## Format AI response for terminal display
|
||||
if not response.ok:
|
||||
|
||||
Reference in New Issue
Block a user