Files
Spark b102a4a206 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
2026-10-11 10:17:30 -04:00

136 lines
6.6 KiB
AutoHotkey

; 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"
}
}