Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4f56a9c
docs: YAML config editor spec + implementation plan (issue #95)
falbany Nov 3, 2026
f2bc348
feat(server): config validate endpoint for the YAML editor
falbany Nov 3, 2026
7fe10a5
feat(server): config content endpoint + session-trial detection
falbany Nov 3, 2026
e06c39b
fix(server): explicit session-trial flag so save never anchors to a s…
falbany Nov 3, 2026
ca9d3c6
feat(server): validated config save endpoint with force escape hatch
falbany Nov 3, 2026
b806dd5
feat(server): save-as endpoint with extension whitelist and overwrite…
falbany Nov 3, 2026
bcf6da0
fix(server): reject path separators in save-as filename
falbany Nov 3, 2026
a42632a
feat(web): API client for config editor endpoints
falbany Nov 3, 2026
8ae6367
feat(server): in-memory starter template endpoint for the editor
falbany Nov 3, 2026
cfa4f41
feat(web): config editor modal shell with CodeMirror and dirty guard
falbany Nov 3, 2026
4f2a92e
fix(web): normalize CRLF baseline so untouched files are not dirty
falbany Nov 3, 2026
4da31c2
feat(web): editor toolbar actions New/Test/Revert/Apply
falbany Nov 3, 2026
7e3e92e
fix(web): guard editor toolbar actions against stale and duplicate di…
falbany Nov 3, 2026
ceb1350
feat(web): save/save-as with compliance gate, force option and replac…
falbany Nov 3, 2026
b2bbb6a
fix(web): align save-as whitelist parity and scope replace confirmati…
falbany Nov 4, 2026
5c90904
feat(web): YAML config editor in the UI
falbany Nov 4, 2026
a6f9d51
docs: YAML config editor user + API documentation
falbany Nov 4, 2026
50ca16d
fix(web): Ctrl+, opens the config editor instead of OS-launching
falbany Nov 4, 2026
0ebddf6
fix: case-insensitive config filename matching, tests-tree sync, hide…
falbany Nov 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
- **Déploiement zero-touch** : détection du hostname/IP/chemin pour sélectionner automatiquement les configurations adaptées aux postes multi-stations.
- **Formatage visuel** : coloration des lignes par règles avec ajustement automatique du contraste.
- **Repli sans configuration** : des valeurs par défaut sensées permettent une utilisation immédiate sans fichier `.profiles`.
- **Éditeur YAML de configuration** : modifiez le fichier `.profiles` dans l'application (bouton **Config** de la barre d'état) — New/Test/Revert/Apply/Save/Save As, validation en mémoire, garde-fou avant les enregistrements risqués, et raccourci « Open in OS ».

## 🚀 Installation

Expand Down Expand Up @@ -190,7 +191,7 @@ Lance le serveur FastAPI local et ouvre le frontend React dans une fenêtre de b

