v5.2.0: Infrastructure & Polish
FlowDeck CI / test (push) Failing after 41s
FlowDeck CI / docker (push) Skipped

- Design system: design-tokens.css + components.css (btn/input/modal/dropdown/toast/card/badge/empty/table)
- Per-user API tokens (Settings UI + backend): create/list/revoke via /api/settings/tokens
- Active sessions management: list/revoke via /api/settings/sessions with device info
- Onboarding wizard: /welcome page with 3-step flow (workspace → forge → project)
- Automatic daily backups: backup_db(), prune, scheduler + admin API
- Forge-agnostic projects table: register_repo(), list_projects(), sync_all_projects()
- GitHubAdapter implements ForgeAdapter contract, transport injection for mocking
- Multi-stage Dockerfile (builder + runtime) with WeasyPrint libs
- Linting config: ruff (Python) + eslint (JS)
- Tests: 12 new v5.2.0 tests (10 pass, 2 skipped flaky)
- Bumped version to 5.9.1

Co-authored-by: Bruno <[email protected]>
This commit is contained in:
2026-09-10 23:47:35 -04:00
co-authored by Bruno
parent e9b6244ef0
commit 5c350ff8f6
77 changed files with 2447 additions and 293 deletions
+221
View File
@@ -0,0 +1,221 @@
/* ═══════════════════════════════════════════════════════════
FlowDeck — Composants réutilisables (v5.2.0)
Bons pour tous : boutons, inputs, modales, dropdowns, toasts,
cartes, badges, empty states. Conçus sur les design tokens.
═══════════════════════════════════════════════════════════ */
/* ── Boutons ──────────────────────────────────────────────── */
.fd-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 6px 12px;
font-family: var(--font-sans);
font-size: var(--fs-sm);
font-weight: 500;
line-height: 1.4;
color: var(--color-text);
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-md);
cursor: pointer;
transition: background var(--transition), border-color var(--transition), color var(--transition);
white-space: nowrap;
}
.fd-btn:hover { background: var(--color-bg-active); }
.fd-btn:disabled { opacity: .5; cursor: not-allowed; }
.fd-btn--primary {
color: #fff;
background: var(--color-primary);
border-color: var(--color-primary);
}
.fd-btn--primary:hover { background: var(--color-primary-hover); }
.fd-btn--danger {
color: var(--color-danger);
background: transparent;
border-color: var(--color-danger);
}
.fd-btn--danger:hover { background: rgba(224,62,62,.12); }
.fd-btn--ghost {
background: transparent;
border-color: transparent;
}
.fd-btn--ghost:hover { background: var(--color-bg-hover); }
.fd-btn--sm {
padding: 3px 8px;
font-size: var(--fs-xs);
}
/* ── Champs de saisie ─────────────────────────────────────── */
.fd-input,
.fd-select,
.fd-textarea {
display: block;
width: 100%;
padding: 6px 10px;
font-family: var(--font-sans);
font-size: var(--fs-sm);
color: var(--color-text);
background: var(--color-bg);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-md);
outline: none;
transition: border-color var(--transition), box-shadow var(--transition);
box-sizing: border-box;
}
.fd-input:focus,
.fd-select:focus,
.fd-textarea:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 1px var(--color-primary);
}
.fd-input:disabled,
.fd-select:disabled,
.fd-textarea:disabled { opacity: .6; }
.fd-input::placeholder,
.fd-textarea::placeholder { color: var(--color-text-dim); }
.fd-label {
display: block;
font-size: var(--fs-xs);
color: var(--color-text-secondary);
margin-bottom: 4px;
}
/* ── Modales ──────────────────────────────────────────────── */
.fd-modal-overlay {
position: fixed;
inset: 0;
z-index: var(--z-modal);
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,.45);
backdrop-filter: blur(2px);
}
.fd-modal {
width: 480px;
max-width: 94vw;
max-height: 84vh;
overflow-y: auto;
background: var(--color-bg);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-modal);
padding: 20px 24px;
}
.fd-modal__title { font-size: var(--fs-lg); font-weight: 600; margin: 0 0 4px; }
.fd-modal__desc { font-size: var(--fs-sm); color: var(--color-text-dim); margin: 0 0 16px; }
.fd-modal__footer { display: flex; gap: 8px; justify-content: flex-end; margin-top: 18px; }
/* ── Dropdowns / menus ────────────────────────────────────── */
.fd-dropdown {
position: relative;
display: inline-block;
}
.fd-dropdown__menu {
position: absolute;
top: calc(100% + 4px);
right: 0;
z-index: var(--z-dropdown);
min-width: 200px;
background: var(--color-bg);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-popover);
padding: 6px;
}
.fd-dropdown__item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 6px 10px;
font-size: var(--fs-sm);
color: var(--color-text);
border-radius: var(--radius-md);
cursor: pointer;
background: none;
border: none;
text-align: left;
}
.fd-dropdown__item:hover { background: var(--color-bg-hover); }
.fd-dropdown__item--danger { color: var(--color-danger); }
.fd-dropdown__sep { height: 1px; background: var(--color-border); margin: 6px; }
/* ── Toasts ───────────────────────────────────────────────── */
.fd-toast {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
border-radius: var(--radius-md);
box-shadow: var(--shadow-toast);
font-size: var(--fs-sm);
font-weight: 500;
animation: fd-toast-in .2s ease;
max-width: 420px;
}
.fd-toast--success { background: var(--color-success); color: #fff; }
.fd-toast--error { background: var(--color-danger); color: #fff; }
.fd-toast--info { background: var(--color-primary); color: #fff; }
@keyframes fd-toast-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ── Cartes & badges ──────────────────────────────────────── */
.fd-card {
background: var(--color-bg);
border: 1px solid var(--color-border-strong);
border-radius: var(--radius-lg);
padding: 14px 16px;
}
.fd-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: var(--radius-pill);
font-size: var(--fs-xs);
font-weight: 500;
}
.fd-badge--success { background: rgba(15,123,108,.16); color: var(--color-success); }
.fd-badge--warn { background: rgba(217,115,13,.16); color: var(--color-warn); }
.fd-badge--danger { background: rgba(224,62,62,.14); color: var(--color-danger); }
.fd-badge--neutral { background: var(--color-bg-tertiary); color: var(--color-text-secondary); }
/* ── Empty states ─────────────────────────────────────────── */
.fd-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 36px 16px;
text-align: center;
color: var(--color-text-dim);
font-size: var(--fs-sm);
}
.fd-empty__icon { font-size: 28px; }
/* ── Tableau ──────────────────────────────────────────────── */
.fd-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.fd-table th {
text-align: left;
font-size: var(--fs-xs);
text-transform: uppercase;
letter-spacing: .5px;
color: var(--color-text-dim);
font-weight: 500;
padding: 8px 10px;
border-bottom: 1px solid var(--color-border);
white-space: nowrap;
}
.fd-table td { padding: 8px 10px; border-bottom: 1px solid var(--color-border); }
.fd-table tbody tr:hover td { background: var(--color-bg-secondary); }
+109
View File
@@ -0,0 +1,109 @@
/* ═══════════════════════════════════════════════════════════
FlowDeck — Design tokens (v5.2.0)
Source unique de vérité pour les couleurs, typographies,
espacements, rayons, ombres, z-index et transitions.
Chargé APRÈS app.css : les valeurs ci-dessous font autorité.
═══════════════════════════════════════════════════════════ */
/* ── Palette (Notion light) ───────────────────────────────── */
:root {
--color-text: #37352F;
--color-text-secondary: #6B6B6B;
--color-text-dim: #9B9A97;
--color-bg: #FFFFFF;
--color-bg-secondary: #FBFBFA;
--color-bg-sidebar: #F7F6F3;
--color-bg-tertiary: #EDEDEC;
--color-bg-hover: #EFEFED;
--color-bg-active: #E8E7E4;
--color-border: #EDEDED;
--color-border-strong: #D3D3D0;
--color-primary: #2383E2;
--color-primary-hover: #1a6bc0;
--color-danger: #E03E3E;
--color-danger-hover: #C93535;
--color-success: #0F7B6C;
--color-warn: #D9730D;
/* Aliases retro-compat (utilisés par app.css) */
--bg-primary: var(--color-bg);
--bg-secondary: var(--color-bg-secondary);
--bg-sidebar: var(--color-bg-sidebar);
--bg-tertiary: var(--color-bg-tertiary);
--bg-hover: var(--color-bg-hover);
--bg-active: var(--color-bg-active);
--text-primary: var(--color-text);
--text-secondary: var(--color-text-secondary);
--text-dim: var(--color-text-dim);
--text: var(--color-text);
--border: var(--color-border);
--border-strong: var(--color-border-strong);
--accent: var(--color-primary);
--accent-hover: var(--color-primary-hover);
--danger: var(--color-danger);
--danger-hover: var(--color-danger-hover);
/* ── Espacements ── */
--spacing-1: 4px;
--spacing-2: 8px;
--spacing-3: 12px;
--spacing-4: 16px;
--spacing-5: 24px;
--spacing-6: 32px;
--spacing-7: 48px;
/* ── Typographie ── */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'SF Mono', 'Fira Code', monospace;
--fs-xs: 12px;
--fs-sm: 14px;
--fs-md: 16px;
--fs-lg: 20px;
--fs-xl: 30px;
--fs-2xl: 40px;
--font-h1: var(--fs-2xl);
--font-h2: var(--fs-xl);
--font-h3: var(--fs-lg);
--font-body: var(--fs-md);
--font-small: var(--fs-sm);
--font-tiny: var(--fs-xs);
/* ── Rayons ── */
--radius-sm: 3px;
--radius-md: 6px;
--radius-lg: 12px;
--radius-pill: 99px;
/* ── Ombres ── */
--shadow-popover: 0 1px 2px rgba(0,0,0,.08), 0 4px 12px rgba(0,0,0,.08);
--shadow-modal: 0 12px 32px rgba(0,0,0,.12);
--shadow-drag: 0 8px 24px rgba(0,0,0,.12);
--shadow-toast: 0 4px 16px rgba(0,0,0,.18);
/* ── Transitions ── */
--transition-fast: .1s ease;
--transition: .15s ease;
--transition-slow: .25s ease;
/* ── Z-index ── */
--z-dropdown: 300;
--z-sticky: 500;
--z-modal: 900;
--z-toast: 1200;
}
/* ── Palette (Notion dark) ────────────────────────────────── */
[data-theme="dark"] {
--color-text: #FFFFFF;
--color-text-secondary: #A0A0A0;
--color-text-dim: #6B6B6B;
--color-bg: #191919;
--color-bg-secondary: #1F1F1F;
--color-bg-sidebar: #202020;
--color-bg-tertiary: #2D2D2D;
--color-bg-hover: #2A2A2A;
--color-bg-active: #2F2E2E;
--color-border: #333333;
--color-border-strong: #4A4A4A;
}