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