Compare commits
2 Commits
v0.7.4-bet
...
v0.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8d706cdca | ||
|
|
a9eedab0b5 |
@@ -68,13 +68,6 @@ jobs:
|
|||||||
echo "beta_num=${BETA_NUM}" >> $GITHUB_OUTPUT
|
echo "beta_num=${BETA_NUM}" >> $GITHUB_OUTPUT
|
||||||
echo "Building beta release: ${VERSION}"
|
echo "Building beta release: ${VERSION}"
|
||||||
|
|
||||||
- name: Generate Windows resource (icon)
|
|
||||||
run: |
|
|
||||||
go install github.com/akavel/rsrc@latest
|
|
||||||
RSRC="$(go env GOPATH)/bin/rsrc"
|
|
||||||
$RSRC -ico assets/muyue.ico -arch amd64 -o cmd/muyue/rsrc_windows_amd64.syso
|
|
||||||
$RSRC -ico assets/muyue.ico -arch arm64 -o cmd/muyue/rsrc_windows_arm64.syso
|
|
||||||
|
|
||||||
- name: Build (all platforms)
|
- name: Build (all platforms)
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
|
|||||||
@@ -64,13 +64,6 @@ jobs:
|
|||||||
echo "base=${BASE_VERSION}" >> $GITHUB_OUTPUT
|
echo "base=${BASE_VERSION}" >> $GITHUB_OUTPUT
|
||||||
echo "Building stable release: ${VERSION}"
|
echo "Building stable release: ${VERSION}"
|
||||||
|
|
||||||
- name: Generate Windows resource (icon)
|
|
||||||
run: |
|
|
||||||
go install github.com/akavel/rsrc@latest
|
|
||||||
RSRC="$(go env GOPATH)/bin/rsrc"
|
|
||||||
$RSRC -ico assets/muyue.ico -arch amd64 -o cmd/muyue/rsrc_windows_amd64.syso
|
|
||||||
$RSRC -ico assets/muyue.ico -arch arm64 -o cmd/muyue/rsrc_windows_arm64.syso
|
|
||||||
|
|
||||||
- name: Build (all platforms)
|
- name: Build (all platforms)
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
@@ -145,12 +138,11 @@ jobs:
|
|||||||
echo "sudo mv muyue-darwin-arm64 /usr/local/bin/muyue"
|
echo "sudo mv muyue-darwin-arm64 /usr/local/bin/muyue"
|
||||||
echo "\`\`\`"
|
echo "\`\`\`"
|
||||||
echo ""
|
echo ""
|
||||||
echo "**Windows (x86_64)** — sans privilèges admin, crée les raccourcis Bureau + Menu Démarrer :"
|
echo "**Windows (x86_64)**"
|
||||||
echo "\`\`\`powershell"
|
echo "\`\`\`powershell"
|
||||||
echo "\$dest = \"\$env:LOCALAPPDATA\\Muyue\"; New-Item -ItemType Directory -Force -Path \$dest | Out-Null"
|
echo "Invoke-WebRequest -Uri \"${DL_URL}/muyue-windows-amd64.zip\" -OutFile \"muyue.zip\""
|
||||||
echo "Invoke-WebRequest -Uri \"${DL_URL}/muyue-windows-amd64.zip\" -OutFile \"\$env:TEMP\\muyue.zip\""
|
echo "Expand-Archive -Path \"muyue.zip\" -DestinationPath \".\""
|
||||||
echo "Expand-Archive -Path \"\$env:TEMP\\muyue.zip\" -DestinationPath \$dest -Force"
|
echo "Move-Item muyue-windows-amd64.exe C:\\Windows\\muyue.exe"
|
||||||
echo "& \"\$dest\\muyue-windows-amd64.exe\" install-shortcuts"
|
|
||||||
echo "\`\`\`"
|
echo "\`\`\`"
|
||||||
} > /tmp/stable_changelog.md
|
} > /tmp/stable_changelog.md
|
||||||
echo "path=/tmp/stable_changelog.md" >> $GITHUB_OUTPUT
|
echo "path=/tmp/stable_changelog.md" >> $GITHUB_OUTPUT
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -24,7 +24,6 @@ Thumbs.db
|
|||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.out
|
*.out
|
||||||
*.syso
|
|
||||||
vendor/
|
vendor/
|
||||||
|
|
||||||
# Config with secrets
|
# Config with secrets
|
||||||
|
|||||||
66
CHANGELOG.md
@@ -4,42 +4,50 @@ 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.7.4
|
## v0.7.2
|
||||||
|
|
||||||
### Logo Muyue intégré
|
### Changes since v0.7.0
|
||||||
|
|
||||||
- `LogoMuyue.png` ajouté à la racine + déclinaisons générées dans `assets/` (16/32/64/128/256/512 px) et `assets/muyue.ico` (multi-résolution 16-256 px).
|
- feat(studio): force advanced reflection during browser-test sessions (v0.7.2) (a7d4b31)
|
||||||
- **Binaire Windows** : icône embarquée comme ressource Windows via `github.com/akavel/rsrc` au build CI (génération de `cmd/muyue/rsrc_windows_{amd64,arm64}.syso`). Conséquences :
|
- fix(terminal/windows): fallback to pipes when PTY unsupported (v0.7.1) (fc7a5b9)
|
||||||
- Explorateur Windows affiche l'icône Muyue sur le `.exe`
|
|
||||||
- Les raccourcis créés par `install-shortcuts` héritent de l'icône (via `IconLocation = "$exe,0"`)
|
|
||||||
- Aucune dépendance Go à runtime ; les `.syso` sont gitignorés et regénérés à chaque build
|
|
||||||
- **UI web** : favicon réel (16/32 px), apple-touch-icon (256 px) et logo affiché dans le header à côté de "MUYUE".
|
|
||||||
- Snippet d'install Windows : 1ʳᵉ ligne idempotente (`New-Item -ItemType Directory -Force`) pour gérer le cas d'une ré-exécution après install partielle.
|
|
||||||
- Préservation du logo source en pleine résolution (912×950 RGBA) — pas de perte d'information.
|
|
||||||
|
|
||||||
## v0.7.3
|
### Downloads
|
||||||
|
|
||||||
### Onboarding — focus MiniMax + MiMo
|
| Platform | File |
|
||||||
|
|----------|------|
|
||||||
|
| Linux x86_64 | [muyue-linux-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-linux-amd64.tar.gz) |
|
||||||
|
| Linux ARM64 | [muyue-linux-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-linux-arm64.tar.gz) |
|
||||||
|
| macOS Intel | [muyue-darwin-amd64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-darwin-amd64.tar.gz) |
|
||||||
|
| macOS Apple Silicon | [muyue-darwin-arm64.tar.gz](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-darwin-arm64.tar.gz) |
|
||||||
|
| Windows x86_64 | [muyue-windows-amd64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-windows-amd64.zip) |
|
||||||
|
| Windows ARM64 | [muyue-windows-arm64.zip](https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.2/muyue-windows-arm64.zip) |
|
||||||
|
|
||||||
- L'étape `apikey` du wizard de premier lancement propose désormais **les deux clés** (MiniMax + MiMo) côte à côte ; au moins une doit être validée pour continuer.
|
The binary includes both CLI and Desktop modes.
|
||||||
- Les autres fournisseurs (OpenAI, Anthropic, Z.AI, Ollama) ne sont plus proposés dans le wizard — l'utilisateur les configure ensuite via l'onglet **Configuration** s'il le souhaite. Justification : pour les nouveaux utilisateurs, deux choix simples > six choix qui ralentissent le démarrage.
|
Run `muyue` for TUI, `muyue desktop` for web UI.
|
||||||
- Si MiniMax est validé, il devient le provider actif. Sinon, c'est MiMo. Si les deux sont validés, MiniMax reste actif (peut être basculé via `/model change` plus tard).
|
|
||||||
|
|
||||||
### Install Windows — pas d'admin + raccourcis automatiques
|
### Install
|
||||||
|
|
||||||
|
**Linux (x86_64)**
|
||||||
|
```bash
|
||||||
|
curl -sL https://gitea.legion-muyue.fr/Muyue/MuyueWorkspace/releases/download/v0.7.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.7.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.7.2/muyue-windows-amd64.zip" -OutFile "muyue.zip"
|
||||||
|
Expand-Archive -Path "muyue.zip" -DestinationPath "."
|
||||||
|
Move-Item muyue-windows-amd64.exe C:\Windows\muyue.exe
|
||||||
|
```
|
||||||
|
|
||||||
- **Avant** : la 3ᵉ ligne du snippet d'install (`Move-Item ... C:\Windows\muyue.exe`) échouait avec `UnauthorizedAccessException` sur PowerShell sans élévation.
|
|
||||||
- **Maintenant** : 4 lignes, toutes exécutables sans admin :
|
|
||||||
```powershell
|
|
||||||
$dest = "$env:LOCALAPPDATA\Muyue"
|
|
||||||
Invoke-WebRequest -Uri ".../muyue-windows-amd64.zip" -OutFile "$env:TEMP\muyue.zip"
|
|
||||||
Expand-Archive -Path "$env:TEMP\muyue.zip" -DestinationPath $dest -Force
|
|
||||||
& "$dest\muyue-windows-amd64.exe" install-shortcuts
|
|
||||||
```
|
|
||||||
- Nouvelle commande `muyue install-shortcuts` (Windows uniquement) :
|
|
||||||
- crée `Muyue.lnk` sur le Bureau et dans le Menu Démarrer (résolus via `[Environment]::GetFolderPath`, robuste OneDrive / profils non-standards) ;
|
|
||||||
- utilise WScript.Shell COM via PowerShell pour générer les `.lnk` (pas de dépendance Go ajoutée) ;
|
|
||||||
- ajoute le dossier d'install au `PATH` utilisateur (scope User, pas de modif système).
|
|
||||||
- Une icône custom pourra être branchée plus tard en remplaçant la ressource embed du `.exe` ; pour l'instant, l'icône Windows par défaut du binaire est utilisée.
|
|
||||||
|
|
||||||
## v0.7.2
|
## v0.7.2
|
||||||
|
|
||||||
|
|||||||
BIN
LogoMuyue.png
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 307 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
BIN
assets/muyue.ico
|
Before Width: | Height: | Size: 119 KiB |
@@ -1,151 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// installShortcutsCmd creates desktop + Start Menu shortcuts on Windows so
|
|
||||||
// non-technical users can launch Muyue without opening a terminal. It also
|
|
||||||
// adds the install directory to the user's PATH (per-user, no admin).
|
|
||||||
//
|
|
||||||
// Implementation note: shortcut (.lnk) creation on Windows is most reliable
|
|
||||||
// via WScript.Shell COM. We invoke it via PowerShell — keeps the Go binary
|
|
||||||
// dependency-free and works on any Windows 10+ host.
|
|
||||||
var installShortcutsCmd = &cobra.Command{
|
|
||||||
Use: "install-shortcuts",
|
|
||||||
Short: "Create Desktop + Start Menu shortcuts (Windows only) and add Muyue to PATH",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
fmt.Println("install-shortcuts is a Windows-only command (no-op on this platform)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
exe, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("locate executable: %w", err)
|
|
||||||
}
|
|
||||||
exe, _ = filepath.Abs(exe)
|
|
||||||
installDir := filepath.Dir(exe)
|
|
||||||
|
|
||||||
fmt.Println("Installing Muyue shortcuts...")
|
|
||||||
fmt.Printf(" Executable : %s\n", exe)
|
|
||||||
|
|
||||||
desktop, err := userShellFolder("Desktop")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("locate Desktop folder: %w", err)
|
|
||||||
}
|
|
||||||
startMenu, err := userShellFolder("Programs")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("locate Start Menu Programs folder: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
desktopLnk := filepath.Join(desktop, "Muyue.lnk")
|
|
||||||
startLnk := filepath.Join(startMenu, "Muyue.lnk")
|
|
||||||
|
|
||||||
if err := createWindowsShortcut(desktopLnk, exe, installDir, "Muyue — AI-powered dev environment"); err != nil {
|
|
||||||
return fmt.Errorf("create desktop shortcut: %w", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" Desktop : %s\n", desktopLnk)
|
|
||||||
|
|
||||||
if err := createWindowsShortcut(startLnk, exe, installDir, "Muyue — AI-powered dev environment"); err != nil {
|
|
||||||
return fmt.Errorf("create Start Menu shortcut: %w", err)
|
|
||||||
}
|
|
||||||
fmt.Printf(" Start Menu : %s\n", startLnk)
|
|
||||||
|
|
||||||
if err := addUserPATH(installDir); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, " PATH : warning — could not add %s to user PATH: %v\n", installDir, err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf(" PATH : added %s (open a new terminal to pick it up)\n", installDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nDone — double-click the Muyue icon on your Desktop to launch.")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(installShortcutsCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// userShellFolder asks Windows for a user shell folder via PowerShell —
|
|
||||||
// resilient to OneDrive redirection and non-default profile locations.
|
|
||||||
// `which` is one of: Desktop, Programs (Start Menu Programs), StartMenu.
|
|
||||||
func userShellFolder(which string) (string, error) {
|
|
||||||
ps := fmt.Sprintf(`[Environment]::GetFolderPath('%s')`, which)
|
|
||||||
out, err := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", ps).Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
path := stripTrailingWhitespace(string(out))
|
|
||||||
if path == "" {
|
|
||||||
return "", fmt.Errorf("empty path for %s", which)
|
|
||||||
}
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripTrailingWhitespace(s string) string {
|
|
||||||
for len(s) > 0 && (s[len(s)-1] == '\n' || s[len(s)-1] == '\r' || s[len(s)-1] == ' ' || s[len(s)-1] == '\t') {
|
|
||||||
s = s[:len(s)-1]
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// createWindowsShortcut generates a .lnk via WScript.Shell COM. The arguments
|
|
||||||
// are passed through PowerShell variables (not interpolated into the script
|
|
||||||
// body) to avoid quoting issues with paths containing spaces or special chars.
|
|
||||||
func createWindowsShortcut(lnkPath, target, workingDir, description string) error {
|
|
||||||
script := `
|
|
||||||
$lnk = $env:MUYUE_LNK
|
|
||||||
$target = $env:MUYUE_TARGET
|
|
||||||
$workdir = $env:MUYUE_WORKDIR
|
|
||||||
$desc = $env:MUYUE_DESC
|
|
||||||
$wsh = New-Object -ComObject WScript.Shell
|
|
||||||
$sc = $wsh.CreateShortcut($lnk)
|
|
||||||
$sc.TargetPath = $target
|
|
||||||
$sc.WorkingDirectory = $workdir
|
|
||||||
$sc.Description = $desc
|
|
||||||
$sc.IconLocation = "$target,0"
|
|
||||||
$sc.Save()
|
|
||||||
`
|
|
||||||
cmd := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", script)
|
|
||||||
cmd.Env = append(os.Environ(),
|
|
||||||
"MUYUE_LNK="+lnkPath,
|
|
||||||
"MUYUE_TARGET="+target,
|
|
||||||
"MUYUE_WORKDIR="+workingDir,
|
|
||||||
"MUYUE_DESC="+description,
|
|
||||||
)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("powershell: %v: %s", err, string(out))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// addUserPATH appends installDir to the user's PATH if not already present.
|
|
||||||
// Uses PowerShell to read/write the User-scope environment via .NET API,
|
|
||||||
// which broadcasts WM_SETTINGCHANGE so new processes pick it up.
|
|
||||||
func addUserPATH(installDir string) error {
|
|
||||||
script := `
|
|
||||||
$dir = $env:MUYUE_INSTALL_DIR
|
|
||||||
$current = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
||||||
if ($current -eq $null) { $current = '' }
|
|
||||||
$parts = $current -split ';' | Where-Object { $_ -ne '' }
|
|
||||||
if ($parts -notcontains $dir) {
|
|
||||||
$new = if ($current -eq '') { $dir } else { "$current;$dir" }
|
|
||||||
[Environment]::SetEnvironmentVariable('Path', $new, 'User')
|
|
||||||
}
|
|
||||||
`
|
|
||||||
cmd := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", script)
|
|
||||||
cmd.Env = append(os.Environ(), "MUYUE_INSTALL_DIR="+installDir)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("powershell: %v: %s", err, string(out))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "muyue"
|
Name = "muyue"
|
||||||
Version = "0.7.4"
|
Version = "0.7.2"
|
||||||
Author = "La Légion de Muyue"
|
Author = "La Légion de Muyue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#0A0A0C" />
|
<meta name="theme-color" content="#0A0A0C" />
|
||||||
<title>Muyue</title>
|
<title>muyue</title>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⬡</text></svg>" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/muyue.png" />
|
|
||||||
<link rel="shortcut icon" href="/muyue.png" />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 81 KiB |
@@ -103,7 +103,6 @@ export default function App() {
|
|||||||
<div className="app-layout">
|
<div className="app-layout">
|
||||||
<header className="header">
|
<header className="header">
|
||||||
<div className="header-brand">
|
<div className="header-brand">
|
||||||
<img src="/muyue-64.png" alt="Muyue" className="header-logo-img" width="22" height="22" style={{ borderRadius: 4, verticalAlign: 'middle' }} />
|
|
||||||
<span className="header-logo">MUYUE</span>
|
<span className="header-logo">MUYUE</span>
|
||||||
<span className="header-version">v{info.version || '...'}</span>
|
<span className="header-version">v{info.version || '...'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,12 +23,8 @@ export default function OnboardingWizard({ api, onComplete }) {
|
|||||||
language: 'fr',
|
language: 'fr',
|
||||||
keyboard: 'azerty',
|
keyboard: 'azerty',
|
||||||
apikey: '',
|
apikey: '',
|
||||||
apikey_mimo: '',
|
|
||||||
editor: '',
|
editor: '',
|
||||||
})
|
})
|
||||||
const [keyValidMimo, setKeyValidMimo] = useState(false)
|
|
||||||
const [errorMimo, setErrorMimo] = useState(null)
|
|
||||||
const [validatingMimo, setValidatingMimo] = useState(false)
|
|
||||||
const [editorList, setEditorList] = useState(BASE_EDITORS)
|
const [editorList, setEditorList] = useState(BASE_EDITORS)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
@@ -56,7 +52,7 @@ export default function OnboardingWizard({ api, onComplete }) {
|
|||||||
case 'name': return answers.name.trim().length > 0
|
case 'name': return answers.name.trim().length > 0
|
||||||
case 'language': return !!answers.language
|
case 'language': return !!answers.language
|
||||||
case 'keyboard': return !!answers.keyboard
|
case 'keyboard': return !!answers.keyboard
|
||||||
case 'apikey': return (keyValid || keyValidMimo) && !scanning
|
case 'apikey': return keyValid && !scanning
|
||||||
case 'editor': return true
|
case 'editor': return true
|
||||||
case 'done': return true
|
case 'done': return true
|
||||||
default: return true
|
default: return true
|
||||||
@@ -177,33 +173,6 @@ export default function OnboardingWizard({ api, onComplete }) {
|
|||||||
setValidating(false)
|
setValidating(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleValidateKeyMimo = async () => {
|
|
||||||
if (!answers.apikey_mimo.trim()) return
|
|
||||||
setValidatingMimo(true)
|
|
||||||
setErrorMimo(null)
|
|
||||||
try {
|
|
||||||
await api.validateProvider({
|
|
||||||
name: 'mimo',
|
|
||||||
api_key: answers.apikey_mimo,
|
|
||||||
model: 'mimo-v2.5-pro',
|
|
||||||
base_url: 'https://token-plan-ams.xiaomimimo.com/v1',
|
|
||||||
})
|
|
||||||
setKeyValidMimo(true)
|
|
||||||
// Save MiMo. If MiniMax wasn't validated yet, MiMo becomes the active provider.
|
|
||||||
await api.saveProvider({
|
|
||||||
name: 'mimo',
|
|
||||||
api_key: answers.apikey_mimo,
|
|
||||||
model: 'mimo-v2.5-pro',
|
|
||||||
base_url: 'https://token-plan-ams.xiaomimimo.com/v1',
|
|
||||||
active: !keyValid,
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
setErrorMimo(err.message || 'Clé invalide')
|
|
||||||
setKeyValidMimo(false)
|
|
||||||
}
|
|
||||||
setValidatingMimo(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
@@ -232,15 +201,6 @@ export default function OnboardingWizard({ api, onComplete }) {
|
|||||||
active: true,
|
active: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (answers.apikey_mimo.trim()) {
|
|
||||||
await api.saveProvider({
|
|
||||||
name: 'mimo',
|
|
||||||
api_key: answers.apikey_mimo,
|
|
||||||
model: 'mimo-v2.5-pro',
|
|
||||||
base_url: 'https://token-plan-ams.xiaomimimo.com/v1',
|
|
||||||
active: !answers.apikey.trim(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onComplete()
|
onComplete()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message || 'Erreur lors de la sauvegarde')
|
setError(err.message || 'Erreur lors de la sauvegarde')
|
||||||
@@ -323,71 +283,38 @@ export default function OnboardingWizard({ api, onComplete }) {
|
|||||||
|
|
||||||
{current.key === 'apikey' && (
|
{current.key === 'apikey' && (
|
||||||
<div className="onboarding-step">
|
<div className="onboarding-step">
|
||||||
<div className="onboarding-title">Clés API</div>
|
<div className="onboarding-title">Clé API MiniMax</div>
|
||||||
<div className="onboarding-desc">
|
<div className="onboarding-desc">
|
||||||
Renseignez au moins l'une des deux clés pour activer l'assistant. Les autres fournisseurs (OpenAI, Anthropic, Ollama, Z.AI) se configurent plus tard depuis l'onglet Configuration.
|
Entrez votre clé API MiniMax pour activer l'assistant IA. La clé est obligatoire pour continuer.
|
||||||
</div>
|
</div>
|
||||||
|
<input
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 }}>
|
className="onboarding-input"
|
||||||
<label style={{ fontSize: 12, color: 'var(--text-tertiary)', fontWeight: 600 }}>MiniMax</label>
|
placeholder="sk-xxxxxxxxxxxxxxxx"
|
||||||
<input
|
type="password"
|
||||||
className="onboarding-input"
|
value={answers.apikey}
|
||||||
placeholder="sk-xxxxxxxxxxxxxxxx (MiniMax)"
|
onChange={e => { setAnswers(a => ({ ...a, apikey: e.target.value })); setKeyValid(false); setError(null) }}
|
||||||
type="password"
|
autoFocus
|
||||||
value={answers.apikey}
|
/>
|
||||||
onChange={e => { setAnswers(a => ({ ...a, apikey: e.target.value })); setKeyValid(false); setError(null) }}
|
{error && !keyValid && <div className="onboarding-required">{error}</div>}
|
||||||
autoFocus
|
{keyValid && !scanning && <div className="onboarding-valid">Clé valide ✓ — Appuyez sur Entrée pour continuer</div>}
|
||||||
/>
|
|
||||||
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
|
|
||||||
<button
|
|
||||||
className="sm primary"
|
|
||||||
onClick={handleValidateKey}
|
|
||||||
disabled={validating || !answers.apikey.trim()}
|
|
||||||
>
|
|
||||||
{validating ? 'Validation...' : 'Valider MiniMax'}
|
|
||||||
</button>
|
|
||||||
{keyValid && <span className="onboarding-valid">✓ MiniMax OK</span>}
|
|
||||||
{error && !keyValid && <span className="onboarding-required">{error}</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 12 }}>
|
|
||||||
<label style={{ fontSize: 12, color: 'var(--text-tertiary)', fontWeight: 600 }}>MiMo (Xiaomi)</label>
|
|
||||||
<input
|
|
||||||
className="onboarding-input"
|
|
||||||
placeholder="sk-xxxxxxxxxxxxxxxx (MiMo)"
|
|
||||||
type="password"
|
|
||||||
value={answers.apikey_mimo}
|
|
||||||
onChange={e => { setAnswers(a => ({ ...a, apikey_mimo: e.target.value })); setKeyValidMimo(false); setErrorMimo(null) }}
|
|
||||||
/>
|
|
||||||
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
|
|
||||||
<button
|
|
||||||
className="sm primary"
|
|
||||||
onClick={handleValidateKeyMimo}
|
|
||||||
disabled={validatingMimo || !answers.apikey_mimo.trim()}
|
|
||||||
>
|
|
||||||
{validatingMimo ? 'Validation...' : 'Valider MiMo'}
|
|
||||||
</button>
|
|
||||||
{keyValidMimo && <span className="onboarding-valid">✓ MiMo OK</span>}
|
|
||||||
{errorMimo && !keyValidMimo && <span className="onboarding-required">{errorMimo}</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{scanning && (
|
{scanning && (
|
||||||
<div className="onboarding-scanning" style={{ marginTop: 8 }}>
|
<div className="onboarding-scanning">
|
||||||
<Loader size={14} className="spin-icon" />
|
<Loader size={14} className="spin-icon" />
|
||||||
<span>{scanMessage}</span>
|
<span>{scanMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{requiredError && (
|
{requiredError && <div className="onboarding-required">Veuillez valider votre clé API pour continuer</div>}
|
||||||
<div className="onboarding-required" style={{ marginTop: 8 }}>
|
<div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
|
||||||
Veuillez valider au moins une clé (MiniMax ou MiMo) pour continuer.
|
<button
|
||||||
</div>
|
className="sm primary"
|
||||||
)}
|
onClick={handleValidateKey}
|
||||||
{(keyValid || keyValidMimo) && !scanning && (
|
disabled={validating || !answers.apikey.trim()}
|
||||||
<div className="onboarding-valid" style={{ marginTop: 8 }}>
|
>
|
||||||
Au moins une clé est valide — appuyez sur Suivant pour continuer.
|
{validating ? 'Validation...' : 'Valider la clé'}
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
|
{!keyValid && !error && answers.apikey.trim() && (
|
||||||
|
<div className="onboarding-hint">Entrez votre clé puis cliquez "Valider la clé"</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||