mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
v0.2.19 : preset actif de nouveau détecté (empreinte ignore les clés appareil MEM_MODE/CRAWL4AI_URL) + surbrillance bleue du preset actif
This commit is contained in:
1 parent
20dd09abc6
commit
acb08d3c6a
6 files changed
+48
-13
No files matched your search
@@ -16,7 +16,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Version = "0.2.18"
|
||||
const Version = "0.2.19"
|
||||
|
||||
func main() {
|
||||
// Migration one-shot des anciens skills (SKILLS/<nom>/SKILL.md) vers la
|
||||
|
||||
+40
-5
@@ -51,15 +51,51 @@ func withDisplayName(content, name string) string {
|
||||
return line + "\n" + content
|
||||
}
|
||||
|
||||
// presetFingerprint hashes config content while IGNORING the device-level keys
|
||||
// (preservedKeys) that SwitchToPreset re-applies on top of a freshly copied
|
||||
// preset. Without this, those injected MEM_MODE/CRAWL4AI_URL lines make
|
||||
// config.env differ from every preset file, so no preset is ever detected as
|
||||
// active (regression introduced when preservedKeys were added). We strip those
|
||||
// assignment lines symmetrically from both sides before hashing so a plain
|
||||
// content match still identifies the active preset.
|
||||
func presetFingerprint(content []byte) string {
|
||||
var b strings.Builder
|
||||
for _, line := range strings.Split(string(content), "\n") {
|
||||
if isPreservedAssignment(line) {
|
||||
continue
|
||||
}
|
||||
b.WriteString(line)
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
h := sha1.Sum([]byte(b.String()))
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
|
||||
// isPreservedAssignment reports whether a line assigns one of the preservedKeys
|
||||
// (commented or not), e.g. `MEM_MODE=off` or `# CRAWL4AI_URL=...`.
|
||||
func isPreservedAssignment(line string) bool {
|
||||
t := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "#"))
|
||||
eq := strings.IndexByte(t, '=')
|
||||
if eq < 0 {
|
||||
return false
|
||||
}
|
||||
key := strings.TrimSpace(t[:eq])
|
||||
for _, k := range preservedKeys {
|
||||
if key == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListPresets returns all configs/*.env, marking the one whose contents match
|
||||
// the current config.env (by SHA-1), sorted by display name.
|
||||
// the current config.env (ignoring device-level preservedKeys), sorted by name.
|
||||
func ListPresets() ([]Preset, error) {
|
||||
dir := presetsDir()
|
||||
_ = os.MkdirAll(dir, 0o755)
|
||||
cur := ""
|
||||
if b, err := os.ReadFile(confPath()); err == nil {
|
||||
h := sha1.Sum(b)
|
||||
cur = hex.EncodeToString(h[:])
|
||||
cur = presetFingerprint(b)
|
||||
}
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
@@ -75,13 +111,12 @@ func ListPresets() ([]Preset, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
h := sha1.Sum(b)
|
||||
id := strings.TrimSuffix(e.Name(), ".env")
|
||||
out = append(out, Preset{
|
||||
ID: id,
|
||||
Name: presetDisplayName(string(b), id),
|
||||
Path: p,
|
||||
Active: hex.EncodeToString(h[:]) == cur,
|
||||
Active: presetFingerprint(b) == cur,
|
||||
})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+4
-4
@@ -21,9 +21,9 @@
|
||||
thème plus tard : 1) copier un bloc [data-theme="…"] ci-dessous en changeant
|
||||
les valeurs, 2) ajouter son {id,label} au registre THEMES (voir le JS). Le
|
||||
reste (sélecteur, persistance, application) est automatique. */
|
||||
:root{color-scheme:dark;--bg:#0d1117;--panel:#161b22;--border:#30363d;--text:#e6edf3;--dim:#7d8590;--accent:#58a6ff;--ok:#58a6ff;--warn:#d29922;--err:#f85149;--mag:#bc8cff;--code-bg:#0d1117;--danger-bg:#3d1f23;--warn-bg:#3d2f1f}
|
||||
[data-theme="dark"]{color-scheme:dark;--bg:#0d1117;--panel:#161b22;--border:#30363d;--text:#e6edf3;--dim:#7d8590;--accent:#58a6ff;--ok:#58a6ff;--warn:#d29922;--err:#f85149;--mag:#bc8cff;--code-bg:#0d1117;--danger-bg:#3d1f23;--warn-bg:#3d2f1f}
|
||||
[data-theme="light"]{color-scheme:light;--bg:#f0f1f4;--panel:#f8f9fb;--border:#d7dbe1;--text:#24292f;--dim:#636b74;--accent:#0969da;--ok:#0969da;--warn:#9a6700;--err:#cf222e;--mag:#8250df;--code-bg:#e6e9ee;--danger-bg:#fbe6e6;--warn-bg:#fbefda}
|
||||
:root{color-scheme:dark;--bg:#0d1117;--panel:#161b22;--border:#30363d;--text:#e6edf3;--dim:#7d8590;--accent:#58a6ff;--accent-bg:rgba(88,166,255,.12);--ok:#58a6ff;--warn:#d29922;--err:#f85149;--mag:#bc8cff;--code-bg:#0d1117;--danger-bg:#3d1f23;--warn-bg:#3d2f1f}
|
||||
[data-theme="dark"]{color-scheme:dark;--bg:#0d1117;--panel:#161b22;--border:#30363d;--text:#e6edf3;--dim:#7d8590;--accent:#58a6ff;--accent-bg:rgba(88,166,255,.12);--ok:#58a6ff;--warn:#d29922;--err:#f85149;--mag:#bc8cff;--code-bg:#0d1117;--danger-bg:#3d1f23;--warn-bg:#3d2f1f}
|
||||
[data-theme="light"]{color-scheme:light;--bg:#f0f1f4;--panel:#f8f9fb;--border:#d7dbe1;--text:#24292f;--dim:#636b74;--accent:#0969da;--accent-bg:rgba(9,105,218,.10);--ok:#0969da;--warn:#9a6700;--err:#cf222e;--mag:#8250df;--code-bg:#e6e9ee;--danger-bg:#fbe6e6;--warn-bg:#fbefda}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--bg);color:var(--text);height:100vh;height:100dvh;display:flex;font-size:14px}
|
||||
/* PWA plein écran (viewport-fit=cover) : on décale le contenu des bords pour ne
|
||||
@@ -134,7 +134,7 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
.row{display:flex;gap:4px;flex-wrap:wrap}
|
||||
.preset{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:6px 10px;border:1px solid var(--border);border-radius:6px;margin:4px 0;cursor:pointer;background:var(--panel)}
|
||||
.preset:hover{border-color:var(--accent)}
|
||||
.preset.active{border-color:var(--ok);color:var(--ok)}
|
||||
.preset.active{border-color:var(--accent);color:var(--accent);border-width:1.5px;padding:5.5px 9.5px}
|
||||
.preset>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.preset>button{flex-shrink:0}
|
||||
.preset-info{flex:1;min-width:0}
|
||||
|
||||
+3
-3
@@ -3,13 +3,13 @@
|
||||
"FileVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2,
|
||||
"Patch": 18,
|
||||
"Patch": 19,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2,
|
||||
"Patch": 18,
|
||||
"Patch": 19,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
@@ -25,7 +25,7 @@
|
||||
"LegalCopyright": "Copyright (c) 2026 Jean contributors. MIT License.",
|
||||
"OriginalFilename": "jean.exe",
|
||||
"ProductName": "Jean",
|
||||
"ProductVersion": "0.2.18",
|
||||
"ProductVersion": "0.2.19",
|
||||
"Comments": "https://github.com/nathaninline/jean — projet open source (MIT)"
|
||||
},
|
||||
"VarFileInfo": {
|
||||
|
||||
Reference in new issue
Block a user