From b0c7cb653f61bb5ec8476a4be914601122836d41 Mon Sep 17 00:00:00 2001
From: Mamat79
Date: Wed, 12 Aug 2026 17:00:40 +0200
Subject: [PATCH] Release Controller Studio V.2026.5 redesign
---
CHANGELOG.md | 7 +
README.md | 36 +-
README_EN.md | 18 +-
docs/CONTROLLER_PROFILE_SOURCES.md | 2 +-
.../windows/SiLeMIO-Controller-Studio.iss | 4 +-
packaging/windows/build_setup.ps1 | 2 +-
packaging/windows/install.ps1 | 2 +-
pyproject.toml | 2 +-
scripts/generate_manuals.py | 22 +-
src/silemio_control_hub/__init__.py | 2 +-
src/silemio_control_hub/desktop.py | 566 ++++++++++++------
src/silemio_control_hub/library_remote.py | 2 +-
...ler-Studio-for-LiveProfessor-Manual-EN.pdf | Bin 1067660 -> 1067964 bytes
...ler-Studio-for-LiveProfessor-Notice-FR.pdf | Bin 1068554 -> 1068841 bytes
tests/test_product_identity.py | 2 +-
15 files changed, 450 insertions(+), 217 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ca716c..2bfa5d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Historique des versions
+## V.2026 (2026.5) — 2026-08-12
+
+- Réorganise l'interface autour d'une navigation latérale stable : Contrôle en direct, Contrôleurs, Plug-ins, Bibliothèque et accès AutoMap dédié.
+- Simplifie la page Contrôle en direct autour de l'état du moteur, de l'action principale, des banques et des accès rapides, tout en gardant visibles le démarrage Windows et la connexion automatique.
+- Regroupe les outils EC4, l'apprentissage MIDI et les réglages techniques dans une section avancée repliable sans supprimer aucune fonction.
+- Protège les libellés français et anglais contre la troncature avec une fenêtre minimale plus large, des zones redimensionnables et des actions réorganisées.
+
## V.2026 (2026.4) — 2026-08-10
- Simplifie l'écran d'attente EC4 en retirant la signature « By Mamat » et efface cet écran dès l'arrêt du moteur ou la fermeture de Controller Studio.
diff --git a/README.md b/README.md
index 7ce269f..2566f6d 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -23,16 +23,16 @@
## Dernière version (accès rapide)
-**Version stable : [Controller Studio V.2026.4](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.4)**
+**Version stable : [Controller Studio V.2026.5](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.5)**
Téléchargement direct :
-- [Installer Controller Studio pour Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Setup-v2026.4.exe)
-- [Version portable Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor.exe)
-- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
-- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
-- [Sommes de contrôle SHA-256](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/SHA256SUMS.txt)
+- [Installer Controller Studio pour Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Setup-v2026.5.exe)
+- [Version portable Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor.exe)
+- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
+- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
+- [Sommes de contrôle SHA-256](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/SHA256SUMS.txt)
-> **Version publique V.2026.4 pour Windows.** Controller Studio travaille sur une copie AutoMap et conserve le projet `.rack2` source intact.
+> **Version publique V.2026.5 pour Windows.** Controller Studio adopte une navigation latérale plus claire, travaille sur une copie AutoMap et conserve le projet `.rack2` source intact.
## À quoi sert Controller Studio ?
@@ -95,7 +95,7 @@ VST est une marque déposée de Steinberg Media Technologies GmbH. Les mentions
## Banque de contrôleurs intégrée
-V.2026.4 fournit 33 profils déclaratifs prêts à exporter et permet d’en fabriquer d’autres directement dans l’application :
+V.2026.5 fournit 33 profils déclaratifs prêts à exporter et permet d’en fabriquer d’autres directement dans l’application :
- Akai Professional LPD8 MK2, MIDImix, APC Mini MK2, MPK Mini MK3, MPK Mini IV et MPK Mini Plus ;
- Arturia MiniLab 3, KeyLab Essential MK3 et BeatStep ;
@@ -210,25 +210,25 @@ LiveProfessor, Faderfox, Behringer, Novation, DJ TechTools et les autres noms ci
-
+
## Latest version (quick access)
-**Stable release: [Controller Studio V.2026.4](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.4)**
+**Stable release: [Controller Studio V.2026.5](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.5)**
Direct downloads:
-- [Controller Studio installer for Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Setup-v2026.4.exe)
-- [Portable Windows x64 version (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor.exe)
-- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
-- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
-- [SHA-256 checksums](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/SHA256SUMS.txt)
+- [Controller Studio installer for Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Setup-v2026.5.exe)
+- [Portable Windows x64 version (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor.exe)
+- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
+- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
+- [SHA-256 checksums](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/SHA256SUMS.txt)
[Back to the French version ↑](#version-francaise)
-> **Public V.2026.4 Windows release.** Controller Studio always works on a new AutoMap copy and keeps the source `.rack2` project untouched.
+> **Public V.2026.5 Windows release.** Controller Studio introduces clearer sidebar navigation, always works on a new AutoMap copy, and keeps the source `.rack2` project untouched.
## What does Controller Studio do?
@@ -291,7 +291,7 @@ VST is a registered trademark of Steinberg Media Technologies GmbH. Third-party
## Built-in controller bank
-V.2026.4 includes 33 declarative profiles ready to export and can build more directly in the application:
+V.2026.5 includes 33 declarative profiles ready to export and can build more directly in the application:
- Akai Professional LPD8 MK2, MIDImix, APC Mini MK2, MPK Mini MK3, MPK Mini IV, and MPK Mini Plus;
- Arturia MiniLab 3, KeyLab Essential MK3, and BeatStep;
diff --git a/README_EN.md b/README_EN.md
index 41fd032..5664e96 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -10,25 +10,25 @@
-
+
## Latest version (quick access)
-**Stable release: [Controller Studio V.2026.4](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.4)**
+**Stable release: [Controller Studio V.2026.5](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/tag/v2026.5)**
Direct downloads:
-- [Controller Studio installer for Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Setup-v2026.4.exe)
-- [Portable Windows x64 version (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor.exe)
-- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
-- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
-- [SHA-256 checksums](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.4/SHA256SUMS.txt)
+- [Controller Studio installer for Windows x64 (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Setup-v2026.5.exe)
+- [Portable Windows x64 version (.exe)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor.exe)
+- [Full English manual (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Manual-EN.pdf)
+- [Notice complète en français (.pdf)](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/Controller-Studio-for-LiveProfessor-Notice-FR.pdf)
+- [SHA-256 checksums](https://github.com/Mamat79/Controller-Studio-for-LiveProfessor/releases/download/v2026.5/SHA256SUMS.txt)
[Lire cette présentation en français](README.md)
-> **Public V.2026.4 Windows release.** Controller Studio always works on a new AutoMap copy and keeps the source `.rack2` project untouched.
+> **Public V.2026.5 Windows release.** Controller Studio introduces clearer sidebar navigation, always works on a new AutoMap copy, and keeps the source `.rack2` project untouched.
## What does Controller Studio do?
@@ -91,7 +91,7 @@ VST is a registered trademark of Steinberg Media Technologies GmbH. Third-party
## Built-in controller bank
-V.2026.4 includes 33 declarative profiles ready to export and can build more directly in the application:
+V.2026.5 includes 33 declarative profiles ready to export and can build more directly in the application:
- Akai Professional LPD8 MK2, MIDImix, APC Mini MK2, MPK Mini MK3, MPK Mini IV, and MPK Mini Plus;
- Arturia MiniLab 3, KeyLab Essential MK3, and BeatStep;
diff --git a/docs/CONTROLLER_PROFILE_SOURCES.md b/docs/CONTROLLER_PROFILE_SOURCES.md
index 766ab83..8feab30 100644
--- a/docs/CONTROLLER_PROFILE_SOURCES.md
+++ b/docs/CONTROLLER_PROFILE_SOURCES.md
@@ -1,6 +1,6 @@
# Sources et modes des profils de contrôleurs
-Controller Studio V.2026.4 fournit 33 profils déclaratifs. Un profil `community`
+Controller Studio V.2026.5 fournit 33 profils déclaratifs. Un profil `community`
est utilisable pour l'export `.ctrl2`, AutoMap et l'apprentissage MIDI, mais son
plan complet n'a pas encore été validé sur un exemplaire physique par le projet.
Les profils programmables utilisent un plan SiLeMIO simple et reproductible à
diff --git a/packaging/windows/SiLeMIO-Controller-Studio.iss b/packaging/windows/SiLeMIO-Controller-Studio.iss
index 0275eaa..71155b9 100644
--- a/packaging/windows/SiLeMIO-Controller-Studio.iss
+++ b/packaging/windows/SiLeMIO-Controller-Studio.iss
@@ -1,5 +1,5 @@
#define MyAppName "Controller Studio for LiveProfessor"
-#define MyAppVersion "2026.4"
+#define MyAppVersion "2026.5"
#define MyAppPublisher "Mamat"
#define MyAppExeName "Controller-Studio-for-LiveProfessor.exe"
#define MyShortcutName "Controller Studio for LiveProfessor"
@@ -23,7 +23,7 @@ ArchitecturesAllowed=x64compatible
PrivilegesRequired=lowest
UsedUserAreasWarning=no
UninstallDisplayIcon={app}\{#MyAppExeName}
-VersionInfoVersion=2026.4.0.0
+VersionInfoVersion=2026.5.0.0
VersionInfoCompany={#MyAppPublisher}
VersionInfoDescription={#MyAppName} installer
VersionInfoProductName={#MyAppName}
diff --git a/packaging/windows/build_setup.ps1 b/packaging/windows/build_setup.ps1
index 3b6bc38..e1dcbcd 100644
--- a/packaging/windows/build_setup.ps1
+++ b/packaging/windows/build_setup.ps1
@@ -7,7 +7,7 @@ $ErrorActionPreference = 'Stop'
$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$application = Join-Path $projectRoot 'dist\Controller-Studio-for-LiveProfessor.exe'
$setupScript = Join-Path $PSScriptRoot 'SiLeMIO-Controller-Studio.iss'
-$setup = Join-Path $projectRoot 'dist\Controller-Studio-for-LiveProfessor-Setup-v2026.4.exe'
+$setup = Join-Path $projectRoot 'dist\Controller-Studio-for-LiveProfessor-Setup-v2026.5.exe'
$checksum = "$setup.sha256"
if ($RebuildApplication -or -not (Test-Path -LiteralPath $application -PathType Leaf)) {
diff --git a/packaging/windows/install.ps1 b/packaging/windows/install.ps1
index 6e195a6..64a9d72 100644
--- a/packaging/windows/install.ps1
+++ b/packaging/windows/install.ps1
@@ -44,7 +44,7 @@ $uninstallKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\SiLeM
$hostExe = (Get-Process -Id $PID).Path
$null = New-Item -Path $uninstallKey -Force
Set-ItemProperty -Path $uninstallKey -Name DisplayName -Value 'Controller Studio for LiveProfessor'
-Set-ItemProperty -Path $uninstallKey -Name DisplayVersion -Value '2026.4'
+Set-ItemProperty -Path $uninstallKey -Name DisplayVersion -Value '2026.5'
Set-ItemProperty -Path $uninstallKey -Name Publisher -Value 'Mamat'
Set-ItemProperty -Path $uninstallKey -Name InstallLocation -Value $InstallRoot
Set-ItemProperty -Path $uninstallKey -Name DisplayIcon -Value $targetExe
diff --git a/pyproject.toml b/pyproject.toml
index c9115e7..3d4351b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "silemio-control-hub"
-version = "2026.4"
+version = "2026.5"
description = "Controller Studio for LiveProfessor by SiLeMI/O"
requires-python = ">=3.11"
dependencies = [
diff --git a/scripts/generate_manuals.py b/scripts/generate_manuals.py
index c6635ef..a463422 100644
--- a/scripts/generate_manuals.py
+++ b/scripts/generate_manuals.py
@@ -156,7 +156,7 @@
(
"steps",
[
- ("1", "Ouvrez l'onglet Plug-ins puis choisissez un projet .rack2."),
+ ("1", "Ouvrez Plug-ins dans la navigation latérale puis choisissez un projet .rack2."),
("2", "Cliquez sur Analyser les plug-ins. Les instances identiques sont regroupées par type."),
("3", "Sélectionnez un type puis ouvrez Créer / modifier le profil."),
("4", "Renseignez le nom, le libellé court, le type, le rôle, l'unité et la priorité des paramètres utiles."),
@@ -617,7 +617,7 @@ def english_content() -> dict:
(
"steps",
[
- ("1", "Ouvrez l'onglet Plug-ins et choisissez un projet .rack2."),
+ ("1", "Ouvrez Plug-ins dans la navigation latérale puis choisissez un projet .rack2."),
("2", "Analysez le projet : les instances identiques sont regroupées automatiquement."),
("3", "Cliquez sur Récupérer tous les vrais noms pour traiter tous les types installés dans des processus isolés."),
("4", "Seuls les inventaires dont le nombre correspond exactement au projet sont enregistrés."),
@@ -663,7 +663,7 @@ def english_content() -> dict:
("h2", "Une première page simple"),
(
"p",
- "La page Live garde uniquement le contrôleur actif, les commandes essentielles, l'état et le dernier événement. Les connexions MIDI/OSC sont dans Réglages et le journal complet s'ouvre dans une fenêtre séparée.",
+ "La navigation latérale donne un accès direct à Live, Banque de contrôleurs, Plug-ins, Bibliothèque et AutoMap. La page Live garde uniquement le contrôleur actif, les deux options de démarrage automatique, les commandes essentielles, l'état et le dernier événement. Les connexions MIDI/OSC sont regroupées dans Réglages avancés et le journal complet s'ouvre dans une fenêtre séparée.",
),
("h2", "Réactivité, feedback et Overlay"),
(
@@ -774,7 +774,7 @@ def english_content() -> dict:
"4. Organize parameters with Plugin Studio",
[
("h2", "Recognize a plug-in"),
- ("steps", [("1", "Open the Plug-ins tab and choose a .rack2 project."), ("2", "Analyze the project; matching instances are grouped automatically."), ("3", "Click Retrieve all real names to process every installed type in isolated workers."), ("4", "Only inventories whose count exactly matches the project are saved."), ("5", "Open a profile to use Select all, Select none, or individual checkboxes."), ("6", "Adjust names and priorities if needed, save locally, then use the profile in AutoMap.")]),
+ ("steps", [("1", "Open Plug-ins from the sidebar and choose a .rack2 project."), ("2", "Analyze the project; matching instances are grouped automatically."), ("3", "Click Retrieve all real names to process every installed type in isolated workers."), ("4", "Only inventories whose count exactly matches the project are saved."), ("5", "Open a profile to use Select all, Select none, or individual checkboxes."), ("6", "Adjust names and priorities if needed, save locally, then use the profile in AutoMap.")]),
("callout", "For an older format or an unusual plug-in, the individual button automatically offers Companion/OSC interception as a fallback."),
("h2", "Safe local profiles"),
("p", "Profiles are declarative JSON files. Replaced versions can be backed up, downloaded code is never executed, and the analyzed project remains unchanged."),
@@ -794,7 +794,7 @@ def english_content() -> dict:
"6. Interface, log, and updates",
[
("h2", "A simple first page"),
- ("p", "The Live page keeps only the active controller, essential actions, status, and last event. MIDI/OSC connections are under Settings, and the complete log opens in a separate window."),
+ ("p", "The sidebar provides direct access to Live, Controller bank, Plug-ins, Library, and AutoMap. The Live page keeps the active controller, both automatic-start options, essential actions, status, and last event. MIDI/OSC connections are grouped under Advanced settings, and the complete log opens in a separate window."),
("h2", "Responsiveness, feedback, and Overlay"),
("p", "Settings restores EC4 Bridge's compact advanced window: Overlay update rate and duration, Companion and label refresh delays, LiveProfessor feedback timeout, and persistent display. These timings are shared by compatible controllers; setup/group and SysEx commands remain EC4-only."),
("h2", "Automatic server startup"),
@@ -828,12 +828,12 @@ def styles():
"subtitle": ParagraphStyle("Subtitle", parent=base["Normal"], fontName="Helvetica", fontSize=12, leading=17, textColor=MID, alignment=TA_CENTER),
"h1": ParagraphStyle("H1", parent=base["Heading1"], fontName="Helvetica-Bold", fontSize=18, leading=22, textColor=NAVY, spaceAfter=5 * mm),
"h2": ParagraphStyle("H2", parent=base["Heading2"], fontName="Helvetica-Bold", fontSize=12.2, leading=16, textColor=BLUE, spaceBefore=3 * mm, spaceAfter=2 * mm),
- "body": ParagraphStyle("Body", parent=base["BodyText"], fontName="Helvetica", fontSize=10.2, leading=14.5, textColor=NAVY, alignment=TA_LEFT, spaceAfter=2.7 * mm),
- "small": ParagraphStyle("Small", parent=base["BodyText"], fontName="Helvetica", fontSize=8.8, leading=12, textColor=MID, alignment=TA_CENTER),
- "toc": ParagraphStyle("Toc", parent=base["BodyText"], fontName="Helvetica", fontSize=10.7, leading=16, leftIndent=5 * mm, textColor=NAVY),
- "bullet": ParagraphStyle("Bullet", parent=base["BodyText"], fontName="Helvetica", fontSize=10.1, leading=14.3, leftIndent=6 * mm, firstLineIndent=-3 * mm, textColor=NAVY, spaceAfter=1.5 * mm),
- "callout": ParagraphStyle("Callout", parent=base["BodyText"], fontName="Helvetica-Bold", fontSize=10.1, leading=14.3, textColor=NAVY, leftIndent=3 * mm, rightIndent=3 * mm),
- "table_header": ParagraphStyle("TableHeader", parent=base["BodyText"], fontName="Helvetica-Bold", fontSize=10.1, leading=14.3, textColor=colors.white, alignment=TA_LEFT),
+ "body": ParagraphStyle("Body", parent=base["BodyText"], fontName="Helvetica", fontSize=10.8, leading=15.3, textColor=NAVY, alignment=TA_LEFT, spaceAfter=2.7 * mm),
+ "small": ParagraphStyle("Small", parent=base["BodyText"], fontName="Helvetica", fontSize=9.2, leading=12.6, textColor=MID, alignment=TA_CENTER),
+ "toc": ParagraphStyle("Toc", parent=base["BodyText"], fontName="Helvetica", fontSize=11, leading=16.5, leftIndent=5 * mm, textColor=NAVY),
+ "bullet": ParagraphStyle("Bullet", parent=base["BodyText"], fontName="Helvetica", fontSize=10.7, leading=15.1, leftIndent=6 * mm, firstLineIndent=-3 * mm, textColor=NAVY, spaceAfter=1.5 * mm),
+ "callout": ParagraphStyle("Callout", parent=base["BodyText"], fontName="Helvetica-Bold", fontSize=10.7, leading=15.1, textColor=NAVY, leftIndent=3 * mm, rightIndent=3 * mm),
+ "table_header": ParagraphStyle("TableHeader", parent=base["BodyText"], fontName="Helvetica-Bold", fontSize=10.7, leading=15.1, textColor=colors.white, alignment=TA_LEFT),
}
diff --git a/src/silemio_control_hub/__init__.py b/src/silemio_control_hub/__init__.py
index 178fe34..5984aad 100644
--- a/src/silemio_control_hub/__init__.py
+++ b/src/silemio_control_hub/__init__.py
@@ -48,4 +48,4 @@
"prepare_liveprofessor_project",
]
-__version__ = "2026.4"
+__version__ = "2026.5"
diff --git a/src/silemio_control_hub/desktop.py b/src/silemio_control_hub/desktop.py
index 6ac444d..9ed33ae 100644
--- a/src/silemio_control_hub/desktop.py
+++ b/src/silemio_control_hub/desktop.py
@@ -104,6 +104,7 @@
PRODUCT_ICON_PATH = Path(__file__).resolve().parent / "assets" / "controller-studio.ico"
+PRODUCT_LOGO_PATH = Path(__file__).resolve().parent / "assets" / "controller-studio.png"
DISPLAY_VERSION = f"V.{__version__}"
AUTO_START_RETRY_DELAYS_MS = (1200, 3000, 6000, 12000, 20000)
@@ -121,6 +122,8 @@
"tab_controllers": "Banque de contrôleurs",
"tab_plugins": "Plug-ins",
"tab_library": "Bibliothèque",
+ "nav_automap": "⚡ AutoMap",
+ "sidebar_footer": "Journal · Aide · Réglages",
"menu_file": "Fichier",
"menu_options": "Options",
"menu_tools": "Outils",
@@ -132,6 +135,16 @@
"utiles à son pilote temps réel."
),
"active_controller": "Contrôleur actif",
+ "automation_options": "Automatisation",
+ "quick_access": "Accès rapides",
+ "advanced_controller": "Réglages avancés du contrôleur",
+ "advanced_controller_open": "▼ Réglages avancés du contrôleur",
+ "advanced_controller_closed": "▶ Réglages avancés du contrôleur",
+ "controllers_title": "Banque de contrôleurs",
+ "controllers_description": (
+ "Choisissez un profil existant, fabriquez votre contrôleur ou exportez "
+ "son fichier LiveProfessor."
+ ),
"driver_ready": "PILOTE TEMPS RÉEL PRÊT",
"driver_profile_only": "PROFIL AUTOMAP / EXPORT",
"driver_profile_only_body": (
@@ -662,6 +675,8 @@
"tab_controllers": "Controller bank",
"tab_plugins": "Plug-ins",
"tab_library": "Library",
+ "nav_automap": "⚡ AutoMap",
+ "sidebar_footer": "Log · Help · Settings",
"menu_file": "File",
"menu_options": "Options",
"menu_tools": "Tools",
@@ -673,6 +688,16 @@
"to its real-time driver."
),
"active_controller": "Active controller",
+ "automation_options": "Automation",
+ "quick_access": "Quick access",
+ "advanced_controller": "Advanced controller settings",
+ "advanced_controller_open": "▼ Advanced controller settings",
+ "advanced_controller_closed": "▶ Advanced controller settings",
+ "controllers_title": "Controller bank",
+ "controllers_description": (
+ "Choose an existing profile, build your controller, or export its "
+ "LiveProfessor file."
+ ),
"driver_ready": "REAL-TIME DRIVER READY",
"driver_profile_only": "AUTOMAP / EXPORT PROFILE",
"driver_profile_only_body": (
@@ -1332,14 +1357,16 @@ def __init__(self, root: tk.Tk, *, start_minimized: bool = False) -> None:
self._library_buttons: list[ttk.Button] = []
self._closing = False
self._suppress_unmap = False
+ self.current_page = "live"
+ self._live_advanced_open = False
try:
self.log_path = configure_runtime_logging(self.runtime_config.log_level)
except OSError as exc:
self.log_path = default_log_path()
self._configuration_load_error = self._configuration_load_error or str(exc)
- root.geometry("1040x680")
- root.minsize(840, 540)
+ root.geometry("1180x760")
+ root.minsize(1080, 680)
if PRODUCT_ICON_PATH.is_file():
try:
root.iconbitmap(default=str(PRODUCT_ICON_PATH))
@@ -1384,73 +1411,154 @@ def _configure_style(self) -> None:
style.configure("Treeview", rowheight=26, font=("Segoe UI", 9))
style.configure("Treeview.Heading", font=("Segoe UI Semibold", 9))
style.configure("Accent.TButton", font=("Segoe UI Semibold", 9))
+ style.configure("Section.TLabelframe", padding=(12, 10))
+ style.configure("Section.TLabelframe.Label", font=("Segoe UI Semibold", 10))
def _build_ui(self) -> None:
self.root.title(self._t("window_title", version=DISPLAY_VERSION))
self._build_menu()
- brand = tk.Frame(
- self.root,
- bg="#111820",
- height=72,
- highlightthickness=1,
- highlightbackground="#2a4050",
- )
- brand.pack(fill="x", padx=18, pady=(14, 8))
- brand.pack_propagate(False)
+ shell = ttk.Frame(self.root)
+ shell.pack(fill="both", expand=True)
+ shell.columnconfigure(1, weight=1)
+ shell.rowconfigure(0, weight=1)
+
+ sidebar = tk.Frame(shell, bg="#101c26", width=210)
+ sidebar.grid(row=0, column=0, sticky="nsew")
+ sidebar.grid_propagate(False)
+ self.sidebar_logo_image: tk.PhotoImage | None = None
+ if PRODUCT_LOGO_PATH.is_file():
+ try:
+ self.sidebar_logo_image = tk.PhotoImage(file=str(PRODUCT_LOGO_PATH)).subsample(
+ 12, 12
+ )
+ except tk.TclError:
+ self.sidebar_logo_image = None
+ if self.sidebar_logo_image is not None:
+ tk.Label(
+ sidebar,
+ image=self.sidebar_logo_image,
+ bg="#101c26",
+ borderwidth=0,
+ ).pack(pady=(16, 4))
tk.Label(
- brand,
+ sidebar,
text=PRODUCT_NAME,
- bg="#111820",
- fg="#e8f4f8",
- font=("Segoe UI Semibold", 20),
- ).pack(side="left", padx=(18, 12))
+ bg="#101c26",
+ fg="#f2f8fb",
+ font=("Segoe UI Semibold", 17),
+ anchor="center",
+ justify="center",
+ ).pack(fill="x", padx=14, pady=(0 if self.sidebar_logo_image else 24, 2))
tk.Label(
- brand,
+ sidebar,
text=self._t("brand_context", version=DISPLAY_VERSION),
- bg="#111820",
- fg="#91a9b5",
- font=("Segoe UI", 11),
- ).pack(side="left")
- signature = tk.Frame(brand, bg="#111820")
- signature.pack(side="right", padx=16)
+ bg="#101c26",
+ fg="#8ca4b3",
+ font=("Segoe UI", 9),
+ anchor="center",
+ justify="center",
+ ).pack(fill="x", padx=14, pady=(0, 18))
+
+ self.sidebar_buttons: dict[str, tk.Button] = {}
+ nav = tk.Frame(sidebar, bg="#101c26")
+ nav.pack(fill="x", padx=12)
+ for page, label in (
+ ("live", self._t("tab_live")),
+ ("controllers", self._t("tab_controllers")),
+ ("plugins", self._t("tab_plugins")),
+ ("library", self._t("tab_library")),
+ ):
+ button = tk.Button(
+ nav,
+ text=label,
+ command=lambda selected=page: self._show_page(selected),
+ anchor="w",
+ padx=14,
+ pady=10,
+ relief="flat",
+ borderwidth=0,
+ font=("Segoe UI Semibold", 10),
+ cursor="hand2",
+ )
+ button.pack(fill="x", pady=2)
+ self.sidebar_buttons[page] = button
+ tk.Button(
+ nav,
+ text=self._t("nav_automap"),
+ command=self.prepare_automap,
+ bg="#08a7c8",
+ fg="#ffffff",
+ activebackground="#0ab4d7",
+ activeforeground="#ffffff",
+ anchor="w",
+ padx=14,
+ pady=10,
+ relief="flat",
+ borderwidth=0,
+ font=("Segoe UI Semibold", 10),
+ cursor="hand2",
+ ).pack(fill="x", pady=(12, 2))
+
+ sidebar_footer = tk.Frame(sidebar, bg="#101c26")
+ sidebar_footer.pack(side="bottom", fill="x", padx=20, pady=18)
+ signature = tk.Frame(sidebar_footer, bg="#101c26")
+ signature.pack(fill="x")
tk.Label(
signature,
text=BRAND_NAME,
- bg="#111820",
- fg="#12c9e8",
- font=("Segoe UI Semibold", 14),
- ).pack(side="left", padx=(0, 12))
+ bg="#101c26",
+ fg="#19d6f2",
+ font=("Segoe UI Semibold", 12),
+ anchor="w",
+ ).pack(side="left")
tk.Label(
signature,
- text="By Mamat\n-------[]--",
- bg="#111820",
- fg="#91a9b5",
- font=("Segoe UI", 9),
- justify="right",
- ).pack(side="left")
-
- ttk.Label(
- self.root,
- text=self._t("intro"),
- style="Subtitle.TLabel",
- ).pack(anchor="w", padx=20, pady=(0, 7))
-
- notebook = ttk.Notebook(self.root)
- self.notebook = notebook
- notebook.pack(fill="both", expand=True, padx=18, pady=(4, 10))
- live_tab = ttk.Frame(notebook, padding=12)
+ text="By Mamat",
+ bg="#101c26",
+ fg="#8ca4b3",
+ font=("Segoe UI", 8),
+ anchor="e",
+ ).pack(side="right")
+ tk.Label(
+ sidebar_footer,
+ text="-------[]--",
+ bg="#101c26",
+ fg="#8ca4b3",
+ font=("Consolas", 8),
+ anchor="w",
+ ).pack(fill="x")
+ tk.Label(
+ sidebar_footer,
+ text=self._t("sidebar_footer"),
+ bg="#101c26",
+ fg="#6f8999",
+ font=("Segoe UI", 8),
+ anchor="w",
+ ).pack(fill="x", pady=(5, 0))
+
+ content = ttk.Frame(shell, padding=(24, 20, 24, 14))
+ content.grid(row=0, column=1, sticky="nsew")
+ content.columnconfigure(0, weight=1)
+ content.rowconfigure(0, weight=1)
+ self.page_frames: dict[str, ttk.Frame] = {}
+ live_tab = ttk.Frame(content)
self.live_tab = live_tab
- controllers_tab = ttk.Frame(notebook, padding=12)
- plugins_tab = ttk.Frame(notebook, padding=12)
- library_tab = ttk.Frame(notebook, padding=12)
- notebook.add(live_tab, text=self._t("tab_live"))
- notebook.add(controllers_tab, text=self._t("tab_controllers"))
- notebook.add(plugins_tab, text=self._t("tab_plugins"))
- notebook.add(library_tab, text=self._t("tab_library"))
+ controllers_tab = ttk.Frame(content)
+ plugins_tab = ttk.Frame(content)
+ library_tab = ttk.Frame(content)
+ self.page_frames.update(
+ live=live_tab,
+ controllers=controllers_tab,
+ plugins=plugins_tab,
+ library=library_tab,
+ )
+ for frame in self.page_frames.values():
+ frame.grid(row=0, column=0, sticky="nsew")
self._build_live_tab(live_tab)
self._build_controllers_tab(controllers_tab)
self._build_plugins_tab(plugins_tab)
self._build_library_tab(library_tab)
+ self._show_page(self.current_page)
ttk.Separator(self.root).pack(fill="x")
ttk.Label(
@@ -1460,6 +1568,27 @@ def _build_ui(self) -> None:
anchor="w",
).pack(fill="x")
+ def _show_page(self, page: str) -> None:
+ if page not in self.page_frames:
+ page = "live"
+ self.current_page = page
+ self.page_frames[page].tkraise()
+ for name, button in self.sidebar_buttons.items():
+ if name == page:
+ button.configure(
+ bg="#173647",
+ fg="#ffffff",
+ activebackground="#1c455a",
+ activeforeground="#ffffff",
+ )
+ else:
+ button.configure(
+ bg="#101c26",
+ fg="#b8c8d2",
+ activebackground="#17303f",
+ activeforeground="#ffffff",
+ )
+
def _build_menu(self) -> None:
menu = tk.Menu(self.root)
file_menu = tk.Menu(menu, tearoff=0)
@@ -1557,20 +1686,37 @@ def _build_menu(self) -> None:
def _build_live_tab(self, parent: ttk.Frame) -> None:
parent.columnconfigure(0, weight=1)
- parent.rowconfigure(6, weight=1)
- ttk.Label(parent, text=self._t("live_title"), style="Title.TLabel").grid(
+ parent.rowconfigure(5, weight=1)
+ header = ttk.Frame(parent)
+ header.grid(row=0, column=0, sticky="ew", pady=(0, 12))
+ header.columnconfigure(0, weight=1)
+ ttk.Label(header, text=self._t("live_title"), style="Title.TLabel").grid(
row=0, column=0, sticky="w"
)
ttk.Label(
- parent,
+ header,
text=self._t("live_description"),
style="Subtitle.TLabel",
- wraplength=900,
+ wraplength=720,
justify="left",
- ).grid(row=1, column=0, sticky="ew", pady=(2, 8))
+ ).grid(row=1, column=0, sticky="ew", pady=(3, 0))
+ self.live_driver_badge = tk.Label(
+ header,
+ bg="#e9f7f1",
+ fg="#16825d",
+ font=("Segoe UI Semibold", 8),
+ padx=10,
+ pady=5,
+ )
+ self.live_driver_badge.grid(row=0, column=1, rowspan=2, sticky="ne")
- controller = ttk.LabelFrame(parent, text=self._t("active_controller"), padding=10)
- controller.grid(row=2, column=0, sticky="ew")
+ controller = ttk.LabelFrame(
+ parent,
+ text=self._t("active_controller"),
+ padding=12,
+ style="Section.TLabelframe",
+ )
+ controller.grid(row=1, column=0, sticky="ew")
controller.columnconfigure(0, weight=1)
self.live_controller_combo = ttk.Combobox(
controller,
@@ -1586,125 +1732,201 @@ def _build_live_tab(self, parent: ttk.Frame) -> None:
text=self._t("configure_active_controller"),
command=self.edit_controller,
).grid(row=0, column=1, sticky="e", padx=(0, 8))
- self.live_driver_badge = tk.Label(
- controller,
- bg="#dff5fb",
- fg="#087d9d",
- font=("Segoe UI Semibold", 8),
- padx=9,
- pady=3,
- )
- self.live_driver_badge.grid(row=0, column=2, sticky="e")
self.live_driver_note = ttk.Label(
controller,
style="Subtitle.TLabel",
- wraplength=850,
+ wraplength=780,
justify="left",
)
self.live_driver_note.grid(
- row=1, column=0, columnspan=3, sticky="ew", pady=(7, 0)
+ row=1, column=0, columnspan=2, sticky="ew", pady=(7, 0)
)
- startup_options = ttk.Frame(controller)
- startup_options.grid(
- row=2, column=0, columnspan=3, sticky="ew", pady=(8, 0)
+
+ startup_options = ttk.LabelFrame(
+ parent,
+ text=self._t("automation_options"),
+ padding=10,
+ style="Section.TLabelframe",
)
- ttk.Checkbutton(
+ startup_options.grid(row=2, column=0, sticky="ew", pady=(10, 0))
+ startup_options.columnconfigure(0, weight=1, uniform="automation")
+ startup_options.columnconfigure(1, weight=1, uniform="automation")
+ windows_start = tk.Checkbutton(
startup_options,
text=self._t("start_with_windows"),
variable=self.start_with_windows_var,
command=self._save_start_with_windows,
- ).pack(side="left")
- self.auto_start_runtime_check = ttk.Checkbutton(
+ anchor="w",
+ justify="left",
+ wraplength=300,
+ bg="#ffffff",
+ activebackground="#ffffff",
+ highlightthickness=0,
+ font=("Segoe UI", 9),
+ )
+ windows_start.grid(row=0, column=0, sticky="ew", padx=(0, 12))
+ self.auto_start_runtime_check = tk.Checkbutton(
startup_options,
text=self._t("auto_start_runtime"),
variable=self.auto_start_runtime_var,
command=self._save_auto_start_runtime,
+ anchor="w",
+ justify="left",
+ wraplength=340,
+ bg="#ffffff",
+ activebackground="#ffffff",
+ highlightthickness=0,
+ font=("Segoe UI", 9),
)
- self.auto_start_runtime_check.pack(side="left", padx=(18, 0))
+ self.auto_start_runtime_check.grid(row=0, column=1, sticky="ew")
- actions = ttk.Frame(parent)
- actions.grid(row=3, column=0, sticky="ew", pady=(9, 5))
- tk.Button(
- actions,
- text=self._t("prepare_automap"),
- command=self.prepare_automap,
- bg="#087d9d",
+ dashboard = ttk.Frame(parent)
+ dashboard.grid(row=3, column=0, sticky="nsew", pady=(10, 0))
+ dashboard.columnconfigure(0, weight=2, uniform="dashboard")
+ dashboard.columnconfigure(1, weight=1, uniform="dashboard")
+ dashboard.rowconfigure(0, weight=1)
+
+ state_frame = ttk.LabelFrame(
+ dashboard,
+ text=self._t("state_frame"),
+ padding=14,
+ style="Section.TLabelframe",
+ )
+ state_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 6))
+ state_frame.columnconfigure(0, weight=1)
+ ttk.Label(
+ state_frame,
+ textvariable=self.runtime_status,
+ wraplength=470,
+ justify="left",
+ font=("Segoe UI Semibold", 12),
+ ).grid(row=0, column=0, columnspan=3, sticky="ew")
+
+ runtime_controls = ttk.Frame(state_frame)
+ runtime_controls.grid(row=1, column=0, columnspan=3, sticky="ew", pady=(13, 12))
+ runtime_controls.columnconfigure(0, weight=1)
+ self.runtime_start_button = tk.Button(
+ runtime_controls,
+ text=f"▶ {self._t('start')}",
+ command=self.start_runtime,
+ bg="#08a7c8",
fg="#ffffff",
- activebackground="#0aa1c9",
+ activebackground="#0ab4d7",
activeforeground="#ffffff",
+ disabledforeground="#d7eef3",
relief="flat",
borderwidth=0,
- padx=14,
- pady=5,
+ padx=16,
+ pady=8,
font=("Segoe UI Semibold", 10),
cursor="hand2",
- ).pack(side="right")
- ttk.Button(
- actions,
- text=self._t("live_settings"),
- command=self.show_live_settings,
- ).pack(side="right", padx=(0, 7))
- ttk.Button(
- actions,
- text=self._t("log_window"),
- command=self.show_runtime_log,
- ).pack(side="right", padx=(0, 7))
- self.runtime_start_button = ttk.Button(
- actions, text=self._t("start"), command=self.start_runtime
)
self.runtime_stop_button = ttk.Button(
- actions, text=self._t("stop"), command=self.stop_runtime
+ runtime_controls, text=self._t("stop"), command=self.stop_runtime
)
self.runtime_restart_button = ttk.Button(
- actions, text=self._t("restart"), command=self.restart_runtime
+ runtime_controls, text=self._t("restart"), command=self.restart_runtime
)
- self.runtime_start_button.pack(side="left")
- self.runtime_stop_button.pack(side="left", padx=(6, 0))
- self.runtime_restart_button.pack(side="left", padx=(6, 0))
- ttk.Button(
- actions,
- text=self._t("minimize"),
- command=self.minimize_to_tray,
- ).pack(side="left", padx=(6, 0))
+ self.runtime_start_button.grid(row=0, column=0, sticky="ew")
+ self.runtime_stop_button.grid(row=0, column=1, padx=(8, 0))
+ self.runtime_restart_button.grid(row=0, column=2, padx=(8, 0))
self.runtime_action_buttons: list[ttk.Button] = []
self.runtime_settings_action_buttons: list[ttk.Button] = []
- state_frame = ttk.LabelFrame(parent, text=self._t("state_frame"), padding=10)
- state_frame.grid(row=4, column=0, sticky="ew", pady=(4, 6))
- state_frame.columnconfigure(0, weight=1)
- ttk.Label(
- state_frame,
- textvariable=self.runtime_status,
- wraplength=500,
- justify="left",
- ).grid(
- row=0, column=0, sticky="ew"
- )
self.previous_bank_button = ttk.Button(
state_frame,
text=self._t("previous_bank"),
command=lambda: self.change_bank(-1),
)
- self.previous_bank_button.grid(row=0, column=1, padx=(8, 4))
+ self.previous_bank_button.grid(row=2, column=0, sticky="w")
ttk.Label(
state_frame,
textvariable=self.runtime_bank,
- width=12,
+ width=9,
anchor="center",
font=("Segoe UI Semibold", 10),
- ).grid(row=0, column=2)
+ ).grid(row=2, column=1, padx=8)
self.next_bank_button = ttk.Button(
state_frame,
text=self._t("next_bank"),
command=lambda: self.change_bank(1),
)
- self.next_bank_button.grid(row=0, column=3, padx=(4, 0))
+ self.next_bank_button.grid(row=2, column=2, sticky="e")
+ ttk.Separator(state_frame).grid(
+ row=3, column=0, columnspan=3, sticky="ew", pady=(14, 10)
+ )
+ ttk.Label(
+ state_frame,
+ text=self._t("last_event"),
+ style="Subtitle.TLabel",
+ ).grid(row=4, column=0, columnspan=3, sticky="w")
+ ttk.Label(
+ state_frame,
+ textvariable=self.runtime_last_event,
+ anchor="w",
+ justify="left",
+ wraplength=470,
+ ).grid(row=5, column=0, columnspan=3, sticky="ew", pady=(3, 0))
+ quick = ttk.LabelFrame(
+ dashboard,
+ text=self._t("quick_access"),
+ padding=14,
+ style="Section.TLabelframe",
+ )
+ quick.grid(row=0, column=1, sticky="nsew", padx=(6, 0))
+ quick.columnconfigure(0, weight=1)
+ ttk.Button(
+ quick,
+ text=self._t("log_window"),
+ command=self.show_runtime_log,
+ ).grid(row=0, column=0, sticky="ew")
+ ttk.Button(
+ quick,
+ text=self._t("shortcuts"),
+ command=self.show_shortcuts,
+ ).grid(row=1, column=0, sticky="ew", pady=(8, 0))
+ ttk.Button(
+ quick,
+ text=self._t("diagnostic"),
+ command=self.show_diagnostics,
+ ).grid(row=2, column=0, sticky="ew", pady=(8, 0))
+ ttk.Button(
+ quick,
+ text=self._t("minimize"),
+ command=self.minimize_to_tray,
+ ).grid(row=3, column=0, sticky="ew", pady=(8, 0))
+
+ self.live_advanced_button = ttk.Button(
+ parent,
+ text=self._t("advanced_controller_closed"),
+ command=self._toggle_live_advanced,
+ )
+ self.live_advanced_button.grid(row=4, column=0, sticky="ew", pady=(10, 0))
+ self.live_advanced_frame = ttk.Frame(parent)
+ self.live_advanced_frame.grid(row=5, column=0, sticky="nsew", pady=(8, 0))
+ self.live_advanced_frame.columnconfigure(0, weight=1)
+ self.live_advanced_frame.rowconfigure(1, weight=1)
+ advanced_actions = ttk.Frame(self.live_advanced_frame)
+ advanced_actions.grid(row=0, column=0, sticky="ew", pady=(0, 8))
+ ttk.Button(
+ advanced_actions,
+ text=self._t("live_settings"),
+ command=self.show_live_settings,
+ ).pack(side="left")
+ ttk.Button(
+ advanced_actions,
+ text=self._t("configure_active_controller"),
+ command=self.edit_controller,
+ ).pack(side="left", padx=(8, 0))
self.ec4_live_frame = ttk.LabelFrame(
- parent, text=self._t("ec4_zone"), padding=10
+ self.live_advanced_frame,
+ text=self._t("ec4_zone"),
+ padding=10,
+ style="Section.TLabelframe",
)
- self.ec4_live_frame.grid(row=5, column=0, sticky="ew", pady=(0, 6))
+ self.ec4_live_frame.grid(row=1, column=0, sticky="nsew")
target_row = ttk.Frame(self.ec4_live_frame)
target_row.pack(fill="x")
ttk.Label(target_row, text=self._t("target_setup")).pack(side="left")
@@ -1746,22 +1968,43 @@ def _build_live_tab(self, parent: ttk.Frame) -> None:
ttk.Button(
learn_row, text=self._t("diagnostic"), command=self.show_diagnostics
).pack(side="right")
-
- event_frame = ttk.LabelFrame(parent, text=self._t("last_event"), padding=10)
- event_frame.grid(row=6, column=0, sticky="nsew", pady=(2, 0))
- ttk.Label(
- event_frame,
- textvariable=self.runtime_last_event,
- anchor="w",
- justify="left",
- wraplength=900,
- ).pack(fill="both", expand=True)
+ if self._live_advanced_open:
+ self.live_advanced_button.configure(
+ text=self._t("advanced_controller_open")
+ )
+ else:
+ self.live_advanced_frame.grid_remove()
self._update_mapping_status()
self._set_runtime_widget_states()
+ def _toggle_live_advanced(self) -> None:
+ self._live_advanced_open = not self._live_advanced_open
+ if self._live_advanced_open:
+ self.live_advanced_frame.grid()
+ self.live_advanced_button.configure(
+ text=self._t("advanced_controller_open")
+ )
+ else:
+ self.live_advanced_frame.grid_remove()
+ self.live_advanced_button.configure(
+ text=self._t("advanced_controller_closed")
+ )
+
def _build_controllers_tab(self, parent: ttk.Frame) -> None:
parent.columnconfigure(0, weight=1)
- parent.rowconfigure(0, weight=1)
+ parent.rowconfigure(2, weight=1)
+ ttk.Label(
+ parent,
+ text=self._t("controllers_title"),
+ style="Title.TLabel",
+ ).grid(row=0, column=0, columnspan=2, sticky="w")
+ ttk.Label(
+ parent,
+ text=self._t("controllers_description"),
+ style="Subtitle.TLabel",
+ wraplength=850,
+ justify="left",
+ ).grid(row=1, column=0, columnspan=2, sticky="ew", pady=(3, 12))
columns = ("maker", "model", "status", "version", "controls", "layout")
self.controller_tree = ttk.Treeview(
parent,
@@ -1792,13 +2035,13 @@ def _build_controllers_tab(self, parent: ttk.Frame) -> None:
parent, orient="vertical", command=self.controller_tree.yview
)
self.controller_tree.configure(yscrollcommand=scrollbar.set)
- self.controller_tree.grid(row=0, column=0, sticky="nsew")
- scrollbar.grid(row=0, column=1, sticky="ns")
+ self.controller_tree.grid(row=2, column=0, sticky="nsew")
+ scrollbar.grid(row=2, column=1, sticky="ns")
self.controller_tree.bind("<>", self._select_controller)
self.controller_tree.bind("", lambda _event: self.edit_controller())
actions = ttk.Frame(parent, padding=(0, 12, 0, 0))
- actions.grid(row=1, column=0, columnspan=2, sticky="ew")
+ actions.grid(row=3, column=0, columnspan=2, sticky="ew")
primary = ttk.Frame(actions)
primary.pack(fill="x")
tk.Button(
@@ -1826,22 +2069,6 @@ def _build_controllers_tab(self, parent: ttk.Frame) -> None:
text=self._t("controller_import"),
command=self.import_controller,
).pack(side="left", padx=(8, 0))
- tk.Button(
- primary,
- text=self._t("prepare_automap"),
- command=self.prepare_automap,
- bg="#087d9d",
- fg="#ffffff",
- activebackground="#0aa1c9",
- activeforeground="#ffffff",
- relief="flat",
- borderwidth=0,
- padx=14,
- pady=5,
- font=("Segoe UI Semibold", 10),
- cursor="hand2",
- ).pack(side="right")
-
secondary = ttk.Frame(actions)
secondary.pack(fill="x", pady=(7, 0))
ttk.Button(secondary, text=self._t("refresh"), command=self.reload_catalog).pack(
@@ -1857,11 +2084,6 @@ def _build_controllers_tab(self, parent: ttk.Frame) -> None:
text=self._t("contribute_controller"),
command=self.contribute_controller,
).pack(side="left", padx=(8, 0))
- ttk.Button(
- secondary,
- text=self._t("minimize"),
- command=self.minimize_to_tray,
- ).pack(side="left", padx=(8, 0))
def _build_plugins_tab(self, parent: ttk.Frame) -> None:
parent.columnconfigure(0, weight=1)
@@ -1922,34 +2144,38 @@ def _build_plugins_tab(self, parent: ttk.Frame) -> None:
toolbar,
textvariable=self.plugin_analysis_status_var,
style="Subtitle.TLabel",
- ).grid(row=0, column=0, sticky="w")
+ wraplength=820,
+ justify="left",
+ ).grid(row=0, column=0, sticky="ew", pady=(0, 7))
+ plugin_actions = ttk.Frame(toolbar)
+ plugin_actions.grid(row=1, column=0, sticky="ew")
self.plugin_scan_all_button = ttk.Button(
- toolbar,
+ plugin_actions,
text=self._t("plugin_scan_all"),
command=self._scan_all_plugin_names,
state="disabled",
style="Accent.TButton",
)
- self.plugin_scan_all_button.grid(row=0, column=1, padx=(8, 0))
+ self.plugin_scan_all_button.pack(side="left")
self.plugin_edit_button = ttk.Button(
- toolbar,
+ plugin_actions,
text=self._t("plugin_edit"),
command=self._open_plugin_profile_editor,
state="disabled",
)
- self.plugin_edit_button.grid(row=0, column=2, padx=(8, 0))
+ self.plugin_edit_button.pack(side="left", padx=(8, 0))
self.plugin_automap_button = ttk.Button(
- toolbar,
+ plugin_actions,
text=self._t("plugin_use_automap"),
command=self._use_plugin_project_in_automap,
state="disabled",
)
- self.plugin_automap_button.grid(row=0, column=3, padx=(8, 0))
+ self.plugin_automap_button.pack(side="left", padx=(8, 0))
ttk.Button(
- toolbar,
+ plugin_actions,
text=self._t("plugin_open_folder"),
command=self._open_plugin_profile_folder,
- ).grid(row=0, column=4, padx=(8, 0))
+ ).pack(side="right")
tree_frame = ttk.Frame(parent)
tree_frame.grid(row=4, column=0, sticky="nsew")
diff --git a/src/silemio_control_hub/library_remote.py b/src/silemio_control_hub/library_remote.py
index 2437176..4e3408c 100644
--- a/src/silemio_control_hub/library_remote.py
+++ b/src/silemio_control_hub/library_remote.py
@@ -226,7 +226,7 @@ def _installed_hub_version() -> str:
try:
return package_version("silemio-control-hub")
except PackageNotFoundError:
- return "2026.4.dev0"
+ return "2026.5.dev0"
def _check_hub_compatibility(manifest: LibraryManifest, hub_version: str) -> None:
diff --git a/src/silemio_control_hub/manuals/Controller-Studio-for-LiveProfessor-Manual-EN.pdf b/src/silemio_control_hub/manuals/Controller-Studio-for-LiveProfessor-Manual-EN.pdf
index 2d6cd4c3e7ef3b1b669446de8c5da874837dc86b..1b75401b9493c49d1b96c67b181427437a874eb4 100644
GIT binary patch
delta 12167
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
U82`Bcb%UVkUy8+nKK$i>1Ho72?*IS*
delta 11878
zcmai)%M!EPp4Mw?+pS+~_iL%UtkNns1OkB^fe=VSASVc?9LRwXNC<>OLIOEYje~dS
zRKHPs6_!2mBD?@EK*!#I+1*-Qdpj@_Dytx*V6Er>Jiqn-ufKNx{eS5GyPv=L`S*VQ
z{hxpG^B?^D)1QC#^B?~FM?Zi0`R70X@y~zq^Dln><$wP1AAkF2Re~TGvij>^{r1P;
z|9<)F|MA-&>2E#+`%9VE1e`
zxMOvISk?ZSc^45Ajz-93h4adx(oM3WOA=RPPBtG)|1dnPN80Te9CND-bIrDCSJF3m
zwQ_|QhL+ZCS3x%NC-Fyj?QtNNqX~8K`|aD?U}-?}>!UzD-!h15
zBX6qjr#`*yjYx!86775yEKGn=VZ5pz76Jrg7j72J&qKmEG3#}wXY!a26SB3`M86Pr
z-sc-#AIPfb$YU>Rf38i>1GFJYHxBcWi0{vDG?RTfUwrb1y8~VMrL$+rx1m-5gAt()
z+s1FtO@6{ns!Nd0P4sQ?sa^9H@Ju;qdqNLX|H2vg8(K#uaN-=P_p*$7+21bi@AL9W
z(#390zbITbZxc{a{K%WdXj7>dQ+^w^Eo|~s@4ci`U)%D|qPyv%Ahud%sK!^8Xr9$L
zhsJV^>-Gf?t6g-nkNNpvg{NsZZ1UIY&=zwjP%DN(P3bKrM~vSlNHfer&hywt}s8WhSs`GAQ3+b3qX7HJ{uG
z8KafzIC|`ob8lBFZKUeRbjt(0uxEO9{y`(>@q61v=M{^^*Vo#L+!pV>=uVR5djalK
z^d+}>&o_4_S=D6pb)0;(F%@)Y>}o!qQd`<0AX=t`N_A_o6*qBM_*tEYlU;RGz_d1L
zrMNXNUEn8-E-kyz-8V)^FzoMVfu8J}=-jNd*R9aaQ2?5d#thh(PzxDh_4)c-F^G+^
zu3Jz*1LBA^j0)E!1@4oy*r$Q7yP;pchpS4@d{%SS+VV_Pj-!OyM@T?T$1*O|#PiK7
z+2B!0tKqn+R|7!64LQ3dO(uTsVGgVEUK~}H88YU&fu)Vysn8+#-fsGyCSjdb)BM88
zEq8u(DGZ2k!1&LGqI|Bq^Hs_~IX`)>-7s}a{po~6+IZYl37@US&vmhk76RJYMMU!4
zPFGjA1{Y;7^a~kQHn&AyB};4!R8MESj91fneIiADPM6|F&D!@*n_OYC&ZGUs%g&!b
z`bZj~|8VyZf0=nPxc!_8VyzK-qKQ%O1|cn!dO>q$Me`Vs@I`-3eV1^R3lO78pk%w5
z>mAc=zHP|-QoKiY!_h8Ot3sU5o&0*FZ#=K$4_Cc@YY!?mJld`X?qfZDg{V%HPuU@@
z)N^(Ptf26fp_%*U-*&=ZA>zkB{q=wT>mTh8`A15WLB|TSmGiCdR7Q4%0A03g+n_#a
zJ#s_|Ql{)V1N>;p(`%LU`r387f61x^j4DG@**4dQ!Q|X)5^1MM7~6}5l$R+C=VtTS
zsP?=xYUQnYl#d4nT8(TVriASWX81ZZu~sYUjJ@k~Wjbt{d!`CPPsrJ>%B?f!L)N;^`b#FWUCR~#SeQf%O>S!6@3q0
zJbrzV@Ey(cLp5w*(;?bO09#cL87n;&?UG%^t`DHPnNE-{m*=eYaeo9m^Px3!qc!V~
zSYq}7MC{Q;9jd;~wkKSZpcDB3oKK@}>9jhWoH-S>#*IzE8-dr7?l&I+jR%wJhQUY3
zIZJ%oeTMplu{TSNW_Crt4AN++4Tn5v6u0l9x2fzLLJL1Q``1Qa2n;+bVX#7!$
zZ2i7OV)^EzIrr^#3|GW?G>nb=(VpzQP}?1{(8Pq~P0hAp|Grz1g`BY8_mawHi{9$n
z^&J;!@^bDbQaaOs`6=F=O+=HMz4xT(+`=(K?Q6v@WTgXq0Vz#hE*EQ#+H0g0C!e^!#N~InmiIAJSn94tw1bg=3EVzDaNdu-fAD0?K{5*
zU8rlcl9w?dw$>PFaA#!s7U`qEKF&Dg5x%ksT=xe=0~_XDcmYnvTxxKD&LK
zd$q!`N7ZFDUmDXcbY4GUOwFrAgCL=tG1ww-uLb&Q0Nj!4kGNiZCK8!O(8KpwsAaZ-ol%udFTXJP6H$;-C;`W#gEI~txA(TYSGav^fP{FTEM-w&6)l-(
zb)>p1?A)1siR8sie$8&wK%>vuAu0d~aWl{5)dqp*8V)LtPqa23Cs2|l;fSaJ}oH()%&$zBtinP&fv{U|h_4_I{qfbng=+K>#8CBkF
zt~7nNZX2|GO~$1wfZt5Nn7_0RC)B_+tDxrsTbo|c@ej`t3AWoeQa(O&{wU|7-OaFRe5uZ#U(#i%a28E
z*R8M@La0`#!){l(l1HFB#I%Z{`Ud#kaALc>>i3|s%^beJKDvZ;3riF3(eAnTQ`2L;
zS^x~TTB>c|3WLq!{T{2)nNrgoQbIghItE*;MO6lvc62>9Pjateud;~yuNFOjIb^;)cEofZ
z7ba6`e*%`LPkiqd7gBHC7#7JE5R7N3(qSpA*IeW}gRN5vD>p_Q1Z$!+AN4pHh6eX^
z&%w9xZ4I?OrHiQp5uWNVkgzW)whzzxGQG2Rm>j!PWEO6wCImPgGp>{xp171ahKOKcL7K`C1Mxe#g)Wx9&$!4t_+=tuN7Aa!8
z`x*p>6u>*S-2^B1q?3)pA&k^F;9@UmtF}76z@3{2Hk3ReImPVc#Q3fgTCQY$b}Oy%=(63{Ogc#Ub7GgxBEG!@-A5uaJ#qIi&ONw6l>JAeeedYbPF+ZCYH
zIe-`Rq)w0Vr-t2wwhAr3|MFcjK%p`#mG1tnA+_iF##C3jC@zSaWdf0N(OdGQsAhy7nH|&f4AW7vp6iJ(kruP9pF0ygMIQsM~FVl74C@
z#i~me+r3_{0i?5+->n<`(;~Owg(X;K`@W2g8w?bMF?K<*C9=0J@B1KQTdy+UmMP39
zcESZ}A~6nq40_g*>MrWJ=PeEtK^4|59b%Y(>S5@}YSRdA0e1b+Ak12Ee~E)Lr%`;%2|XXt3|Nb!6ZW%?wkz-i>MJ&Vr!l
zJkwkiU|*U^HCw2JVz;rXT`4O&^z32AnC`8a8?Cp|Y&suqaDKm<7uqi%oZ&6!M!(`c
zmSg<`8@`rr=+j-@6sr!)uPeVuwyr$6SB8SzKAp(s%Na-wFvHsGsWq$bwF3dv*Gaml
z^rB)awtTpyalDWod}SF0lnyqZJG%W8xM#Vgv8|%uPG8bKDGV}Ru1@7%uCh#zi>xCy
z)yyuGK{@7N`Exyk7v{i7uK}`;(E6VAYr1LlxFl~kcRo`Zi1bPA;;I#wR5sxaLawB*kO}tu(+Zmt7fk|E>fVfTUklNeu*X&5#@B`iwxizL
zXj=Aq48j0}H^iV52f#@QF!igwjUJ#OstXt>q%2@8Nv
z<#izaX8UBcKBz$X5y9Qan3+ydQ=Pi{t6A&KuxYrzsry1$u4L;pz9ow+LwZnCYQ63h
zG0*IMgI=vn3EnJ#@)Df+ZdVzdVMNn(iO^4uDU~SWc$mq=l&r8nQ
z@3GqX-L5_6lMj^49z=P${*n!B+f-t|uF8Ot<}g9YVNZ?APu;~?Is1=Vz4&+|$<54-FmEvoZPKmov&TbPLoV3(
z6(D~6^I!j0@%NFU{s)obUG4D@=&6Vixf`dQ8=Tu;T!IU68l@(0Zrbq#enq~Ip#nswweZ2N`p76Uz9^@?-yfzU|jYeIj
z22&qT1n`wBEyE@@J=q~Q^Jfc!p(GGN=D~eiwz2pLGpV8`mnt(LF7e|%jf|*Lr-+I=
zZT8g1o+4YVmN^Sh<@ACd2hJ_{!y~)k!{cq+iW%ksnh`MvEuu5p*xsYbWY3^hm-4s|
zreEqKn(iw_!`}Mo6xAuP4?Mqz-?lCU)y96Ov72II5Al}NwDAREu-v9C?@ymi=`CDXlTPtLIb*nc*;^+EP3>|Mcx2{r
zpcE@z@9N)!Lvu^4^Y>_fmbFkK>OOMXtovo(dmRzP-b&=5cW+un05U`kHBpXaUND^y3+z*Usd-S
z>ikoA*4zB8A
ztc_<-z7$2EklxIfI8z@x$
zt`cQ*{n&ARn9XyumclJhJmL$7aT@gX(Uec9-4Fz8c?qoA*QH0*9d-SbV=mREw8tQQpPpSF=-C
zDEgOjvTwLEDZm(W|{D7ng;WU<+K_0SBoI(L%&p$e7@eV-I}MHSzk2+s@;iy+Ar
zg8X!a?OQ8HK!S|PqpzYlYrXQ{!1L9)(SBMWoZsu*>_bt1AaSp6o~YukmmjsTf_056
zWs;9vItUAZ6g)hy5qB>8KES^ot6VziJw{c10x7X*4ioU<+-q!nqpS;XUu6l{W%yYE
z58%P(a&m;wL51t9S+BKh>4?d0xATavjSsPcb-2*w7y!)
z858kT-_q}gdTU6Vs0A&<_sg-;>8b98qX)3;mau$2>fuq2{F3HEbJEV>(V;ee4hKS2
z>%WIViJ&J$WIh|aWBJ?ya`9?tsEm7QD&}WizixLcVI=ldtDjvk-gwxmV}0tdnmP-GzGjy|T?cU$HMOxx7;kHp%>yD`2Fj||q#T
z!eb@ZncE~#LQa#e#!Md#S)CL2-abHO+{&jL+?`mv?ur@*;||<8jp6F0I2>lvfX;hO
zM7M#ye4W(F{cXCVF-0|7;-0*@IOyMSaK)I%{0CYI`I$9dh5dQ#M&f?-5S}5}*44G$
z>vKTb(JMTDM{0rM?G5c#u1C=EYPUyC#rq>xfO~RL7BcRy;c8(^w?<57L!Y%jjhEB06&hGlO-r@sJq$nIb`hKG5o%_?Ne{e
zjUzUzD;=LEC%tPewbWaA*!Q=`S`A-7+ekcIluu#aNVW@LiRZ+QL|1a%JUYD37_Q%hkEqiKOFu|0ire0}l~6rgA^{8@FWf1VafUHHYOe_-NjystIsxQd
zE(!GQ!)rm*sdsDBQP|A2fUBJiV-FvJyjwBaALU#xb8l6h4}1B9!PD}=Tv{)-*@Z5G
zFH9j!YtU-^1fD2=wfN>0i5fLRD%r{VMy`ySeeI%9uQinV)dtIq?(duKFJ?C1uheH9
zzV1XPuG8$y*<0^+ya>-0u&q0rM7MP~RgP;tZJyTpm11wjeHNgqG|$hLe!oX8cfu5i
z;|aJ^rq9OFSskee+HHVxl;kJXDyBW)=+`du)$iIL$oE?&<8c8x=UeJ#iWoEcod1T0
zFbEN&cvU25Vb2|W#AwSA_Ki1{yo244!8lF4P4LR*#sndV@ABiC2V_gSQ}HQAQVu`q
z->kGSN_
zxn#oZGQ)BC(mVHVn)k}-r2(ixz229%zFnas^c^F%c5lO{<;fDa(>OMC;5Z@q;yP-q
zTY6rs^7Hf{sadYgD{0mSb_ZJ7e;m2foIT6pxAsim4|Ov9?r-IZCypPTqWH@7!E$dA
zo2G!;i1~OO7AnwL$M;iFhZ;j`erG*G3ffXp^6tlXZG$!iP!^41&~~&&54lt5qs{`8
zeTCa;jlOMB=f`laXxQ~4f)fmP@Q<|K8r!S
z153rB_R20b}KT1c_O-V6v*+cy%weba}?3-Ynj79ZKDAH^ohcpHI0*C+-pc(yg6gtekVe{?yvs+ZXa{
zvzSfa%uHf8n$ArDC-!B1%a0W-JyD9aDYo@$l)2a7GH-RLQ-8lu`eqyj@U%Y{x6SeE
zY3|@x99ieEyD~xb|0GPi{oPG%G>AV~z+eCMH@jAd;nd4>f!|y3E@u|N)ho<(xlvB!
zIhkwDknu4;?l%;zyjc$cs=}3x1E&R^Ue2GmGHi;A;j6knU}XYq`_AzEfOap5PKIH1
z>op0wsfSU9&n5e_A5Wg(-JfpOg(hEr&c|4<@^tT(+9Q|Gu~R>D^x|0VJi9DZNOp^y
zcMu9J**9{TmL6~}u7uwRN
z@91D()7uPFuNy?OsD7lRErOD2%O~NcmL5CeN#XrfqQ1$KKpwtvr4-~E>0#WlD$7GR
zGU-_AO(vJds$0F!zkF)l@Y{v;*|;+OOzQ?~sSE)dnPp6&XXe1UtMj5Rm#t}9rSyW?
zR$#sb>?d7eM%O#lob}mr>Gft1d3OxD`4?1B4Cj2pzmtW(nf$&u3=BvbFqXuJdlY*w
zq+|UCQedSE(AVvTkmrV{Y|d@w0bDKc&BtE@U9vas7ukuSHn2{W7mbl50Z?I4;>96NtI=@u#>agE#sxPzH
zfmD&UzQnXhU=kjQclfxzFx|1aj*!~YUb(5Y2Jg9=iuZGR?!5M@xn*p=P;UC}pDx!g
zTc~5w@C!YsUn*2=C^GTc?v7+jST5#t9(hdZCO!k8smRw(b=yA;zyZZ6&RFv6@o8{b
zD#h(Cmwua`@^AIc1TVaLRc;nmv!pnU?t_M3ZGKICC$*k7=huuwtG(`o%LXFh#9TU_
zuD4qUrNs;~gGlQ2Hj^9i!3&q_85#lEy1j0I-REpPN)Csaq?2Nj_Snv1>m{AV&XF&B
zJ3+74q}wz{*_JC^BZ$7Npfb>z?qByvLXRE<)<#l)wtmT4`#IZPHGZXk4=0qcoJU~V
zhe@liJ~-B#)=QN_ty`#HK6mq5=sQmKZFz}A3MSk6m!C}rYqsCtZ-|$92krhONsq|_
zH(8|g>{+)MP1w-n(8f)Gg+44++9D-;&)nXv)xc(cdE`sjYSQmE<|9sL2(6Ic8u~-@
zE{j^XSVKDl@v9Mql_cx9|6w=nl?&V7_8HtY2!`AFIOPl~Obx8_ll}@gl&?fUS-O&U
z;ZeWAQ#U6~6_hZ{PS`2eD!pQ_iPZS{>UN*T(7QNW(|nRsg^mKWH_<&3PXk#BAFb+TrO1C)gPrzlPoLWGVxkK9eJ|~A$Tbfjmppf0>~cz5
zXL%c@p(zN-d;{^d%}hwoEQ-waI-^a2r%6KEDP(TfYQF3?uUB!aewz)yq2P#k$2zfM
zyYmrn*3C^zD7PWAR|Y1BdT%!R({_xm$;00w*MB+qBG>vK8rpB8BmXVpr+iaS%SPdl
z@>NU^n)2$PFs~EzDpNR@%(i)8JQ!Txe3>`FLA}(NxqpjX$-|reBG*H+XEV8N
zc_4=#z7KK$NShZOA-SV3Q;k?c=KGiCJwG_RI`!klJm-%q$*h(f#^jEbXIcGLz!-0z
zcr`|IuhM#4+bXx<2Jbs7eKZuB&u{w}+bD49MS$dnM-$)8qHUxT`YZ2#=f@)Je_*6X}F`XGqoZC2f+>sZ@K7(}RLAgI;g_W$X9U
z+I8rG6j5u<@doJET&Qm82;~nOxRV*@kS^jMED?Mk`^LeV+gNX*;9a~5eyJjnK)AD8
zW2=xX1{ED+&qam6n-M#o61PoQ#K9B_9W^+wni4c^u3xBs|)Hu`GTeU_1TDf4U3=WSmfM0bBI%~E>_
z^iT3ak-yuoC%-5hpfgVLbIcQSHjQwY5)2pOx?H9B9>v5Jnz%hMeKm^7o4=vIFEw<-
z{h_#2ZtMlI&fmvU0h_Jg3&%hE>;E+R@n3%Pp{d_hfB*Si_Pbxchg!8x)9n8<)?jPo
zAD_pvO!be(=o-N`{&?)$oc+hw(&X35{mpsl-`$w5H)`zPUHp1@r%U+wrQQCeA`gD|
zi``}@sz!MYpNhOFApM5lh^n=aU}>^WvNeY6{D05*RZxH2|D0xM;ujbmPQ+jSH!8G^
AQUCw|
diff --git a/src/silemio_control_hub/manuals/Controller-Studio-for-LiveProfessor-Notice-FR.pdf b/src/silemio_control_hub/manuals/Controller-Studio-for-LiveProfessor-Notice-FR.pdf
index 69c69e935f3347be2872dacec013e009de747f43..bd6029c39fc8a121cb1d7fba98c8443dab71f961 100644
GIT binary patch
delta 13022
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
delta 12734
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