mac support

This commit is contained in:
2026-08-26 13:45:15 +02:00
parent 15d1319374
commit a9897606ac
6 changed files with 115 additions and 21 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ le même dossier, synchronise son contenu, puis remplace la destination.
Une interruption ne laisse donc pas un JSON partiellement écrit. Pour
une modification concurrente de type lire-modifier-écrire, utiliser
=atomic_update_json= : cet utilitaire protège l'opération complète avec
un verrou inter-processus Linux/Windows.
un verrou inter-processus Linux, Windows et macOS.
* Convention des scripts standardisés
+42 -5
View File
@@ -37,13 +37,24 @@ labels contenant notamment =:= ne sont pas acceptés par Windows.
*** Python 3.11 ou plus récent
Sous macOS avec Homebrew, installer Python et la version correspondante
de Tkinter avant de créer l'environnement virtuel. Par exemple :
#+BEGIN_SRC bash
brew install python@3.13 python-tk@3.13
#+END_SRC
Utiliser alors =python3.13= à la place de =python= dans les commandes
de création de l'environnement si la commande non versionnée n'est pas
disponible.
Créer et activer un environnement virtuel est recommandé :
#+BEGIN_SRC bash
python -m venv .venv
#+END_SRC
Sous Linux :
Sous Linux et macOS :
#+BEGIN_SRC bash
source .venv/bin/activate
@@ -99,11 +110,35 @@ les installations Python qui ne l'incluent pas d'origine.
Fermer puis rouvrir le terminal et le GUI après une modification de
=PATH=.
**** macOS
Avec Homebrew, installer Poppler et MacTeX :
#+BEGIN_SRC bash
brew install poppler
brew install --cask mactex
#+END_SRC
MacTeX fournit la distribution TeX Live complète utilisée par les
modèles de Copienator. Après son installation, rouvrir le terminal ou
exécuter :
#+BEGIN_SRC bash
eval "$(/usr/libexec/path_helper)"
#+END_SRC
Copienator recherche aussi les exécutables dans =/opt/homebrew/bin=,
=/usr/local/bin= et =/Library/TeX/texbin=, notamment lorsque le GUI ne
récupère pas le =PATH= du terminal. Ces instructions conviennent aux
Mac Intel et Apple Silicon sous macOS 11 ou plus récent.
*** Programme externe facultatif
PDF Arranger permet d'ouvrir et de réorganiser plus facilement les
PDF. Son absence est signalée comme facultative dans le diagnostic. Il
doit fournir la commande =pdf-arranger= ou =pdfarranger= dans =PATH=.
Sous macOS, Copienator utilise automatiquement Aperçu comme solution de
repli.
*** Accès à Gemini
@@ -155,10 +190,12 @@ absent, vide ou ne contient que des commentaires. Ces automatismes ne
se répètent pas lors d'un retour en arrière.
Le bouton =Diagnostic…= vérifie les modules Python, Poppler, LaTeX,
PDF Arranger et la configuration Gemini. Sous Windows, les exécutables
externes doivent être accessibles depuis =PATH=. Quand la création de
liens symboliques ou physiques n'est pas autorisée, l'export et la
préparation de =A Rendre= utilisent automatiquement une copie normale.
PDF Arranger (ou Aperçu sous macOS) et la configuration Gemini. Sous
Windows, les exécutables externes doivent être accessibles depuis
=PATH=. Sous macOS, les emplacements standards de Homebrew et MacTeX
sont également inspectés. Quand la création de liens symboliques ou
physiques n'est pas autorisée, l'export et la préparation de =A Rendre=
utilisent automatiquement une copie normale.
Les chemins des étapes personnelles peuvent être adaptés avec
=CURRENT_SCORE_ODS_PATH=, =FINAL_SCORE_ODS_PATH=,
+2 -2
View File
@@ -119,11 +119,11 @@ Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~
2. =python -m copienator review-labels Interro=
Permet de vérifier visuellement les labels trouvés.
+ Sous linux, on peut faire =e= pour ouvrir le fichier .json et
+ Sous Linux et macOS, on peut faire =e= pour ouvrir le fichier .json et
l'éditer a la main.
+ Quand un label est manquant, il est possible de cliquer sur
l'image, ce qui copie les coordonnées dans le presse papier
(sous linux…), puis on peut l'ajouter à la main.
puis on peut l'ajouter à la main.
+ Utilisation de `_`, `|…` et `…|` :
+ `|…` n'est pas arrêté verticalement par son type opposé.
+ `…|` est stoppé horizontalement par le `|…` le plus proche.
+51 -7
View File
@@ -8,6 +8,12 @@ import sys
from pathlib import Path
from typing import Literal
MACOS_EXECUTABLE_DIRECTORIES = (
Path("/opt/homebrew/bin"),
Path("/usr/local/bin"),
Path("/Library/TeX/texbin"),
)
WINDOWS_RESERVED_NAMES = {
"CON",
"PRN",
@@ -71,6 +77,36 @@ def safe_filename(value: str, fallback: str = "Unknown") -> str:
return cleaned
def add_platform_executable_paths(
environment: dict[str, str], platform_name: str | None = None
) -> dict[str, str]:
"""Expose common desktop-installed executables to child processes."""
result = dict(environment)
if (platform_name or sys.platform) != "darwin":
return result
existing = result.get("PATH", "").split(os.pathsep)
additions = [str(path) for path in MACOS_EXECUTABLE_DIRECTORIES if path.is_dir()]
result["PATH"] = os.pathsep.join(dict.fromkeys([*additions, *existing]))
return result
def find_executable(
*candidates: str, platform_name: str | None = None
) -> str | None:
"""Find a command in PATH or in standard macOS package locations."""
for candidate in candidates:
executable = shutil.which(candidate)
if executable:
return executable
if (platform_name or sys.platform) == "darwin":
for directory in MACOS_EXECUTABLE_DIRECTORIES:
for candidate in candidates:
executable = directory / candidate
if executable.is_file() and os.access(executable, os.X_OK):
return str(executable)
return None
def open_path(path: str | Path) -> None:
"""Open a file with the desktop's default application."""
target = str(Path(path).expanduser().resolve())
@@ -78,17 +114,26 @@ def open_path(path: str | Path) -> None:
os.startfile(target) # type: ignore[attr-defined]
elif sys.platform.startswith("linux"):
subprocess.Popen(["xdg-open", target])
elif sys.platform == "darwin":
opener = find_executable("open") or "/usr/bin/open"
subprocess.Popen([opener, target])
else:
raise RuntimeError(f"Unsupported platform: {sys.platform}")
def launch_pdf_arranger(path: str | Path) -> None:
executable = shutil.which("pdf-arranger") or shutil.which("pdfarranger")
if not executable:
raise FileNotFoundError(
"PDF Arranger is not installed or is not available in PATH."
)
subprocess.Popen([executable, str(Path(path).expanduser().resolve())])
target = str(Path(path).expanduser().resolve())
executable = find_executable("pdf-arranger", "pdfarranger")
if executable:
subprocess.Popen([executable, target])
return
if sys.platform == "darwin":
opener = find_executable("open") or "/usr/bin/open"
subprocess.Popen([opener, "-b", "com.apple.Preview", target])
return
raise FileNotFoundError(
"PDF Arranger is not installed or is not available in PATH."
)
def replace_with_link_or_copy(
@@ -118,4 +163,3 @@ def replace_with_link_or_copy(
shutil.copy2(source_path, destination_path)
return "copy"
+7 -2
View File
@@ -8,7 +8,12 @@ from tkinter import filedialog, messagebox, ttk
from typing import Any
from copienator import ExitCode
from copienator.platform import WindowsLabelError, open_path, validate_windows_labels
from copienator.platform import (
WindowsLabelError,
add_platform_executable_paths,
open_path,
validate_windows_labels,
)
from .diagnostics import collect_diagnostics
from .runner import ProcessRunner
@@ -95,7 +100,7 @@ def plotting_shortcut_lines() -> list[str]:
def build_runner_environment(
base: dict[str, str], api_key: str, proxy: str, use_proxy: bool
) -> dict[str, str]:
environment = dict(base)
environment = add_platform_executable_paths(base)
environment["PYTHONUNBUFFERED"] = "1"
environment["PYTHONIOENCODING"] = "utf-8"
if api_key.strip():
+12 -4
View File
@@ -7,7 +7,7 @@ import sys
from dataclasses import dataclass
from pathlib import Path
from copienator.platform import windows_filename_problems
from copienator.platform import find_executable, windows_filename_problems
@dataclass(frozen=True)
@@ -33,8 +33,10 @@ def collect_diagnostics(
checks = [
DiagnosticCheck(
"Système",
os.name == "nt" or sys.platform.startswith("linux"),
f"{sys.platform} — Linux et Windows sont pris en charge",
os.name == "nt"
or sys.platform.startswith("linux")
or sys.platform == "darwin",
f"{sys.platform} — Linux, Windows et macOS sont pris en charge",
),
DiagnosticCheck("Python", True, sys.executable),
]
@@ -65,7 +67,13 @@ def collect_diagnostics(
("PDF Arranger", ("pdf-arranger", "pdfarranger"), False),
)
for label, candidates, required in programs:
executable = next((shutil.which(candidate) for candidate in candidates if shutil.which(candidate)), None)
executable = find_executable(*candidates)
if label == "PDF Arranger" and not executable and sys.platform == "darwin":
system_open = Path("/usr/bin/open")
opener = find_executable("open") or (
str(system_open) if system_open.is_file() else None
)
executable = f"{opener} (Aperçu)" if opener else None
detail = executable or "Introuvable dans PATH"
checks.append(DiagnosticCheck(label, executable is not None, detail, required))