- **Interface** : le chrome web est bilingue (anglais / français). Utilisez le sélecteur de langue dans la barre de statut pour changer à la volée, ou définissez `language: en` / `language: fr` dans la section `defaults`.
- **Recherche** : utilisez `+` pour l'inclusion, `-` pour l'exclusion, `OR` pour les alternatives et `"..."` pour les expressions exactes.
- **Raccourcis clavier** : `Ctrl+F` (champ filtre) · `Ctrl+D` (champ répertoire) · `Ctrl+E` (champ extension) · `F5` (rescan) · `Ctrl+R` (recharger la config + rescan) · `Ctrl+H` (répertoire home) · `Ctrl+Shift+L` (visionneuse de logs) · `Ctrl+,` (ouvrir la config) · `Ctrl+Shift+T` (cycler le thème) · `Ctrl+B` (mode scanner de code-barres) · `Ctrl+Shift+B` (scanner de code-barres caméra) · `Ctrl+T` (timer de rescan auto) · `?` (dialogue des raccourcis).
- **Raccourcis clavier** : `Ctrl+F` (champ filtre) · `Ctrl+D` (champ répertoire) · `Ctrl+E` (champ extension) · `F5` (rescan) · `Ctrl+R` (recharger la config + rescan) · `Ctrl+H` (répertoire home) · `Ctrl+Shift+L` (visionneuse de logs) · `Ctrl+,` (ouvrir l'éditeur de config) · `Ctrl+Shift+T` (cycler le thème) · `Ctrl+B` (mode scanner de code-barres) · `Ctrl+Shift+B` (scanner de code-barres caméra) · `Ctrl+T` (timer de rescan auto) · `?` (dialogue des raccourcis).
- **PWA** : le frontend embarque un precache de service worker, la page peut donc être installée dans le navigateur comme une application.

### Mode Navigateur / PWA (serveurs partagés)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
- **Zero-Touch Deployment**: Detects hostname/IP/path to auto-select matching configurations for multi-station setups.
- **Visual Formatting**: Rules-based row coloring with automatic contrast adjustment.
- **No-Config Fallback**: Sensible defaults allow immediate use without a `.profiles` file.
- **YAML Config Editor**: Edit the `.profiles` file in-app (status-bar **Config** button) — New/Test/Revert/Apply/Save/Save As, in-memory validation, a compliance gate before risky saves, and an "Open in OS" shortcut.

## 🚀 Installation

Expand Down Expand Up @@ -189,7 +190,7 @@ Launches the local FastAPI server and opens the React frontend in a native PyWeb

- **Interface**: The web chrome is bilingual (English / French). Use the language switcher in the status bar to change on the fly, or set `language: en` / `language: fr` in the `defaults` section.
- **Search**: Use `+` for inclusion, `-` for exclusion, `OR` for alternatives, and `"..."` for exact phrases.
- **Keyboard shortcuts**: `Ctrl+F` (focus filter) · `Ctrl+D` (focus directory) · `Ctrl+E` (focus extension) · `F5` (rescan) · `Ctrl+R` (reload config + rescan) · `Ctrl+H` (home directory) · `Ctrl+Shift+L` (log viewer) · `Ctrl+,` (open config) · `Ctrl+Shift+T` (cycle theme) · `Ctrl+B` (barcode scanner mode) · `Ctrl+Shift+B` (camera barcode scanner) · `Ctrl+T` (auto-rescan timer) · `?` (shortcuts dialog).
- **Keyboard shortcuts**: `Ctrl+F` (focus filter) · `Ctrl+D` (focus directory) · `Ctrl+E` (focus extension) · `F5` (rescan) · `Ctrl+R` (reload config + rescan) · `Ctrl+H` (home directory) · `Ctrl+Shift+L` (log viewer) · `Ctrl+,` (open config editor) · `Ctrl+Shift+T` (cycle theme) · `Ctrl+B` (barcode scanner mode) · `Ctrl+Shift+B` (camera barcode scanner) · `Ctrl+T` (auto-rescan timer) · `?` (shortcuts dialog).
- **PWA**: The frontend ships a service worker precache, so the page can be installed in the browser as an app.

### Browser / PWA Mode (shared servers)
Expand Down
12 changes: 12 additions & 0 deletions docs/advanced/advanced-guide.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ if __name__ == "__main__":
app.run(debug=True)
```

### Configuration Editor Endpoints

Alternative front-ends can reuse the `/api/config` endpoints that power the built-in YAML editor (one line each; see `src/profiles/server/routes/config.py` for the full contracts):

- `GET /api/config/content` — the active file's text as `{filename, content, config_path, session_trial}`; a session trial answers an empty body with `session_trial=True`, a missing file answers 404.
- `POST /api/config/validate` — validate content in memory, never applied to the session; always answers 200 with `{valid, issues[]}` (invalid drafts come back as data, not as an HTTP error).
- `POST /api/config/template` — render the starter template in memory (nothing written, no state change); the same content `--init` puts on disk.
- `POST /api/config/save` — write the content to the active config file and re-apply it; refuses a session trial (400) since there is no file to overwrite.
- `POST /api/config/save-as` — write to a new file (extension whitelist `.profiles` / `.profiles.yaml`, overwrite guard answering 409 without `overwrite=true`) and anchor the session onto it.

Note the error duality on `save`/`save-as`: validation failures answer **400 with a JSON issues list** (`[{severity, message}, ...]`), while every other refusal (session-trial save, bad filename, missing directory, 409 existing target) answers with a **plain string** `detail` — clients must handle both shapes. The save endpoints also apply the freshly written file to the session, so a response's `config` object is the new live configuration.

---

## Programmatic API Usage
Expand Down
12 changes: 12 additions & 0 deletions docs/advanced/guide-avance.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ if __name__ == "__main__":
app.run(debug=True)
```

### Endpoints de l'Éditeur de Configuration

Les interfaces alternatives peuvent réutiliser les endpoints `/api/config` qui alimentent l'éditeur YAML intégré (une ligne chacun ; voir `src/profiles/server/routes/config.py` pour les contrats complets) :

- `GET /api/config/content` — le texte du fichier actif sous `{filename, content, config_path, session_trial}` ; une config d'essai répond un corps vide avec `session_trial=True`, un fichier manquant répond 404.
- `POST /api/config/validate` — valide le contenu en mémoire, jamais appliqué à la session ; répond toujours 200 avec `{valid, issues[]}` (les brouillons invalides reviennent sous forme de données, pas d'erreur HTTP).
- `POST /api/config/template` — rend le modèle starter en mémoire (rien n'est écrit, aucun changement d'état) ; le même contenu que `--init` pose sur disque.
- `POST /api/config/save` — écrit le contenu dans le fichier de config actif puis le ré-applique ; refuse une config d'essai (400) puisqu'il n'y a aucun fichier à écraser.
- `POST /api/config/save-as` — écrit dans un nouveau fichier (liste blanche d'extensions `.profiles` / `.profiles.yaml`, garde d'existence répondant 409 sans `overwrite=true`) et ancre la session dessus.

Notez la dualité des erreurs sur `save`/`save-as` : un échec de validation répond **400 avec une liste JSON de problèmes** (`[{severity, message}, ...]`), tandis que tout autre refus (save d'une config d'essai, nom de fichier invalide, répertoire inexistant, cible existante en 409) répond avec un `detail` **chaîne simple** — les clients doivent gérer les deux formes. Les endpoints de sauvegarde appliquent aussi le fichier fraîchement écrit à la session : le `config` de la réponse est donc la nouvelle configuration active.

---

## Utilisation de l'API Programmatique
Expand Down
49 changes: 46 additions & 3 deletions docs/configuration-profile.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ On first launch in interactive mode (web UI in the PyWebView shell or browser),
python -m profiles --init
```

This command creates a `.profiles` file with default values in the current working directory.
This command creates a `.profiles` file with default values in the current working directory. The same starter content is also available in the web UI via the configuration editor's **New** button (see [Configuration Editor](#configuration-editor-web-ui)).

### Method 3: Manual Creation

Expand Down Expand Up @@ -296,6 +296,50 @@ python -m profiles --headless
python -m profiles --init
```

The starter content written by `--init` is also available from the web UI: open the configuration editor (**Config** button in the status bar) and click **New** — see [Configuration Editor](#configuration-editor-web-ui).

---

## Configuration Editor (Web UI)

The web UI includes a built-in **YAML configuration editor** — no external editor required.

### Opening the Editor

Click the **Config** button in the status bar (bottom of the window). The editor opens fullscreen with the active configuration file's content.

> The editor header also carries an **Open in OS** button that hands the file to your system's default editor, if you prefer your own tooling.

### Toolbar

| Button | Action |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **New** | Load the starter configuration template (the same content `--init` writes to disk) into the editor. Unsaved edits are confirmed first. |
| **Test** | Validate the editor content in memory; any issues are listed without touching the session or the disk. |
| **Revert** | Discard edits and reload the file from disk (confirmed first when the editor is dirty). |
| **Save** | Write the editor content to the active configuration file on disk. Hidden while the session runs a trial config with no file behind it. |
| **Save As** | Write the content to a **new** file (see below) — this also anchors the session to that file. |
| **Apply** | Trial the editor content as the **session configuration** — in-memory only, nothing is written to disk. |

Keyboard: `Ctrl/Cmd+S` saves when the file is anchored to disk; otherwise it falls back to **Apply**.

### The Two-Step Model: Apply vs Save

- **Apply** is a *session trial*: the text is parsed and becomes the live configuration immediately (directory, extensions, filters and theme are re-derived), but nothing is written to disk. The trial disappears on restart, and a **Reload** returns to the file on disk.
- **Save** (and **Save As**) *anchor* the text to a file: the file is written and the session switches to it. What you see in the editor is what the next launch will read.

### Compliance Gate (Save & Save As)

Saving runs a validation first. If issues are found, a dialog lists them and blocks the write until you choose:

- **Go back and fix** — returns to the editor with the issues listed above it;
- **Force save anyway** — writes the file despite the issues (the file will load with default values until they are fixed).

### Save As

- Allowed extensions: **`.profiles`** and **`.profiles.yaml`** (case-insensitive).
- If a file with the same name already exists in the chosen directory, a **Replace** confirmation is required before overwriting it.

---

## Advanced Configuration Examples
Expand Down Expand Up @@ -637,7 +681,7 @@ A: Not currently supported. Use absolute paths or configure per-machine sections
A: The first matching section (by order in the `configs` dictionary) is used. The catch-all (`match.hostname: ["*"]`) should be last.

**Q: How do I reset to defaults?**
A: Delete `.profiles` or run `python -m profiles --init` to regenerate.
A: Delete `.profiles` or run `python -m profiles --init` to regenerate. The same starter template can also be loaded into the web UI editor with **New**.

---

Expand All @@ -655,5 +699,4 @@ A: Delete `.profiles` or run `python -m profiles --init` to regenerate.

- Environment variable substitution
- Configuration validation tool
- GUI configuration editor
- Configuration import/export
49 changes: 46 additions & 3 deletions docs/configuration-profile.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Au premier lancement en mode interactif (interface web dans la fenêtre PyWebVie
python -m profiles --init
```

Cette commande crée un fichier `.profiles` avec des valeurs par défaut dans le répertoire courant.
Cette commande crée un fichier `.profiles` avec des valeurs par défaut dans le répertoire courant. Le même contenu starter est également disponible dans l'interface web via le bouton **New** de l'éditeur de configuration (voir [Éditeur de Configuration](#éditeur-de-configuration-interface-web)).

### Méthode 3 : Création manuelle

Expand Down Expand Up @@ -294,6 +294,50 @@ python -m profiles --headless
python -m profiles --init
```

Le contenu starter écrit par `--init` est également disponible depuis l'interface web : ouvrez l'éditeur de configuration (bouton **Config** de la barre d'état) et cliquez sur **New** — voir [Éditeur de Configuration](#éditeur-de-configuration-interface-web).

---

## Éditeur de Configuration (Interface Web)

L'interface web intègre un **éditeur YAML de configuration** — plus besoin d'éditeur externe.

### Ouvrir l'éditeur

Cliquez sur le bouton **Config** de la barre d'état (en bas de la fenêtre). L'éditeur s'ouvre en plein écran avec le contenu du fichier de configuration actif.

> L'en-tête de l'éditeur comporte également un bouton **Open in OS** qui ouvre le fichier avec l'éditeur par défaut du système, si vous préférez votre propre outil.

### Barre d'outils

| Bouton | Action |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **New** | Charge le modèle de configuration starter (le même contenu que `--init` écrit sur disque) dans l'éditeur. Les modifications non enregistrées sont confirmées au préalable. |
| **Test** | Valide le contenu de l'éditeur en mémoire ; les problèmes éventuels sont listés sans toucher à la session ni au disque. |
| **Revert** | Abandonne les modifications et recharge le fichier depuis le disque (confirmation préalable si l'éditeur contient des modifications). |
| **Save** | Écrit le contenu de l'éditeur dans le fichier de configuration actif sur disque. Masqué tant que la session utilise une config d'essai sans fichier associé. |
| **Save As** | Écrit le contenu dans un **nouveau** fichier (voir ci-dessous) — cela ancre également la session sur ce fichier. |
| **Apply** | Teste le contenu de l'éditeur comme **configuration de session** — en mémoire uniquement, rien n'est écrit sur disque. |

Raccourci clavier : `Ctrl/Cmd+S` enregistre quand le fichier est ancré sur disque ; sinon il revient à **Apply**.

### Le modèle en deux étapes : Apply vs Save

- **Apply** est un *essai de session* : le texte est analysé et devient immédiatement la configuration active (répertoire, extensions, filtres et thème recalculés), mais rien n'est écrit sur disque. L'essai disparaît au redémarrage, et un **Reload** ramène au fichier présent sur disque.
- **Save** (et **Save As**) *ancre* le texte à un fichier : le fichier est écrit et la session bascule dessus. Ce que vous voyez dans l'éditeur est ce que le prochain lancement lira.

### Garde-fou de conformité (Save & Save As)

L'enregistrement déclenche d'abord une validation. Si des problèmes sont détectés, un dialogue les liste et bloque l'écriture jusqu'à ce que vous choisissiez :

- **Go back and fix** — retour à l'éditeur avec la liste des problèmes affichée au-dessus ;
- **Force save anyway** — écrit le fichier malgré les problèmes (il se chargera avec les valeurs par défaut jusqu'à leur correction).

### Save As

- Extensions autorisées : **`.profiles`** et **`.profiles.yaml`** (insensibles à la casse).
- Si un fichier du même nom existe déjà dans le répertoire choisi, une confirmation **Replace** est exigée avant de l'écraser.

---

## Exemples de Configuration Avancée
Expand Down Expand Up @@ -624,7 +668,7 @@ R : Pas actuellement pris en charge. Utilisez des chemins absolus ou configurez
R : La première section correspondante (par ordre dans le dictionnaire `configs`) est utilisée. Le piège universel (`match.hostname: ["*"]`) doit être en dernier.

**Q : Comment réinitialiser aux valeurs par défaut ?**
R : Supprimez `.profiles` ou exécutez `python -m profiles --init` pour régénérer.
R : Supprimez `.profiles` ou exécutez `python -m profiles --init` pour régénérer. Le même modèle starter peut aussi être chargé dans l'éditeur de l'interface web avec **New**.

---

Expand All @@ -640,5 +684,4 @@ R : Supprimez `.profiles` ou exécutez `python -m profiles --init` pour régén
### Fonctionnalités Planifiées
- Substitution de variables d'environnement
- Outil de validation de configuration
- Éditeur de configuration GUI
- Import/export de configuration
Loading
Loading