feat(core): moteur PromptKey — bibliothèque .md, variables, injection, raccourcis
- PromptStore/Frontmatter : un prompt = un fichier Markdown, en-tête au sous-ensemble strict du document d'architecture (§7), écriture UTF-8 - VariableEngine : variables typées + automatiques (selection, clipboard, date, app, cursor), résolution et profils de destination - Injector/ClipboardGuard : focus capturé/rendu, presse-papiers sauvegardé puis restauré conditionnellement, aucun envoi automatique par défaut - Search : classement flou insensible aux accents, pondéré par l'usage - UsageStats (usage.json), HotkeyManager (conflits signalés, jamais muets) - Settings : config.ini + profils par IA (claude, chatgpt, qwen, terminal…) - PromptKey.ahk : tray, instance unique, capture du contexte, surveillance de la bibliothèque, installation du pack au premier lancement
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
; Injector.ahk — SEUL module autorisé à toucher au focus, au presse-papiers et à la frappe
|
||||
; simulée pour livrer un prompt (architecture §11.1). Aucun autre module n'appelle
|
||||
; Send / A_Clipboard pour l'injection.
|
||||
|
||||
class Injector {
|
||||
Guard := ClipboardGuard()
|
||||
SettingsRef := ""
|
||||
|
||||
__New(settings) {
|
||||
this.SettingsRef := settings
|
||||
}
|
||||
|
||||
; options : Map { hwnd, method ("clipboard"|"sendtext"), sendAfter (0/1), sensitive (0/1), cursorFromEnd }
|
||||
; Retourne Map { ok, method, error }
|
||||
Deliver(text, options) {
|
||||
st := this.SettingsRef
|
||||
method := options.Has("method") && options["method"] != "" ? options["method"] : st.Method
|
||||
hwnd := options.Has("hwnd") ? options["hwnd"] : 0
|
||||
if (Trim(text) = "")
|
||||
return Map("ok", false, "method", method, "error", "Texte vide : rien à injecter.")
|
||||
if (hwnd && !WinExist("ahk_id " hwnd)) {
|
||||
; Fenêtre disparue : copie de secours (§11.5).
|
||||
A_Clipboard := text
|
||||
TrayTip("La fenêtre d'origine a disparu — le prompt a été copié dans le presse-papiers.", "PromptKey")
|
||||
return Map("ok", false, "method", method, "error", "Fenêtre cible disparue, prompt copié.")
|
||||
}
|
||||
|
||||
if (method = "sendtext" && StrLen(text) > 4000) {
|
||||
TrayTip("Prompt long (" StrLen(text) " caractères) : la frappe simulée sera lente.", "PromptKey")
|
||||
}
|
||||
|
||||
; Rendre le focus à la fenêtre d'origine.
|
||||
if (hwnd) {
|
||||
try WinActivate("ahk_id " hwnd)
|
||||
try {
|
||||
if !WinWaitActive("ahk_id " hwnd, , 0.5) {
|
||||
A_Clipboard := text
|
||||
TrayTip("Impossible de réactiver la fenêtre cible — prompt copié dans le presse-papiers.", "PromptKey")
|
||||
return Map("ok", false, "method", method, "error", "Réactivation de la fenêtre impossible.")
|
||||
}
|
||||
}
|
||||
Sleep(60)
|
||||
}
|
||||
|
||||
if (method = "sendtext") {
|
||||
SendText(text)
|
||||
} else {
|
||||
this.Guard.Save()
|
||||
this.Guard.InjectedText := text
|
||||
A_Clipboard := text
|
||||
if !ClipWait(0.5) {
|
||||
this.Guard.RestoreIfUnchanged()
|
||||
return Map("ok", false, "method", method, "error", "Presse-papiers non prêt.")
|
||||
}
|
||||
Send("^v")
|
||||
Sleep(st.PasteDelayMs)
|
||||
; Restauration (ou vidage) différée, conditionnelle (§11.3).
|
||||
guard := this.Guard
|
||||
sensitive := options.Has("sensitive") ? options["sensitive"] : 0
|
||||
if sensitive
|
||||
SetTimer(() => guard.WipeIfUnchanged(), -st.RestoreDelayMs)
|
||||
else
|
||||
SetTimer(() => guard.RestoreIfUnchanged(), -st.RestoreDelayMs)
|
||||
}
|
||||
|
||||
; Marqueur {{cursor}} : replacer le curseur (§10.3).
|
||||
if (options.Has("cursorFromEnd") && options["cursorFromEnd"] > 0) {
|
||||
n := options["cursorFromEnd"]
|
||||
Send("{Left " n "}")
|
||||
}
|
||||
|
||||
; Envoi automatique : seulement si le profil le demande explicitement (§12.4, D5).
|
||||
if (options.Has("sendAfter") && options["sendAfter"]) {
|
||||
delay := st.SendAfterDelayMs
|
||||
TrayTip("Envoi automatique dans " Round(delay / 1000, 1) " s — Échap pour annuler.", "PromptKey")
|
||||
; Fenêtre d'annulation : on écoute Échap pendant le délai.
|
||||
cancelled := false
|
||||
endTime := A_TickCount + delay
|
||||
while (A_TickCount < endTime) {
|
||||
if GetKeyState("Escape", "P") {
|
||||
cancelled := true
|
||||
break
|
||||
}
|
||||
Sleep(25)
|
||||
}
|
||||
if (!cancelled)
|
||||
Send("{Enter}")
|
||||
else
|
||||
TrayTip("Envoi annulé — le texte reste en place.", "PromptKey")
|
||||
}
|
||||
Util.Log("Injection OK (" method ", " StrLen(text) " caractères)")
|
||||
return Map("ok", true, "method", method, "error", "")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user