Files
Loki/internal/jean/cli_app.go
T
nathaninline e2475c6688 v0.5.10 : le produit s'appelle AJEAN + diff des ecritures dans le chat + variante AJEAN sombre
- renommage : « Jean » devient « AJEAN » dans tous les textes visibles (titre de
  l'UI, proprietes du binaire Windows, barre de menus macOS / zone de
  notification, messages du terminal, README). La commande, les services, les
  chemins et le depot ne changent pas.
- nouveau thème « AJEAN sombre » (même habillage pixel, palette nuit) ; les
  regles de mise en page sont partagees via [data-theme^="ajean"].
- l'IA montre ce qu'elle ecrit : les outils edit / mem_add / mem_edit renvoient
  un diff ligne a ligne, affiche en vert (+) et rouge (-) dans la bulle d'outil
  et persiste avec la conversation (chat_diff.go).
- ecritures idempotentes : si la modification est deja en place, edit et
  mem_edit repondent « deja a jour » au lieu d'une erreur trompeuse ; un appel
  d'outil rigoureusement identique n'est plus rejoue dans le meme tour.
- saisie du chat auto-agrandissante (une ligne au repos en thème ChatGPT, deux
  ailleurs), bouton d'envoi enfin aligne, astuce clavier detachee de la carte.
- editeur de preset relu pour le thème AJEAN (plus de double encadrement), champ
  + bouton de telechargement recolles dans tous les thèmes, interrupteurs carres.
- emojis retires de l'interface (les glyphes monochromes restent).
2026-07-28 10:20:19 +02:00

84 lines
2.7 KiB
Go

package jean
// cli_app.go — expérience « application » de Jean.
//
// Quand on double-clique sur le binaire (aucun argument, console fraîche), au
// lieu d'afficher l'aide dans une console qui se ferme, Jean démarre son UI web,
// l'ouvre dans le navigateur ET pose une icône dans la zone de notification
// Windows (voir sys_tray_windows.go) : on voit que Jean tourne et on le pilote
// (« Ouvrir Jean » / « Quitter »).
import (
"fmt"
"net"
"net/http"
"os"
"time"
)
const appPort = 8090
// cmdApp lance l'UI web, l'ouvre dans le navigateur et fait tourner l'icône de
// la zone de notification (Windows). Sur une machine vierge, il crée d'abord le
// dossier de données + config.env pour que l'interface s'ouvre quand même
// (l'écran d'accueil prendra le relais pour le choix du modèle).
func cmdApp(args []string) error {
url := fmt.Sprintf("http://localhost:%d", appPort)
if _, err := os.Stat(confPath()); err != nil {
// Première fois : installe le minimum (dossiers + config de départ).
_ = cmdInstall(nil)
}
addr := fmt.Sprintf("0.0.0.0:%d", appPort)
ln, err := net.Listen("tcp", addr)
if err != nil {
// Jean tourne déjà sur ce port : on ouvre juste l'UI sur l'instance
// existante plutôt que d'échouer.
fmt.Printf("AJEAN est déjà lancé — ouverture de %s\n", url)
return openBrowser(url)
}
// UN SEUL process propriétaire de la conversation, comme le service jean-link
// sous Linux : l'app sert l'UI locale ET le tunnel avec le MÊME mux. Deux
// process qui servent la conversation (objet en mémoire persisté dans
// conversation.json) donnent deux fils divergents entre le local et
// app.ajean.link — le bug qu'on a vécu avec le worker détaché.
appOwnsLink = true
appWebMux = newWebMux()
go func() { _ = http.Serve(ln, appWebMux) }()
go func() {
if readLinkToken() == "" {
return
}
// Worker détaché rescapé (version antérieure, autre copie de l'app) : il
// possède SA propre conversation et écrase la nôtre. On le supprime avant
// de prendre la main.
killForeignLinkWorker()
startAppLink(appWebMux)
}()
sp := showSplash("Lancement d'AJEAN en cours…")
waitServerReady(url)
_ = openBrowser(url)
time.Sleep(900 * time.Millisecond) // laisse le navigateur s'afficher par-dessus le splash
sp.close()
runTray(url) // icône zone de notification ; bloque jusqu'à « Quitter »
return nil
}
// waitServerReady attend que l'UI réponde (jusqu'à ~4 s) pour n'ouvrir le
// navigateur qu'une fois le serveur prêt.
func waitServerReady(url string) {
c := &http.Client{Timeout: 400 * time.Millisecond}
for i := 0; i < 20; i++ {
if resp, err := c.Get(url); err == nil {
resp.Body.Close()
return
}
time.Sleep(150 * time.Millisecond)
}
}