6 Commits
Author SHA1 Message Date
Michael e759a1a7d5 prepare v1.1.0 release 2026-05-31 10:53:40 +02:00
LogiFlowandClaude 3b5ede5872 Robustesse release : accepte les tags V*/v* et normalise la version (#3)
* Passe la version à 1.1.0 et publie automatiquement les releases

- Monte la version à 1.1.0 (tauri.conf.json, Cargo.toml, package.json)
  pour générer un nouvel installeur via la CI.
- Met releaseDraft à false dans le workflow : les releases étaient
  créées en brouillon, donc invisibles et jamais marquées 'Latest' sur
  la page GitHub. Elles sont désormais publiées et présentées comme la
  dernière version.

* Durcit le déclenchement de la release (tags V*/v*) et normalise la version

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-31 10:41:18 +02:00
LogiFlowandClaude a635aa29a4 Correction de la restauration du widget + release automatique v1.1.0 (#2)
- Monte la version à 1.1.0 (tauri.conf.json, Cargo.toml, package.json)
  pour générer un nouvel installeur via la CI.
- Met releaseDraft à false dans le workflow : les releases étaient
  créées en brouillon, donc invisibles et jamais marquées 'Latest' sur
  la page GitHub. Elles sont désormais publiées et présentées comme la
  dernière version.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-31 10:28:14 +02:00
LogiFlow fcac504693 Merge pull request #1 from R0m1k3/claude/amazing-ramanujan-56hE0
Corrige la restauration du widget masqué sans icône de barre des tâches
2026-05-31 04:16:22 +02:00
Claude 27d3fb7d50 Corrige la restauration du widget masqué sans icône de barre des tâches
Le bouton Réduire appelait minimize() alors que la fenêtre utilise
skipTaskbar:true : sans entrée dans la barre des tâches, la fenêtre
minimisée devenait irrécupérable. Le bouton masque désormais vers le
system tray.

La logique de bascule du tray gère maintenant correctement l'état
minimisé (is_minimized) et restaure la fenêtre via unminimize/show/
set_focus, faisant de l'icône de la barre d'état un point de
restauration fiable dans tous les cas.
2026-05-31 01:27:33 +00:00
Michael d9c1720523 preserve API key across container updates 2026-05-28 08:10:41 +02:00
9 changed files with 122 additions and 31 deletions

No files matched your search

+33 -11
View File
@@ -3,7 +3,9 @@ name: Release Tauri App
on:
push:
tags:
# Accept lowercase and uppercase tags, for example v1.1.0.0 or V1.1.0.0.
- 'v*'
- 'V*'
workflow_dispatch:
jobs:
@@ -38,20 +40,37 @@ jobs:
cd widget
npm install
- name: Update version in tauri.conf.json
- name: Update app version from tag
shell: bash
run: |
TAG_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//')
if [[ ! "$TAG_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
TAG_VERSION="1.0.0"
# Keep Tauri/Cargo on valid SemVer while allowing release tags like V1.1.0.0.
RAW_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^[vV]//')
APP_VERSION="1.1.0"
if [[ "$RAW_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)(\.[0-9]+)?$ ]]; then
APP_VERSION="${BASH_REMATCH[1]}"
fi
echo "Updating tauri.conf.json version to $TAG_VERSION"
echo "Updating app version to $APP_VERSION"
node -e "
const fs = require('fs');
const file = './widget/src-tauri/tauri.conf.json';
const config = JSON.parse(fs.readFileSync(file, 'utf8'));
config.package.version = '$TAG_VERSION';
fs.writeFileSync(file, JSON.stringify(config, null, 2), 'utf8');
const updateJson = (file, update) => {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
update(data);
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n', 'utf8');
};
updateJson('./widget/package.json', (pkg) => {
pkg.version = '$APP_VERSION';
});
updateJson('./widget/src-tauri/tauri.conf.json', (config) => {
config.package.version = '$APP_VERSION';
});
const cargoFile = './widget/src-tauri/Cargo.toml';
const cargo = fs.readFileSync(cargoFile, 'utf8')
.replace(/^version = \".*\"$/m, 'version = \"$APP_VERSION\"');
fs.writeFileSync(cargoFile, cargo, 'utf8');
"
- name: Build the Tauri app
@@ -61,7 +80,10 @@ jobs:
with:
tagName: ${{ github.ref_name }}
releaseName: 'TodoFlow ${{ github.ref_name }}'
releaseBody: 'Release generated automatically by GitHub Actions.'
releaseDraft: true
releaseBody: |
Installeur Windows de TodoFlow ${{ github.ref_name }}.
Telechargez le fichier `.exe` (NSIS) ou `.msi` ci-dessous pour installer l'application.
releaseDraft: false
prerelease: false
projectPath: './widget'
+2 -1
View File
@@ -30,11 +30,12 @@ ENV PYTHONUNBUFFERED=1
RUN mkdir -p /app/data
# Création d'un utilisateur système non-privilégié pour des raisons de sécurité
RUN useradd -u 8888 appuser && chown -R appuser:appuser /app
RUN chmod +x /app/entrypoint.sh && useradd -u 8888 appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
# Lancement d'Uvicorn
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
+1 -1
View File
@@ -43,7 +43,7 @@ async def lifespan(app: FastAPI):
app = FastAPI(
title="TodoFlow API",
description="API centralisée pour la Todo list, connectée au widget de bureau et pilotable par Agent IA.",
version="1.0.0",
version="1.1.0",
lifespan=lifespan
)
+51
View File
@@ -0,0 +1,51 @@
#!/bin/sh
set -eu
DEFAULT_API_KEYS_RAW="widget-token-secure-789,ai-agent-token-secure-101"
PERSISTED_ENV_FILE="${PERSISTED_ENV_FILE:-/app/data/.env}"
APP_ENV_FILE="${APP_ENV_FILE:-/app/.env}"
read_env_value() {
file="$1"
name="$2"
if [ ! -f "$file" ]; then
return 0
fi
sed -n "s/^[[:space:]]*${name}[[:space:]]*=[[:space:]]*//p" "$file" \
| tail -n 1 \
| sed 's/^"//; s/"$//; s/^'\''//; s/'\''$//'
}
write_env_value() {
file="$1"
name="$2"
value="$3"
mkdir -p "$(dirname "$file")"
if [ -f "$file" ] && grep -q "^[[:space:]]*${name}[[:space:]]*=" "$file"; then
escaped_value=$(printf '%s' "$value" | sed 's/[\/&]/\\&/g')
sed -i "s/^[[:space:]]*${name}[[:space:]]*=.*/${name}=${escaped_value}/" "$file"
else
printf '%s=%s\n' "$name" "$value" >> "$file"
fi
}
persisted_api_keys="$(read_env_value "$PERSISTED_ENV_FILE" "API_KEYS_RAW" || true)"
app_env_api_keys="$(read_env_value "$APP_ENV_FILE" "API_KEYS_RAW" || true)"
current_api_keys="${API_KEYS_RAW:-}"
if [ -n "$persisted_api_keys" ] && { [ -z "$current_api_keys" ] || [ "$current_api_keys" = "$DEFAULT_API_KEYS_RAW" ]; }; then
export API_KEYS_RAW="$persisted_api_keys"
elif [ -n "$app_env_api_keys" ] && { [ -z "$current_api_keys" ] || [ "$current_api_keys" = "$DEFAULT_API_KEYS_RAW" ]; }; then
export API_KEYS_RAW="$app_env_api_keys"
if [ "$app_env_api_keys" != "$DEFAULT_API_KEYS_RAW" ]; then
write_env_value "$PERSISTED_ENV_FILE" "API_KEYS_RAW" "$app_env_api_keys"
fi
elif [ -n "$current_api_keys" ] && [ "$current_api_keys" != "$DEFAULT_API_KEYS_RAW" ]; then
write_env_value "$PERSISTED_ENV_FILE" "API_KEYS_RAW" "$current_api_keys"
fi
exec "$@"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "todoflow-widget",
"version": "1.0.0",
"version": "1.1.0",
"private": true,
"scripts": {
"tauri": "tauri"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "todoflow"
version = "1.0.0"
version = "1.1.0"
description = "Sleek Windows TodoFlow desktop widget"
authors = ["Michael"]
edition = "2021"
+28 -14
View File
@@ -67,6 +67,32 @@ fn get_autostart() -> Result<bool, String> {
Ok(val.is_ok())
}
// --- GESTION DE LA VISIBILITÉ DE LA FENÊTRE ---
fn toggle_window_visibility(app: &AppHandle) {
// Bascule l'affichage du widget depuis l'icône de la barre d'état.
// Comme la fenêtre est configurée avec skipTaskbar:true, l'icône du tray est le
// seul point de restauration : il doit donc fonctionner dans tous les états.
let window = match app.get_window("main") {
Some(w) => w,
None => return,
};
// Une fenêtre réduite est toujours rapportée comme "visible" par Tauri. On la
// considère donc comme masquée afin de la restaurer correctement (et non la re-masquer).
let is_minimized = window.is_minimized().unwrap_or(false);
let is_visible = window.is_visible().unwrap_or(false);
if is_visible && !is_minimized {
let _ = window.hide();
} else {
// Restauration robuste : dé-minimiser, ré-afficher, puis ramener au premier plan.
let _ = window.unminimize();
let _ = window.show();
let _ = window.set_focus();
}
}
// --- CONTEXTE ET INITIALISATION ---
fn main() {
@@ -84,26 +110,14 @@ fn main() {
.on_system_tray_event(|app, event| match event {
SystemTrayEvent::LeftClick { .. } => {
// Un simple clic gauche sur l'icône de la barre des tâches bascule la visibilité
let window = app.get_window("main").unwrap();
if window.is_visible().unwrap() {
window.hide().unwrap();
} else {
window.show().unwrap();
window.set_focus().unwrap();
}
toggle_window_visibility(app);
}
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"quit" => {
std::process::exit(0);
}
"toggle" => {
let window = app.get_window("main").unwrap();
if window.is_visible().unwrap() {
window.hide().unwrap();
} else {
window.show().unwrap();
window.set_focus().unwrap();
}
toggle_window_visibility(app);
}
_ => {}
},
+1 -1
View File
@@ -8,7 +8,7 @@
},
"package": {
"productName": "TodoFlow",
"version": "1.0.0"
"version": "1.1.0"
},
"tauri": {
"allowlist": {
+4 -1
View File
@@ -88,7 +88,10 @@ document.addEventListener("DOMContentLoaded", async () => {
// Événements Tauri pour le contrôle de la fenêtre de bureau
if (appWindow) {
btnMinimize.addEventListener("click", () => appWindow.minimize());
// La fenêtre utilise skipTaskbar:true : elle n'apparaît jamais dans la barre des tâches.
// Un minimize() classique la rendrait donc irrécupérable. On masque plutôt vers le
// system tray, d'où elle peut toujours être restaurée via l'icône de la barre d'état.
btnMinimize.addEventListener("click", () => appWindow.hide());
btnClose.addEventListener("click", () => appWindow.hide()); // Masquer vers le tray au clic sur 'Fermer'
// Récupération de l'état réel du démarrage automatique depuis le registre Windows