mirror of
https://github.com/R0m1k3/TodoFlow.git
synced 2026-10-14 19:00:04 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e759a1a7d5 | ||
|
|
3b5ede5872 | ||
|
|
a635aa29a4 | ||
|
|
fcac504693 | ||
|
|
27d3fb7d50 | ||
|
|
d9c1720523 |
No files matched your search
@@ -3,7 +3,9 @@ name: Release Tauri App
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
|
# Accept lowercase and uppercase tags, for example v1.1.0.0 or V1.1.0.0.
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
- 'V*'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -38,20 +40,37 @@ jobs:
|
|||||||
cd widget
|
cd widget
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
- name: Update version in tauri.conf.json
|
- name: Update app version from tag
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
TAG_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//')
|
# Keep Tauri/Cargo on valid SemVer while allowing release tags like V1.1.0.0.
|
||||||
if [[ ! "$TAG_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
RAW_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^[vV]//')
|
||||||
TAG_VERSION="1.0.0"
|
APP_VERSION="1.1.0"
|
||||||
|
if [[ "$RAW_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)(\.[0-9]+)?$ ]]; then
|
||||||
|
APP_VERSION="${BASH_REMATCH[1]}"
|
||||||
fi
|
fi
|
||||||
echo "Updating tauri.conf.json version to $TAG_VERSION"
|
echo "Updating app version to $APP_VERSION"
|
||||||
node -e "
|
node -e "
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const file = './widget/src-tauri/tauri.conf.json';
|
|
||||||
const config = JSON.parse(fs.readFileSync(file, 'utf8'));
|
const updateJson = (file, update) => {
|
||||||
config.package.version = '$TAG_VERSION';
|
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||||
fs.writeFileSync(file, JSON.stringify(config, null, 2), '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
|
- name: Build the Tauri app
|
||||||
@@ -61,7 +80,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tagName: ${{ github.ref_name }}
|
tagName: ${{ github.ref_name }}
|
||||||
releaseName: 'TodoFlow ${{ github.ref_name }}'
|
releaseName: 'TodoFlow ${{ github.ref_name }}'
|
||||||
releaseBody: 'Release generated automatically by GitHub Actions.'
|
releaseBody: |
|
||||||
releaseDraft: true
|
Installeur Windows de TodoFlow ${{ github.ref_name }}.
|
||||||
|
|
||||||
|
Telechargez le fichier `.exe` (NSIS) ou `.msi` ci-dessous pour installer l'application.
|
||||||
|
releaseDraft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
projectPath: './widget'
|
projectPath: './widget'
|
||||||
+2
-1
@@ -30,11 +30,12 @@ ENV PYTHONUNBUFFERED=1
|
|||||||
RUN mkdir -p /app/data
|
RUN mkdir -p /app/data
|
||||||
|
|
||||||
# Création d'un utilisateur système non-privilégié pour des raisons de sécurité
|
# 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
|
USER appuser
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# Lancement d'Uvicorn
|
# Lancement d'Uvicorn
|
||||||
|
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|
||||||
+1
-1
@@ -43,7 +43,7 @@ async def lifespan(app: FastAPI):
|
|||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="TodoFlow API",
|
title="TodoFlow API",
|
||||||
description="API centralisée pour la Todo list, connectée au widget de bureau et pilotable par Agent IA.",
|
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
|
lifespan=lifespan
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "todoflow-widget",
|
"name": "todoflow-widget",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "todoflow"
|
name = "todoflow"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
description = "Sleek Windows TodoFlow desktop widget"
|
description = "Sleek Windows TodoFlow desktop widget"
|
||||||
authors = ["Michael"]
|
authors = ["Michael"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -67,6 +67,32 @@ fn get_autostart() -> Result<bool, String> {
|
|||||||
Ok(val.is_ok())
|
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 ---
|
// --- CONTEXTE ET INITIALISATION ---
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -84,26 +110,14 @@ fn main() {
|
|||||||
.on_system_tray_event(|app, event| match event {
|
.on_system_tray_event(|app, event| match event {
|
||||||
SystemTrayEvent::LeftClick { .. } => {
|
SystemTrayEvent::LeftClick { .. } => {
|
||||||
// Un simple clic gauche sur l'icône de la barre des tâches bascule la visibilité
|
// Un simple clic gauche sur l'icône de la barre des tâches bascule la visibilité
|
||||||
let window = app.get_window("main").unwrap();
|
toggle_window_visibility(app);
|
||||||
if window.is_visible().unwrap() {
|
|
||||||
window.hide().unwrap();
|
|
||||||
} else {
|
|
||||||
window.show().unwrap();
|
|
||||||
window.set_focus().unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
||||||
"quit" => {
|
"quit" => {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
"toggle" => {
|
"toggle" => {
|
||||||
let window = app.get_window("main").unwrap();
|
toggle_window_visibility(app);
|
||||||
if window.is_visible().unwrap() {
|
|
||||||
window.hide().unwrap();
|
|
||||||
} else {
|
|
||||||
window.show().unwrap();
|
|
||||||
window.set_focus().unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "TodoFlow",
|
"productName": "TodoFlow",
|
||||||
"version": "1.0.0"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|||||||
+4
-1
@@ -88,7 +88,10 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
// Événements Tauri pour le contrôle de la fenêtre de bureau
|
// Événements Tauri pour le contrôle de la fenêtre de bureau
|
||||||
if (appWindow) {
|
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'
|
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
|
// Récupération de l'état réel du démarrage automatique depuis le registre Windows
|
||||||
|
|||||||
Reference in new issue
Block a user