feat(gui): Quick Picker, Gestionnaire, Éditeur, formulaire de variables, Paramètres
- Picker : recherche instantanée, aperçu, navigation clavier complète, raccourcis actifs seulement dans sa fenêtre (HotIfWinActive) - Gestionnaire : arbre des catégories, favoris/récents/erreurs, aperçu, création depuis le presse-papiers, suppression par la corbeille - Éditeur : métadonnées, insertion/déclaration de variables synchronisées, validation en direct, Tester (prévisualisation résolue, sans injection) - VarForm : un contrôle par type de variable, requis validés, Échap annule toute l'injection et rend le focus
This commit is contained in:
+199
@@ -0,0 +1,199 @@
|
||||
; Manager.ahk — Le Gestionnaire : bibliothèque en trois volets (architecture §9.1).
|
||||
|
||||
class PromptManager {
|
||||
GuiObj := ""
|
||||
TV := ""
|
||||
LV := ""
|
||||
Preview := ""
|
||||
Status := ""
|
||||
NodeFilter := Map() ; id de nœud TreeView -> filtre (catégorie ou __all__/__fav__/__recent__/__errors__)
|
||||
Items := []
|
||||
Filter := "__all__"
|
||||
OnUse := "" ; Func(prompt)
|
||||
OnEdit := "" ; Func(prompt)
|
||||
|
||||
Build(onUse, onEdit) {
|
||||
this.OnUse := onUse
|
||||
this.OnEdit := onEdit
|
||||
g := Gui("-MaximizeBox +Resize", "PromptKey — Gestionnaire")
|
||||
g.SetFont("s10", "Segoe UI")
|
||||
this.GuiObj := g
|
||||
this.TV := g.Add("TreeView", "xm w180 r18")
|
||||
this.TV.OnEvent("ItemSelect", (tv, id) => this.NodeFilter.Has(id) ? this.SetFilter(this.NodeFilter[id]) : "")
|
||||
this.LV := g.Add("ListView", "x+10 w430 r18", ["Titre", "Modifié", "Utilisations"])
|
||||
this.LV.ModifyCol(1, 240)
|
||||
this.LV.ModifyCol(2, 90)
|
||||
this.LV.ModifyCol(3, 95)
|
||||
this.LV.OnEvent("ItemSelect", (lv, row, selected) => selected ? this.UpdatePreview(row) : "")
|
||||
this.LV.OnEvent("DoubleClick", (lv, row) => row > 0 ? this.EditSelected() : "")
|
||||
this.Preview := g.Add("Edit", "x+10 w330 r18 ReadOnly -Wrap", "")
|
||||
|
||||
btnUse := g.Add("Button", "xm w130", "Utiliser")
|
||||
btnUse.OnEvent("Click", (*) => this.UseSelected())
|
||||
btnEdit := g.Add("Button", "x+8 w130", "Modifier")
|
||||
btnEdit.OnEvent("Click", (*) => this.EditSelected())
|
||||
btnNew := g.Add("Button", "x+8 w130", "Nouveau")
|
||||
btnNew.OnEvent("Click", (*) => App.NewPromptInteractive())
|
||||
btnClip := g.Add("Button", "x+8 w180", "Nouveau depuis le presse-papiers")
|
||||
btnClip.OnEvent("Click", (*) => this.NewFromClipboard())
|
||||
btnDup := g.Add("Button", "xm w130", "Dupliquer")
|
||||
btnDup.OnEvent("Click", (*) => this.DuplicateSelected())
|
||||
btnDel := g.Add("Button", "x+8 w130", "Supprimer")
|
||||
btnDel.OnEvent("Click", (*) => this.DeleteSelected())
|
||||
btnFolder := g.Add("Button", "x+8 w160", "Ouvrir le dossier")
|
||||
btnFolder.OnEvent("Click", (*) => Run(App.Settings.LibraryPath))
|
||||
btnReload := g.Add("Button", "x+8 w130", "Recharger")
|
||||
btnReload.OnEvent("Click", (*) => this.Refresh())
|
||||
this.Status := g.Add("Text", "xm w950", "")
|
||||
g.OnEvent("Close", (*) => g.Hide())
|
||||
g.OnEvent("Escape", (*) => g.Hide())
|
||||
}
|
||||
|
||||
Show() {
|
||||
this.Refresh()
|
||||
this.GuiObj.Show("w990")
|
||||
}
|
||||
|
||||
Hide() {
|
||||
this.GuiObj.Hide()
|
||||
}
|
||||
|
||||
SetFilter(filter) {
|
||||
this.Filter := filter
|
||||
this.RefreshList()
|
||||
}
|
||||
|
||||
Refresh() {
|
||||
App.Store.Load()
|
||||
this.NodeFilter := Map()
|
||||
this.TV.Delete()
|
||||
root := this.TV.Add("Bibliothèque")
|
||||
this.NodeFilter[root] := "__all__"
|
||||
for cat in App.Store.Categories() {
|
||||
node := this.TV.Add(cat, root)
|
||||
this.NodeFilter[node] := cat
|
||||
}
|
||||
fav := this.TV.Add("★ Favoris", root)
|
||||
this.NodeFilter[fav] := "__fav__"
|
||||
rec := this.TV.Add("◷ Récents", root)
|
||||
this.NodeFilter[rec] := "__recent__"
|
||||
if (App.Store.ErrorFiles.Length > 0) {
|
||||
err := this.TV.Add("⚠ En erreur (" App.Store.ErrorFiles.Length ")", root)
|
||||
this.NodeFilter[err] := "__errors__"
|
||||
}
|
||||
this.TV.Modify(root, "Expand")
|
||||
this.RefreshList()
|
||||
}
|
||||
|
||||
FilteredPrompts() {
|
||||
out := []
|
||||
switch this.Filter {
|
||||
case "__all__":
|
||||
return App.Store.Prompts
|
||||
case "__fav__":
|
||||
for p in App.Store.Prompts {
|
||||
if p.Favorite
|
||||
out.Push(p)
|
||||
}
|
||||
case "__recent__":
|
||||
for p in App.Store.Prompts {
|
||||
if (App.Usage && App.Usage.Data.Has(p.Id))
|
||||
out.Push(p)
|
||||
}
|
||||
case "__errors__":
|
||||
return App.Store.ErrorFiles
|
||||
default:
|
||||
for p in App.Store.Prompts {
|
||||
if (p.Category = this.Filter)
|
||||
out.Push(p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
RefreshList() {
|
||||
this.Items := this.FilteredPrompts()
|
||||
this.LV.Delete()
|
||||
for p in this.Items {
|
||||
count := App.Usage ? App.Usage.Count(p.Id) : 0
|
||||
marker := (p.Favorite ? "★ " : "") (p.Pinned ? "📌 " : "")
|
||||
this.LV.Add("", marker p.Title, p.Updated, count)
|
||||
}
|
||||
if (this.Items.Length > 0) {
|
||||
this.LV.Modify(1, "Select")
|
||||
this.UpdatePreview(1)
|
||||
} else {
|
||||
this.Preview.Value := ""
|
||||
}
|
||||
this.Status.Text := App.Store.Prompts.Length " prompt(s) dans la bibliothèque · filtre : " this.Filter
|
||||
}
|
||||
|
||||
UpdatePreview(row) {
|
||||
if (row < 1 || row > this.Items.Length)
|
||||
return
|
||||
p := this.Items[row]
|
||||
txt := p.Body
|
||||
if (p.ParseError != "")
|
||||
txt := "⚠ Erreur d'en-tête : " p.ParseError "`r`nFichier : " p.Path "`r`n`r`n" txt
|
||||
else
|
||||
txt := "Catégorie : " p.Category " · Étiquettes : " Join(p.Tags, ", ") "`r`n" (p.Hotkey != "" ? "Raccourci : " p.Hotkey "`r`n" : "") (p.Hotstring != "" ? "Hotstring : ::" p.Hotstring "`r`n" : "") "`r`n" txt
|
||||
this.Preview.Value := txt
|
||||
}
|
||||
|
||||
SelectedPrompt() {
|
||||
row := this.LV.GetNext()
|
||||
if (row >= 1 && row <= this.Items.Length)
|
||||
return this.Items[row]
|
||||
return ""
|
||||
}
|
||||
|
||||
UseSelected() {
|
||||
p := this.SelectedPrompt()
|
||||
if (p = "")
|
||||
return
|
||||
this.Hide()
|
||||
this.OnUse(p)
|
||||
}
|
||||
|
||||
EditSelected() {
|
||||
p := this.SelectedPrompt()
|
||||
if (p = "")
|
||||
return
|
||||
this.OnEdit(p)
|
||||
}
|
||||
|
||||
DuplicateSelected() {
|
||||
p := this.SelectedPrompt()
|
||||
if (p = "")
|
||||
return
|
||||
App.Store.Duplicate(p)
|
||||
this.Refresh()
|
||||
}
|
||||
|
||||
DeleteSelected() {
|
||||
p := this.SelectedPrompt()
|
||||
if (p = "")
|
||||
return
|
||||
r := MsgBox("Envoyer « " p.Title " » à la corbeille ?", "PromptKey", "YesNo Icon?")
|
||||
if (r != "Yes")
|
||||
return
|
||||
App.Store.Delete(p)
|
||||
App.RefreshAfterLibraryChange()
|
||||
this.Refresh()
|
||||
}
|
||||
|
||||
NewFromClipboard() {
|
||||
txt := ClipboardGuard.ClipboardText()
|
||||
if (Trim(txt) = "") {
|
||||
TrayTip("Le presse-papiers ne contient pas de texte.", "PromptKey")
|
||||
return
|
||||
}
|
||||
ib := InputBox("Titre du nouveau prompt :", "PromptKey — Depuis le presse-papiers")
|
||||
if (ib.Result != "OK" || Trim(ib.Value) = "")
|
||||
return
|
||||
item := App.Store.CreateNew(Trim(ib.Value), "Sans catégorie", txt)
|
||||
App.RefreshAfterLibraryChange()
|
||||
this.Refresh()
|
||||
TrayTip("Prompt « " item.Title " » créé depuis le presse-papiers.", "PromptKey")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user