- 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
162 lines
5.3 KiB
AutoHotkey
162 lines
5.3 KiB
AutoHotkey
; Picker.ahk — Le Quick Picker : surface principale de PromptKey (architecture §8).
|
|
; Fenêtre pré-créée, recherche instantanée, navigation clavier, aperçu, retour de focus propre.
|
|
|
|
class PromptPicker {
|
|
GuiObj := ""
|
|
SearchEdit := ""
|
|
LV := ""
|
|
Preview := ""
|
|
Status := ""
|
|
Items := [] ; prompts correspondant aux lignes affichées
|
|
Ctx := Map()
|
|
OnUse := "" ; Func(prompt, useDefaults)
|
|
OnEdit := "" ; Func(prompt)
|
|
ProfileLabel := ""
|
|
HotkeysArmed := false
|
|
|
|
Build(onUse, onEdit) {
|
|
this.OnUse := onUse
|
|
this.OnEdit := onEdit
|
|
g := Gui("-MaximizeBox +Resize", "PromptKey")
|
|
g.SetFont("s10", "Segoe UI")
|
|
this.GuiObj := g
|
|
g.Add("Text", "xm", "Rechercher :")
|
|
this.SearchEdit := g.Add("Edit", "x+10 w470", "")
|
|
this.SearchEdit.OnEvent("Change", (*) => this.RefreshList())
|
|
this.ProfileLabel := g.Add("Text", "x+10 w180", "")
|
|
this.LV := g.Add("ListView", "xm w500 r14", ["Titre", "Catégorie", "Étiquettes"])
|
|
this.LV.ModifyCol(1, 240)
|
|
this.LV.ModifyCol(2, 110)
|
|
this.LV.ModifyCol(3, 140)
|
|
this.LV.OnEvent("ItemSelect", (lv, row, selected) => selected ? this.UpdatePreview(row) : "")
|
|
this.LV.OnEvent("DoubleClick", (lv, row) => row > 0 ? this.UseSelected(false) : "")
|
|
this.Preview := g.Add("Edit", "x+15 w330 r14 ReadOnly -Wrap", "")
|
|
this.Status := g.Add("Text", "xm w850", "")
|
|
g.OnEvent("Escape", (*) => this.Hide())
|
|
g.OnEvent("Close", (*) => this.Hide())
|
|
}
|
|
|
|
Show(ctx) {
|
|
this.Ctx := ctx
|
|
this.SearchEdit.Value := ""
|
|
this.RefreshList()
|
|
this.GuiObj.Show("Center w880")
|
|
this.SearchEdit.Focus()
|
|
this.ArmHotkeys()
|
|
if (ctx.Has("profileName"))
|
|
this.ProfileLabel.Text := "Profil : " ctx["profileName"]
|
|
}
|
|
|
|
Hide() {
|
|
this.DisarmHotkeys()
|
|
this.GuiObj.Hide()
|
|
}
|
|
|
|
; Raccourcis propres au Picker, actifs seulement quand sa fenêtre est active (§8.2).
|
|
ArmHotkeys() {
|
|
if this.HotkeysArmed
|
|
return
|
|
HotIfWinActive("ahk_id " this.GuiObj.Hwnd)
|
|
Hotkey("Enter", (*) => this.UseSelected(false), "On")
|
|
Hotkey("^Enter", (*) => this.UseSelected(true), "On")
|
|
Hotkey("^e", (*) => this.EditSelected(), "On")
|
|
Hotkey("Down", (*) => this.MoveSelection(1), "On")
|
|
Hotkey("Up", (*) => this.MoveSelection(-1), "On")
|
|
Hotkey("^c", (*) => this.CopySelected(), "On")
|
|
HotIfWinActive()
|
|
this.HotkeysArmed := true
|
|
}
|
|
|
|
DisarmHotkeys() {
|
|
if !this.HotkeysArmed
|
|
return
|
|
HotIfWinActive("ahk_id " this.GuiObj.Hwnd)
|
|
for hk in ["Enter", "^Enter", "^e", "Down", "Up", "^c"] {
|
|
try Hotkey(hk, "Off")
|
|
}
|
|
HotIfWinActive()
|
|
this.HotkeysArmed := false
|
|
}
|
|
|
|
MoveSelection(delta) {
|
|
row := this.LV.GetNext()
|
|
if (row = 0)
|
|
row := delta > 0 ? 1 : this.Items.Length
|
|
else
|
|
row := Max(1, Min(this.Items.Length, row + delta))
|
|
if (row >= 1 && row <= this.Items.Length) {
|
|
this.LV.Modify(0, "-Select")
|
|
this.LV.Modify(row, "Select Focus Vis")
|
|
this.UpdatePreview(row)
|
|
}
|
|
}
|
|
|
|
RefreshList() {
|
|
q := this.SearchEdit.Value
|
|
usageData := App.Usage ? App.Usage.Data : Map()
|
|
this.Items := Search.Filter(App.Store.Prompts, q, usageData)
|
|
this.LV.Delete()
|
|
for p in this.Items {
|
|
marker := ""
|
|
if p.Pinned
|
|
marker .= "📌 "
|
|
if p.Favorite
|
|
marker .= "★ "
|
|
this.LV.Add("", marker p.Title, p.Category, Join(p.Tags, ", "))
|
|
}
|
|
if (this.Items.Length > 0) {
|
|
this.LV.Modify(1, "Select Focus")
|
|
this.UpdatePreview(1)
|
|
} else {
|
|
this.Preview.Value := "(aucun prompt ne correspond)"
|
|
}
|
|
this.Status.Text := this.Items.Length " résultat(s) · Entrée Utiliser · Ctrl+Entrée Valeurs par défaut · Ctrl+E Modifier · Échap Fermer"
|
|
}
|
|
|
|
UpdatePreview(row) {
|
|
if (row < 1 || row > this.Items.Length)
|
|
return
|
|
p := this.Items[row]
|
|
txt := p.Body
|
|
sel := this.Ctx.Has("selection") ? this.Ctx["selection"] : ""
|
|
if (sel != "")
|
|
txt .= "`r`n`r`n— Sélection capturée (" StrLen(sel) " caractères) : « " Util.Truncate(sel, 120) " »"
|
|
if (p.ParseError != "")
|
|
txt := "⚠ Erreur de fichier : " p.ParseError "`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(useDefaults) {
|
|
p := this.SelectedPrompt()
|
|
if (p = "")
|
|
return
|
|
this.Hide()
|
|
this.OnUse(p, useDefaults)
|
|
}
|
|
|
|
EditSelected() {
|
|
p := this.SelectedPrompt()
|
|
if (p = "")
|
|
return
|
|
this.Hide()
|
|
this.OnEdit(p)
|
|
}
|
|
|
|
; Ctrl+C : copie le corps brut (sans résolution) — la résolution complète passe par l'injection.
|
|
CopySelected() {
|
|
p := this.SelectedPrompt()
|
|
if (p = "")
|
|
return
|
|
A_Clipboard := p.Body
|
|
TrayTip("Corps du prompt « " p.Title " » copié.", "PromptKey")
|
|
this.Hide()
|
|
}
|
|
}
|