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,135 @@
|
||||
; Settings.ahk — Paramètres de PromptKey (config.ini) et profils de destination.
|
||||
; Architecture §12 (profils) et §17.1 (paramètres en INI).
|
||||
|
||||
class Settings {
|
||||
Path := ""
|
||||
LibraryPath := ""
|
||||
PickerHotkey := "^!Space"
|
||||
ManagerHotkey := "^!m"
|
||||
NewHotkey := "^!n"
|
||||
ReplayHotkey := "^!v"
|
||||
Method := "clipboard" ; clipboard | sendtext
|
||||
PasteDelayMs := 150
|
||||
RestoreDelayMs := 1500
|
||||
SendAfterDelayMs := 800
|
||||
ExternalEditor := "" ; vide = éditeur par défaut de Windows pour .md
|
||||
Profiles := Map() ; nom -> Map(preambule, postambule, methode, sendAfter, appMatch, titreMatch)
|
||||
|
||||
__New(configPath) {
|
||||
this.Path := configPath
|
||||
this.Load()
|
||||
}
|
||||
|
||||
Load() {
|
||||
ini := this.Path
|
||||
if FileExist(ini) {
|
||||
this.LibraryPath := IniRead(ini, "General", "LibraryPath", "")
|
||||
this.PickerHotkey := IniRead(ini, "General", "PickerHotkey", this.PickerHotkey)
|
||||
this.ManagerHotkey := IniRead(ini, "General", "ManagerHotkey", this.ManagerHotkey)
|
||||
this.NewHotkey := IniRead(ini, "General", "NewHotkey", this.NewHotkey)
|
||||
this.ReplayHotkey := IniRead(ini, "General", "ReplayHotkey", this.ReplayHotkey)
|
||||
this.Method := IniRead(ini, "Injection", "Method", this.Method)
|
||||
this.PasteDelayMs := Integer(IniRead(ini, "Injection", "PasteDelayMs", this.PasteDelayMs))
|
||||
this.RestoreDelayMs := Integer(IniRead(ini, "Injection", "RestoreDelayMs", this.RestoreDelayMs))
|
||||
this.SendAfterDelayMs := Integer(IniRead(ini, "Injection", "SendAfterDelayMs", this.SendAfterDelayMs))
|
||||
this.ExternalEditor := IniRead(ini, "Editeur", "ExternalEditor", "")
|
||||
}
|
||||
if (this.LibraryPath = "" || !DirExist(this.LibraryPath)) {
|
||||
; Mode portable : dossier Bibliothèque à côté du script. Sinon Documents\PromptKey.
|
||||
portable := A_ScriptDir "\Bibliothèque"
|
||||
this.LibraryPath := DirExist(portable) ? portable : A_MyDocuments "\PromptKey\Bibliothèque"
|
||||
}
|
||||
DirCreate(this.LibraryPath)
|
||||
this.LoadProfiles()
|
||||
}
|
||||
|
||||
Save() {
|
||||
ini := this.Path
|
||||
IniWrite(this.LibraryPath, ini, "General", "LibraryPath")
|
||||
IniWrite(this.PickerHotkey, ini, "General", "PickerHotkey")
|
||||
IniWrite(this.ManagerHotkey, ini, "General", "ManagerHotkey")
|
||||
IniWrite(this.NewHotkey, ini, "General", "NewHotkey")
|
||||
IniWrite(this.ReplayHotkey, ini, "General", "ReplayHotkey")
|
||||
IniWrite(this.Method, ini, "Injection", "Method")
|
||||
IniWrite(this.PasteDelayMs, ini, "Injection", "PasteDelayMs")
|
||||
IniWrite(this.RestoreDelayMs, ini, "Injection", "RestoreDelayMs")
|
||||
IniWrite(this.SendAfterDelayMs, ini, "Injection", "SendAfterDelayMs")
|
||||
IniWrite(this.ExternalEditor, ini, "Editeur", "ExternalEditor")
|
||||
}
|
||||
|
||||
; Profils intégrés, surchargeables par des sections [Profil.<nom>] dans config.ini.
|
||||
LoadProfiles() {
|
||||
this.Profiles := Map()
|
||||
base := Map(
|
||||
"défaut", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", ""),
|
||||
"claude", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "Claude"),
|
||||
"chatgpt", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "ChatGPT"),
|
||||
"gemini", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "Gemini"),
|
||||
"qwen", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "Qwen"),
|
||||
"deepseek", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "DeepSeek"),
|
||||
"terminal", Map("preambule", "", "postambule", "", "methode", "sendtext", "sendAfter", 0, "appMatch", "wt.exe,powershell.exe,cmd.exe,Code.exe", "titreMatch", ""),
|
||||
"courriel", Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "")
|
||||
)
|
||||
for name, p in base
|
||||
this.Profiles[name] := p
|
||||
; Surcharge INI : lire le fichier et repérer les sections [Profil.xxx].
|
||||
if FileExist(this.Path) {
|
||||
text := FileRead(this.Path, "UTF-8")
|
||||
current := ""
|
||||
for line in StrSplit(text, "`n", "`r") {
|
||||
line := Trim(line)
|
||||
if RegExMatch(line, "^\[Profil\.(.+)\]$", &m) {
|
||||
current := StrLower(m[1])
|
||||
if !this.Profiles.Has(current)
|
||||
this.Profiles[current] := Map("preambule", "", "postambule", "", "methode", "", "sendAfter", 0, "appMatch", "", "titreMatch", "")
|
||||
} else if (current != "" && RegExMatch(line, "^\[")) {
|
||||
current := ""
|
||||
} else if (current != "" && InStr(line, "=")) {
|
||||
pos := InStr(line, "=")
|
||||
key := StrLower(Trim(SubStr(line, 1, pos - 1)))
|
||||
val := Trim(SubStr(line, pos + 1))
|
||||
val := StrReplace(val, "\n", "`n")
|
||||
switch key {
|
||||
case "preambule": this.Profiles[current]["preambule"] := val
|
||||
case "postambule": this.Profiles[current]["postambule"] := val
|
||||
case "methode": this.Profiles[current]["methode"] := val
|
||||
case "sendafter": this.Profiles[current]["sendAfter"] := (val = "true" || val = "1") ? 1 : 0
|
||||
case "appmatch": this.Profiles[current]["appMatch"] := val
|
||||
case "titrematch": this.Profiles[current]["titreMatch"] := val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetProfile(name) {
|
||||
name := StrLower(Trim(name))
|
||||
if (name != "" && this.Profiles.Has(name))
|
||||
return this.Profiles[name]
|
||||
return this.Profiles["défaut"]
|
||||
}
|
||||
|
||||
ProfileNames() {
|
||||
names := []
|
||||
for name in this.Profiles
|
||||
names.Push(name)
|
||||
return names
|
||||
}
|
||||
|
||||
; Auto-détection (§12.3, étape 3) : AppMatch (processus) puis TitreMatch (titre de fenêtre).
|
||||
DetectProfile(processName, windowTitle) {
|
||||
for name, p in this.Profiles {
|
||||
if (name = "défaut")
|
||||
continue
|
||||
if (p["appMatch"] != "") {
|
||||
for app in StrSplit(p["appMatch"], ",") {
|
||||
if (StrLower(Trim(app)) = StrLower(processName))
|
||||
return name
|
||||
}
|
||||
}
|
||||
if (p["titreMatch"] != "" && InStr(windowTitle, p["titreMatch"]))
|
||||
return name
|
||||
}
|
||||
return "défaut"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user