diff --git a/.gitignore b/.gitignore index 07bb5b2..1088248 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ vendor/ .DS_Store Thumbs.db -runix* \ No newline at end of file +runix* +!setting/runix.local diff --git a/go/chat/streamer.go b/go/chat/streamer.go index c3a48b7..bfb44c7 100644 --- a/go/chat/streamer.go +++ b/go/chat/streamer.go @@ -10,6 +10,16 @@ import ( "github.com/fatih/color" ) +const asciiBanner = `____/\\\\\______/\\________/\\__/\\\_____/\\__/\\\\\\\\__/\\_______/\\_ + __/\\///////\\___\/\\_______\/\\_\/\\\\\___\/\\_\/////\\///__\///\\___/\\\/__ + _\/\\_____\/\\___\/\\_______\/\\_\/\\\/\\__\/\\_____\/\\_______\///\\\\\/____ + _\/\\\\\\\\\/____\/\\_______\/\\_\/\\//\\_\/\\_____\/\\_________\//\\______ + _\/\\//////\\____\/\\_______\/\\_\/\\\\//\\/\\_____\/\\__________\/\\______ + _\/\\____\//\\___\/\\_______\/\\_\/\\_\//\\\/\\_____\/\\__________/\\\\\_____ + _\/\\_____\//\\__\//\\______/\\__\/\\__\//\\\\\_____\/\\________/\\////\\___ + _\/\\______\//\\__\///\\\\\\\/___\/\\___\//\\\__/\\\\\\\\\/__/\\\/___\///\\_ + _\///________\///_____\////////_____\///_____\////__/\\\\\\///__\///_______\///__` + type ChatStreamer struct { typingSpeed time.Duration } @@ -23,6 +33,9 @@ func NewChatStreamer() *ChatStreamer { // StreamResponse streams the AI response in real-time with special handling for code blocks func (cs *ChatStreamer) StreamResponse(role, response string) { + // Format response for better streaming + response = cs.FormatResponse(response) + // Colors for different roles userColor := color.New(color.FgCyan, color.Bold) botColor := color.New(color.FgGreen, color.Bold) @@ -133,6 +146,13 @@ func (cs *ChatStreamer) showProgressBar(message string, steps int) { fmt.Print("\n") } +// FormatResponse normalizes whitespace and line endings +func (cs *ChatStreamer) FormatResponse(resp string) string { + resp = strings.ReplaceAll(resp, "\r\n", "\n") + resp = regexp.MustCompile(`\n{3,}`).ReplaceAllString(resp, "\n\n") + return strings.TrimSpace(resp) +} + // ResponsePart represents a part of the response (text or code) type ResponsePart struct { content string @@ -213,6 +233,8 @@ func (cs *ChatStreamer) containsCodeBlocks(response string) bool { "```js", "```python", "```go", + "```bash", + "```shell", } for _, pattern := range patterns { @@ -225,6 +247,7 @@ func (cs *ChatStreamer) containsCodeBlocks(response string) bool { // ShowWelcome shows the welcome message with style func (cs *ChatStreamer) ShowWelcome() { + cs.ShowBanner() titleColor := color.New(color.FgMagenta, color.Bold) subtitleColor := color.New(color.FgCyan) tipColor := color.New(color.FgYellow) @@ -260,3 +283,9 @@ func (cs *ChatStreamer) ShowThinking() { thinkingColor.Print("💭 Generando respuesta...\n") } + +// ShowBanner prints the Runix ASCII art banner +func (cs *ChatStreamer) ShowBanner() { + bannerColor := color.New(color.FgHiMagenta, color.Bold) + bannerColor.Println(asciiBanner) +} diff --git a/go/cli/commands/commands.go b/go/cli/commands/commands.go index ea405ab..2402055 100644 --- a/go/cli/commands/commands.go +++ b/go/cli/commands/commands.go @@ -15,14 +15,16 @@ import ( "runix/go/filemanager" "runix/go/openrouter" "runix/go/processor" + "runix/go/settings" "runix/go/webserver" ) var ( - globalFileManager *filemanager.FileManager - globalWebServer *webserver.Server - globalProcessor *processor.ResponseProcessor - globalStreamer *chat.ChatStreamer + globalFileManager *filemanager.FileManager + globalWebServer *webserver.Server + globalConfigServer *webserver.ConfigServer + globalProcessor *processor.ResponseProcessor + globalStreamer *chat.ChatStreamer ) // Execute parses CLI arguments and dispatches to subcommands. @@ -60,6 +62,9 @@ func usage() { func initializeSystem() error { var err error + // Load local environment variables + _ = settings.LoadEnv("setting/runix.local") + // Initialize file manager globalFileManager, err = filemanager.NewFileManager() if err != nil { @@ -75,6 +80,10 @@ func initializeSystem() error { // Initialize chat streamer globalStreamer = chat.NewChatStreamer() + // Start configuration server + globalConfigServer = webserver.NewConfigServer("setting") + globalConfigServer.StartInBackground() + return nil } @@ -89,6 +98,9 @@ func setupCleanup() { if globalWebServer != nil { globalWebServer.Stop() } + if globalConfigServer != nil { + globalConfigServer.Stop() + } if globalFileManager != nil { globalFileManager.Cleanup() } @@ -216,6 +228,9 @@ func demoCmd(args []string) { if globalWebServer != nil { globalWebServer.Stop() } + if globalConfigServer != nil { + globalConfigServer.Stop() + } if globalFileManager != nil { globalFileManager.Cleanup() } diff --git a/go/settings/env.go b/go/settings/env.go new file mode 100644 index 0000000..b1118fc --- /dev/null +++ b/go/settings/env.go @@ -0,0 +1,31 @@ +package settings + +import ( + "bufio" + "os" + "strings" +) + +// LoadEnv reads key=value pairs from the given file and sets them as environment variables. +func LoadEnv(path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + os.Setenv(key, value) + } + } + return scanner.Err() +} diff --git a/go/webserver/config_server.go b/go/webserver/config_server.go new file mode 100644 index 0000000..752e88b --- /dev/null +++ b/go/webserver/config_server.go @@ -0,0 +1,44 @@ +package webserver + +import ( + "fmt" + "net/http" + "time" +) + +// ConfigServer serves the configuration interface on port 2222. +type ConfigServer struct { + Dir string + server *http.Server +} + +// NewConfigServer creates a new configuration server. +func NewConfigServer(dir string) *ConfigServer { + return &ConfigServer{Dir: dir} +} + +// StartInBackground starts the server asynchronously. +func (cs *ConfigServer) StartInBackground() chan error { + errChan := make(chan error, 1) + go func() { + fs := http.FileServer(http.Dir(cs.Dir)) + cs.server = &http.Server{ + Addr: ":2222", + Handler: fs, + } + fmt.Println("⚙️ Configuración disponible en http://localhost:2222") + if err := cs.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + errChan <- err + } + }() + time.Sleep(100 * time.Millisecond) + return errChan +} + +// Stop stops the configuration server. +func (cs *ConfigServer) Stop() error { + if cs.server != nil { + return cs.server.Close() + } + return nil +} diff --git a/setting/chat.rn b/setting/chat.rn new file mode 100644 index 0000000..2f35e29 --- /dev/null +++ b/setting/chat.rn @@ -0,0 +1,2 @@ +# chat preferences +welcome_message=true diff --git a/setting/colors.rn b/setting/colors.rn new file mode 100644 index 0000000..11827e7 --- /dev/null +++ b/setting/colors.rn @@ -0,0 +1,4 @@ +# Default color settings +prompt_color=cyan +user_color=green +bot_color=yellow diff --git a/setting/pront.rn b/setting/pront.rn new file mode 100644 index 0000000..64be8c2 --- /dev/null +++ b/setting/pront.rn @@ -0,0 +1,2 @@ +# prompt settings +speed=fast diff --git a/setting/runix.local b/setting/runix.local new file mode 100644 index 0000000..164cc3a --- /dev/null +++ b/setting/runix.local @@ -0,0 +1,2 @@ +# Environment variables for Runix +EXAMPLE_VAR=example