- 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]>
221 lines
7.3 KiB
CSS
221 lines
7.3 KiB
CSS
/* ═══════════════════════════════════════════════════════════
|
|
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); } |