Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
167ab82978 | ||
|
|
a23c0c5b94 | ||
|
|
24b31b0b47 |
190
CHANGELOG.md
190
CHANGELOG.md
@@ -4,196 +4,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
## v0.3.4
|
|
||||||
|
|
||||||
### Changes since v0.3.3
|
|
||||||
|
|
||||||
- fix(ci): replace jq with python3 in release step, add debug output (7ae4017)
|
|
||||||
- feat: AI terminal, Z.AI quota, /model change, formatting fixes, update redirects (8c540eb)
|
|
||||||
- feat(studio): Tab focuses textarea, autocomplete commands (1074b01)
|
|
||||||
- fix(studio): convert newlines to <br/> in AI message rendering (2da0cf9)
|
|
||||||
- fix(config): replace hardcoded model list with free text input (9987a58)
|
|
||||||
- feat(config): providers panel shows only MINIMAX/ZAI with model selector (2827acf)
|
|
||||||
- feat(dashboard): show top 5 most used commands as clickable chips (afb6e77)
|
|
||||||
- fix: tab containers height, dashboard 2-row grid, studio scroll buttons (84be226)
|
|
||||||
- feat(shell): dedicated System Analyst AI, no code execution, analyze system (f9c4cf1)
|
|
||||||
- fix: keep all tabs mounted, switch via CSS display instead of unmount (eda7293)
|
|
||||||
- refactor(config): locale panel with edit/save flow like profile (b55feae)
|
|
||||||
- feat(config): split profile into Personal Info + Preferences sections, centered (54621bd)
|
|
||||||
- feat(studio): improve context compression UI and provider display (6bad294)
|
|
||||||
- fix(config): locale panel show active language/keyboard, add save button (92eb783)
|
|
||||||
- feat(config): dynamic profile panel, generic save, tabs margin fix (8005e97)
|
|
||||||
- fix(dashboard): remove bg graphs, add scrollable lists, show used/total quota (6e76e7d)
|
|
||||||
- feat(chat): add auto-summarization with token tracking UI (e8f6dc4)
|
|
||||||
- feat(dashboard): add background graphs to cards and improve layout (bb03c9f)
|
|
||||||
- feat(dashboard): single-view grid with live CPU/RAM/Net graphs, API quota, processes, and sudo indicator (79d0821)
|
|
||||||
- feat(dashboard): add quota monitoring, process list, and command history (7682717)
|
|
||||||
- refactor(chat): deduplicate streaming code, add multi-conv, and XSS protection (3948a4c)
|
|
||||||
- fix(studio): improve chat context, thinking tags, streaming, and tool results (65804aa)
|
|
||||||
- feat: add Cobra CLI, LSP/MCP registries, workflow engine, and enriched dashboard (2e50366)
|
|
||||||
- feat(agent): refactor AI chat with streaming, agent registry, and tool execution (66b773f)
|
|
||||||
- feat(onboarding): add minimax api key step and AI-powered editor scan (bc5c295)
|
|
||||||
- fix(onboarding): require fields before advancing steps (e19122d)
|
|
||||||
- fix: register missing /api/config/reset and /api/starship/apply-theme routes (8b6a7e8)
|
|
||||||
- fix(config): per-provider form state to avoid field cross-talk (58f8cb0)
|
|
||||||
- fix(onboarding): auto-save on done step, keyboard nav, error feedback (b52fecc)
|
|
||||||
- feat(config): add system panel with reset and starship theme, add onboarding wizard (5bbac49)
|
|
||||||
|
|
||||||
### Downloads
|
|
||||||
|
|
||||||
| Platform | File |
|
|
||||||
|----------|------|
|
|
||||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-linux-amd64.tar.gz) |
|
|
||||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-linux-arm64.tar.gz) |
|
|
||||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-darwin-amd64.tar.gz) |
|
|
||||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-darwin-arm64.tar.gz) |
|
|
||||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-windows-amd64.zip) |
|
|
||||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-windows-arm64.zip) |
|
|
||||||
|
|
||||||
The binary includes both CLI and Desktop modes.
|
|
||||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
**Linux (x86_64)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-linux-amd64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-linux-amd64
|
|
||||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**macOS (Apple Silicon)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-darwin-arm64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-darwin-arm64
|
|
||||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows (x86_64)**
|
|
||||||
```powershell
|
|
||||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.4/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
|
||||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
|
||||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## v0.3.2
|
|
||||||
|
|
||||||
### Changes since v0.3.1
|
|
||||||
|
|
||||||
- chore: update CHANGELOG for v0.3.2-beta.1 (51a599f)
|
|
||||||
- fix: correct version from 3.2 to 0.3.2 (83d7a57)
|
|
||||||
- chore: bump version to 3.2 (0fe82f6)
|
|
||||||
- refactor(config): remove Terminal sub-tab from Configuration page (3b6cc38)
|
|
||||||
- fix(terminal): init payload never sent due to ws.onopen being overwritten (93a22d4)
|
|
||||||
- fix(terminal): improve shell resolution with better error handling and ws proxy support (e0e1e73)
|
|
||||||
- feat(studio): parse AI thinking and tool launch messages in terminal panel (0496ca7)
|
|
||||||
- fix(studio): forward AI thinking chunks to frontend instead of dropping them (b407ab8)
|
|
||||||
- feat(studio): add tool execution and hide AI thinking tags (12df184)
|
|
||||||
- fix(terminal): ignore invalid shell config from race condition (8af6d25)
|
|
||||||
- feat(shell): restore AI assistant panel (4fd599a)
|
|
||||||
- fix(terminal): restore terminal input and cursor visibility (bcba593)
|
|
||||||
- refactor(api): split monolithic handlers.go into focused modules (04b0fff)
|
|
||||||
|
|
||||||
### Downloads
|
|
||||||
|
|
||||||
| Platform | File |
|
|
||||||
|----------|------|
|
|
||||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-amd64.tar.gz) |
|
|
||||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-arm64.tar.gz) |
|
|
||||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-amd64.tar.gz) |
|
|
||||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-arm64.tar.gz) |
|
|
||||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-amd64.zip) |
|
|
||||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-arm64.zip) |
|
|
||||||
|
|
||||||
The binary includes both CLI and Desktop modes.
|
|
||||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
**Linux (x86_64)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-linux-amd64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-linux-amd64
|
|
||||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**macOS (Apple Silicon)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-darwin-arm64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-darwin-arm64
|
|
||||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows (x86_64)**
|
|
||||||
```powershell
|
|
||||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.2/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
|
||||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
|
||||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## v0.3.2-beta.1 (Beta)
|
|
||||||
|
|
||||||
### Commits since v0.3.1
|
|
||||||
|
|
||||||
- fix: correct version from 3.2 to 0.3.2 (83d7a57)
|
|
||||||
|
|
||||||
> This is a **beta** release. Use at your own risk.
|
|
||||||
|
|
||||||
## v0.3.1
|
|
||||||
|
|
||||||
### Changes since v0.3.0
|
|
||||||
|
|
||||||
- refactor(config): remove Terminal sub-tab from Configuration page (95bd824)
|
|
||||||
- fix(terminal): init payload never sent due to ws.onopen being overwritten (252f178)
|
|
||||||
- fix(terminal): improve shell resolution with better error handling and ws proxy support (7dcf505)
|
|
||||||
- feat(studio): parse AI thinking and tool launch messages in terminal panel (8fb93fa)
|
|
||||||
- fix(studio): forward AI thinking chunks to frontend instead of dropping them (5ec373c)
|
|
||||||
- feat(studio): add tool execution and hide AI thinking tags (1eb5a6d)
|
|
||||||
- fix(terminal): ignore invalid shell config from race condition (cd5ebe0)
|
|
||||||
- feat(shell): restore AI assistant panel (2004c15)
|
|
||||||
- fix(terminal): restore terminal input and cursor visibility (9306152)
|
|
||||||
- refactor(api): split monolithic handlers.go into focused modules (e15a034)
|
|
||||||
|
|
||||||
### Downloads
|
|
||||||
|
|
||||||
| Platform | File |
|
|
||||||
|----------|------|
|
|
||||||
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-amd64.tar.gz) |
|
|
||||||
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-arm64.tar.gz) |
|
|
||||||
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-amd64.tar.gz) |
|
|
||||||
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-arm64.tar.gz) |
|
|
||||||
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-amd64.zip) |
|
|
||||||
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-arm64.zip) |
|
|
||||||
|
|
||||||
The binary includes both CLI and Desktop modes.
|
|
||||||
Run `muyue` for TUI, `muyue desktop` for web UI.
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
**Linux (x86_64)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-linux-amd64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-linux-amd64
|
|
||||||
sudo mv muyue-linux-amd64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**macOS (Apple Silicon)**
|
|
||||||
```bash
|
|
||||||
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-darwin-arm64.tar.gz | tar xz
|
|
||||||
chmod +x muyue-darwin-arm64
|
|
||||||
sudo mv muyue-darwin-arm64 /usr/local/bin/muyue
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows (x86_64)**
|
|
||||||
```powershell
|
|
||||||
Invoke-WebRequest -Uri "https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.3.1/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
|
||||||
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
|
||||||
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## v0.3.0
|
## v0.3.0
|
||||||
|
|
||||||
### Changes since v0.2.1
|
### Changes since v0.2.1
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -495,22 +496,34 @@ func (s *Server) handleProvidersQuota(w http.ResponseWriter, r *http.Request) {
|
|||||||
if json.Unmarshal(body, &data) == nil {
|
if json.Unmarshal(body, &data) == nil {
|
||||||
if d, ok := data["data"].(map[string]interface{}); ok {
|
if d, ok := data["data"].(map[string]interface{}); ok {
|
||||||
if limits, ok := d["limits"].([]interface{}); ok {
|
if limits, ok := d["limits"].([]interface{}); ok {
|
||||||
timeLimit := map[string]interface{}{}
|
models := make([]map[string]interface{}, 0)
|
||||||
for _, l := range limits {
|
for _, l := range limits {
|
||||||
if lm, ok := l.(map[string]interface{}); ok && lm["type"] == "TIME_LIMIT" {
|
if lm, ok := l.(map[string]interface{}); ok {
|
||||||
|
name := "Z.AI"
|
||||||
|
if model, ok := lm["model"].(string); ok && model != "" {
|
||||||
|
name = model
|
||||||
|
} else if t, ok := lm["type"].(string); ok && t != "TIME_LIMIT" {
|
||||||
|
name = t
|
||||||
|
}
|
||||||
usage, _ := lm["usage"].(float64)
|
usage, _ := lm["usage"].(float64)
|
||||||
remaining, _ := lm["remaining"].(float64)
|
remaining, _ := lm["remaining"].(float64)
|
||||||
|
limitVal, hasLimit := lm["limit"].(float64)
|
||||||
total := usage + remaining
|
total := usage + remaining
|
||||||
timeLimit = map[string]interface{}{
|
if hasLimit && limitVal > 0 {
|
||||||
"model": "Z.AI",
|
total = limitVal
|
||||||
"used": usage,
|
}
|
||||||
"total": total,
|
if total > 0 {
|
||||||
"remaining": remaining,
|
models = append(models, map[string]interface{}{
|
||||||
|
"model": name,
|
||||||
|
"used": usage,
|
||||||
|
"total": total,
|
||||||
|
"remaining": remaining,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(timeLimit) > 0 {
|
if len(models) > 0 {
|
||||||
q.Data = map[string]interface{}{"models": []map[string]interface{}{timeLimit}}
|
q.Data = map[string]interface{}{"models": models}
|
||||||
q.Healthy = true
|
q.Healthy = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,10 +566,11 @@ func (s *Server) handleRecentCommands(w http.ResponseWriter, r *http.Request) {
|
|||||||
shell = "zsh"
|
shell = "zsh"
|
||||||
}
|
}
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.Split(string(data), "\n")
|
||||||
start := len(lines) - 25
|
start := len(lines) - 50
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(lines) - 1; i >= start; i-- {
|
for i := len(lines) - 1; i >= start; i-- {
|
||||||
line := strings.TrimSpace(lines[i])
|
line := strings.TrimSpace(lines[i])
|
||||||
if line == "" || strings.HasPrefix(line, "#") {
|
if line == "" || strings.HasPrefix(line, "#") {
|
||||||
@@ -573,6 +587,15 @@ func (s *Server) handleRecentCommands(w http.ResponseWriter, r *http.Request) {
|
|||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base := strings.Fields(line)[0]
|
||||||
|
if len(base) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`^[a-zA-Z@./]`).MatchString(base) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
entries = append(entries, cmdEntry{Cmd: line, Shell: shell})
|
entries = append(entries, cmdEntry{Cmd: line, Shell: shell})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "muyue"
|
Name = "muyue"
|
||||||
Version = "0.3.4"
|
Version = "0.3.5"
|
||||||
Author = "La Légion de Muyue"
|
Author = "La Légion de Muyue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -93,13 +93,14 @@ export default function Dashboard({ api, refreshRef }) {
|
|||||||
const minimax = (quota || []).find(p => p.name === 'minimax')
|
const minimax = (quota || []).find(p => p.name === 'minimax')
|
||||||
const zai = (quota || []).find(p => p.name === 'zai')
|
const zai = (quota || []).find(p => p.name === 'zai')
|
||||||
|
|
||||||
const EXCLUDE_CMDS = ['ls', 'cd', 'pwd', 'clear', 'exit', 'history']
|
const EXCLUDE_CMDS = ['ls', 'cd', 'pwd', 'clear', 'exit', 'history', 'cat', 'echo', 'grep', 'export', 'alias', 'unalias', 'set', 'unset', 'source', '.', 'fg', 'bg', 'jobs', 'wait', 'true', 'false', 'yes', 'sleep', 'date', 'whoami', 'id', 'uname', 'hostname', 'uptime', 'df', 'free', 'top', 'htop', 'nano', 'vi', 'vim', 'less', 'more', 'tail', 'head', 'man', 'info', 'which', 'whereis', 'type', 'command', 'hash', 'builtin', 'help']
|
||||||
|
|
||||||
const topCmds = (() => {
|
const topCmds = (() => {
|
||||||
const counts = {}
|
const counts = {}
|
||||||
for (const c of recentCmds) {
|
for (const c of recentCmds) {
|
||||||
const base = c.cmd.split(/\s+/)[0]
|
const base = c.cmd.split(/\s+/)[0]
|
||||||
if (EXCLUDE_CMDS.includes(base) || !base) continue
|
if (!base || base.length < 2 || EXCLUDE_CMDS.includes(base)) continue
|
||||||
|
if (!/^[a-zA-Z@.\/]/.test(base)) continue
|
||||||
counts[base] = (counts[base] || 0) + 1
|
counts[base] = (counts[base] || 0) + 1
|
||||||
}
|
}
|
||||||
return Object.entries(counts)
|
return Object.entries(counts)
|
||||||
|
|||||||
@@ -28,7 +28,16 @@ function renderContent(text) {
|
|||||||
lastIndex = match.index + full.length
|
lastIndex = match.index + full.length
|
||||||
}
|
}
|
||||||
if (lastIndex < text.length) {
|
if (lastIndex < text.length) {
|
||||||
parts.push({ type: 'text', content: text.slice(lastIndex) })
|
const remaining = text.slice(lastIndex)
|
||||||
|
const openBlock = remaining.match(/```(\w*)\n?([\s\S]*)$/)
|
||||||
|
if (openBlock) {
|
||||||
|
if (openBlock.index > 0) {
|
||||||
|
parts.push({ type: 'text', content: remaining.slice(0, openBlock.index) })
|
||||||
|
}
|
||||||
|
parts.push({ type: 'code', lang: openBlock[1] || '', content: openBlock[2] || '' })
|
||||||
|
} else {
|
||||||
|
parts.push({ type: 'text', content: remaining })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
@@ -308,7 +317,7 @@ export default function Shell({ api }) {
|
|||||||
if (tabsRef.current[tabId]) return
|
if (tabsRef.current[tabId]) return
|
||||||
|
|
||||||
const container = document.getElementById(`terminal-${tabId}`)
|
const container = document.getElementById(`terminal-${tabId}`)
|
||||||
if (!container || container.offsetHeight === 0) return
|
if (!container) return
|
||||||
|
|
||||||
const s = settingsRef.current
|
const s = settingsRef.current
|
||||||
const { term, fitAddon } = createTerminal(container, {
|
const { term, fitAddon } = createTerminal(container, {
|
||||||
@@ -368,7 +377,12 @@ export default function Shell({ api }) {
|
|||||||
if (!tab) return
|
if (!tab) return
|
||||||
|
|
||||||
const tryInit = (attempt) => {
|
const tryInit = (attempt) => {
|
||||||
if (attempt > 10) return
|
if (attempt > 20) return
|
||||||
|
const shellCol = document.querySelector('.shell-terminal-col')
|
||||||
|
if (!shellCol || shellCol.offsetParent === null) {
|
||||||
|
setTimeout(() => tryInit(attempt + 1), 150)
|
||||||
|
return
|
||||||
|
}
|
||||||
const container = document.getElementById(`terminal-${tab.id}`)
|
const container = document.getElementById(`terminal-${tab.id}`)
|
||||||
if (!container || container.offsetHeight === 0) {
|
if (!container || container.offsetHeight === 0) {
|
||||||
setTimeout(() => tryInit(attempt + 1), 100)
|
setTimeout(() => tryInit(attempt + 1), 100)
|
||||||
@@ -404,7 +418,17 @@ export default function Shell({ api }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onKey = (e) => {
|
const onKey = (e) => {
|
||||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return
|
||||||
if (!e.altKey) return
|
if (!e.altKey && !(e.key === 'Tab' && e.shiftKey)) return
|
||||||
|
|
||||||
|
if (e.key === 'Tab' && e.shiftKey) {
|
||||||
|
const shellTab = document.querySelector('.shell-layout')
|
||||||
|
if (!shellTab || shellTab.closest('.tab-hidden')) return
|
||||||
|
e.preventDefault()
|
||||||
|
const idx = tabs.findIndex(t => t.id === activeTab)
|
||||||
|
const next = (idx + 1) % tabs.length
|
||||||
|
setActiveTab(tabs[next].id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const num = parseInt(e.key)
|
const num = parseInt(e.key)
|
||||||
if (num >= 1 && num <= tabs.length) {
|
if (num >= 1 && num <= tabs.length) {
|
||||||
|
|||||||
@@ -47,7 +47,16 @@ function renderContent(text) {
|
|||||||
lastIndex = match.index + full.length
|
lastIndex = match.index + full.length
|
||||||
}
|
}
|
||||||
if (lastIndex < text.length) {
|
if (lastIndex < text.length) {
|
||||||
parts.push({ type: 'text', content: text.slice(lastIndex) })
|
const remaining = text.slice(lastIndex)
|
||||||
|
const openBlock = remaining.match(/```(\w*)\n?([\s\S]*)$/)
|
||||||
|
if (openBlock) {
|
||||||
|
if (openBlock.index > 0) {
|
||||||
|
parts.push({ type: 'text', content: remaining.slice(0, openBlock.index) })
|
||||||
|
}
|
||||||
|
parts.push({ type: 'code', lang: openBlock[1] || '', content: openBlock[2] || '' })
|
||||||
|
} else {
|
||||||
|
parts.push({ type: 'text', content: remaining })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ input::placeholder { color: var(--text-disabled); }
|
|||||||
position: relative;
|
position: relative;
|
||||||
background: var(--bg-card); border: 1px solid var(--border);
|
background: var(--bg-card); border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-lg); padding: 14px 16px;
|
border-radius: var(--radius-lg); padding: 14px 16px;
|
||||||
display: flex; flex-direction: column; gap: 8px;
|
display: flex; flex-direction: column; justify-content: center; gap: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user