feat(ui): aggiunge guida utente e menu Help > Documentazione
This commit is contained in:
23
main.js
23
main.js
@@ -1,4 +1,4 @@
|
||||
const { app, BrowserWindow, dialog, ipcMain, shell } = require('electron');
|
||||
const { app, BrowserWindow, dialog, ipcMain, shell, Menu } = require('electron');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
} = require('./services/unrouted');
|
||||
|
||||
const CAD_EXTENSIONS = ['prt', 'asm', 'dwr'];
|
||||
const DEFAULT_DESTINATION = './output/cad';
|
||||
const DEFAULT_DESTINATION = 'X:\\';
|
||||
const SETTINGS_FILENAME = 'cad-router-settings.json';
|
||||
const LINUX_RUNTIME_DIR = '.cadroute';
|
||||
|
||||
@@ -79,6 +79,25 @@ function createWindow() {
|
||||
win.loadFile(path.join(__dirname, 'renderer', 'index.html'));
|
||||
}
|
||||
|
||||
function createDocsWindow() {
|
||||
const docs = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 700,
|
||||
title: 'CadRoute — Documentazione',
|
||||
icon: path.join(__dirname, 'build', 'icon.png'),
|
||||
webPreferences: { contextIsolation: true, nodeIntegration: false },
|
||||
});
|
||||
docs.loadFile(path.join(__dirname, 'renderer', 'docs', 'index.html'));
|
||||
docs.setMenuBarVisibility(false);
|
||||
}
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Help',
|
||||
submenu: [{ label: 'Documentazione', click: () => createDocsWindow() }],
|
||||
},
|
||||
]));
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
await ensureRuntimeDirectory();
|
||||
const persistedDestination = await loadPersistedDestination();
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"preload.js",
|
||||
"renderer/**/*",
|
||||
"services/**/*",
|
||||
"build/icon.png"
|
||||
"build/icon.png",
|
||||
"renderer/docs/**/*"
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis",
|
||||
|
||||
277
renderer/docs/index.html
Normal file
277
renderer/docs/index.html
Normal file
@@ -0,0 +1,277 @@
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CadRoute — Documentazione</title>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<div class="nav-brand">
|
||||
<div class="logo">CadRoute</div>
|
||||
<div class="version">Guida utente</div>
|
||||
</div>
|
||||
|
||||
<span class="nav-section-label">Introduzione</span>
|
||||
<a href="#intro">Cos'è CadRoute</a>
|
||||
<a href="#requisiti">Requisiti</a>
|
||||
|
||||
<span class="nav-section-label">Utilizzo</span>
|
||||
<a href="#destinazione">Configurare la destinazione</a>
|
||||
<a href="#cartella">Smistare i file</a>
|
||||
<a href="#risultati">Leggere i risultati</a>
|
||||
|
||||
<span class="nav-section-label">Approfondimenti</span>
|
||||
<a href="#nomi">Convenzione nomi file</a>
|
||||
<a href="#nonsmistati">File non smistati</a>
|
||||
<a href="#duplicati">File duplicati</a>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
|
||||
<!-- ── INTRODUZIONE ──────────────────────── -->
|
||||
<section id="intro">
|
||||
<h1>CadRoute — Guida utente</h1>
|
||||
<div class="intro-card">
|
||||
<p><strong>CadRoute</strong> automatizza lo smistamento di file CAD provenienti da Creo. Analizza una cartella o un archivio ZIP, riconosce i file CAD e li copia automaticamente nella sottocartella di destinazione corretta, basandosi sulla struttura numerica del nome file.</p>
|
||||
</div>
|
||||
|
||||
<h3>Formati supportati</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Estensione</th><th>Tipo</th><th>Descrizione</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="badge prt">.prt</span></td>
|
||||
<td>Part</td>
|
||||
<td>File di componente singolo Creo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge asm">.asm</span></td>
|
||||
<td>Assembly</td>
|
||||
<td>File di assemblaggio Creo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge dwr">.dwr</span></td>
|
||||
<td>Drawing</td>
|
||||
<td>File di disegno Creo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="badge skip">altri</span></td>
|
||||
<td>—</td>
|
||||
<td>Ignorati (PDF, immagini, documenti, ecc.)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- ── REQUISITI ────────────────────────── -->
|
||||
<section id="requisiti">
|
||||
<h2>Requisiti</h2>
|
||||
<p>Prima di iniziare, assicurati che la <strong>cartella di destinazione</strong> sia quella corretta.</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- ── CONFIGURARE DESTINAZIONE ─────────── -->
|
||||
<section id="destinazione">
|
||||
<h2>Configurare la destinazione</h2>
|
||||
<p>La <strong>destinazione</strong> è la cartella radice dove CadRoute copierà i file CAD smistati. Va impostata una volta sola e viene ricordata tra le sessioni.</p>
|
||||
|
||||
<ol class="steps">
|
||||
<li><span>Individua la sezione <strong>Destinazione file CAD</strong> nella finestra principale.</span></li>
|
||||
<li><span>Clicca <strong>Sfoglia</strong> per aprire il selettore cartelle, oppure digita il percorso direttamente nel campo di testo.</span></li>
|
||||
<li><span>Clicca <strong>Salva</strong>. La destinazione viene memorizzata e sarà attiva anche alla prossima apertura dell'app.</span></li>
|
||||
</ol>
|
||||
|
||||
<div class="callout tip">
|
||||
<strong>Tip:</strong> il percorso di default è <code>X:\</code>. Assicurati che sia quello corretto prima di avviare lo smistamento.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── SMISTARE FILE ──────────────────────── -->
|
||||
<section id="cartella">
|
||||
<h2>Smistare i file</h2>
|
||||
<p>È possibile smistare una <strong>cartella</strong> (anche con sottocartelle annidate) o un archivio <strong>.zip</strong>, tramite i pulsanti o trascinando direttamente nell'area apposita.</p>
|
||||
|
||||
<ol class="steps">
|
||||
<li><span>Clicca <strong>Process Folder</strong> per selezionare una cartella, oppure <strong>Process ZIP</strong> per selezionare un archivio. In alternativa, trascina la cartella o il file <code>.zip</code> nell'area tratteggiata.</span></li>
|
||||
<li><span>L'app scansiona tutti i file e identifica automaticamente quelli CAD.</span></li>
|
||||
<li><span>I file CAD vengono copiati nella sottocartella di destinazione corrispondente. I file non CAD vengono saltati.</span></li>
|
||||
<li><span>Al termine vengono mostrate le statistiche nell'area di output.</span></li>
|
||||
</ol>
|
||||
|
||||
<div class="callout info">
|
||||
<strong>Nota:</strong> i file originali non vengono mai modificati né cancellati.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- ── RISULTATI ─────────────────────────── -->
|
||||
<section id="risultati">
|
||||
<h2>Leggere i risultati</h2>
|
||||
<p>Al termine di ogni operazione l'area di output mostra un riepilogo. Ecco il significato di ogni voce.</p>
|
||||
|
||||
<div class="stat-grid">
|
||||
<div class="stat-item info">
|
||||
<div class="stat-label">Scansionati</div>
|
||||
<div class="stat-desc">Totale file analizzati (CAD e non).</div>
|
||||
</div>
|
||||
<div class="stat-item ok">
|
||||
<div class="stat-label">Copiati</div>
|
||||
<div class="stat-desc">File effettivamente scritti su disco (destinazione, non smistati o duplicati).</div>
|
||||
</div>
|
||||
<div class="stat-item muted">
|
||||
<div class="stat-label">Saltati</div>
|
||||
<div class="stat-desc">File ignorati perché non CAD (PDF, immagini, ecc.).</div>
|
||||
</div>
|
||||
<div class="stat-item warn">
|
||||
<div class="stat-label">Non smistati</div>
|
||||
<div class="stat-desc">File CAD per cui non è stata trovata una destinazione valida.</div>
|
||||
</div>
|
||||
<div class="stat-item warn">
|
||||
<div class="stat-label">Duplicati</div>
|
||||
<div class="stat-desc">File CAD già presenti nella destinazione al momento dello smistamento.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Sotto le statistiche sono elencati i dettagli di ogni file (massimo 20 voci) con il percorso di destinazione e la motivazione, nel caso di file non smistati o duplicati.</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- ── NOMI FILE ─────────────────────────── -->
|
||||
<section id="nomi">
|
||||
<h2>Convenzione nomi file CAD</h2>
|
||||
<p>CadRoute determina automaticamente la sottocartella di destinazione leggendo il nome del file. È fondamentale che i file seguano la convenzione corretta.</p>
|
||||
|
||||
<h3>Struttura del nome</h3>
|
||||
<pre class="code-block"><code>CODICE.tipo[.versione]
|
||||
|
||||
Esempi:
|
||||
ABC12345.prt → Part, nessuna versione
|
||||
ABC12345.prt.6 → Part, versione 6
|
||||
XYZ67890.asm.10 → Assembly, versione 10
|
||||
DEF11111.drw.15 → Drawing, versione 15</code></pre>
|
||||
|
||||
<h3>Gestione versioni</h3>
|
||||
<p>Se sono presenti più versioni dello stesso file nella sorgente (es. <code>ABC12345.prt.8</code> e <code>ABC12345.prt.9</code>), CadRoute mantiene <strong>solo la versione più alta</strong> e scarta le inferiori.</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Scenario</th><th>Comportamento</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>File non presente in destinazione</td>
|
||||
<td>Copia diretta nella sottocartella corretta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File già presente in destinazione</td>
|
||||
<td>Copiato in <code>duplicati/</code> per revisione</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Versione inferiore presente nella sorgente</td>
|
||||
<td>Saltata — la versione più alta prende precedenza</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<!-- ── NON SMISTATI ──────────────────────── -->
|
||||
<section id="nonsmistati">
|
||||
<h2>File non smistati</h2>
|
||||
<p>Un file CAD finisce in <strong>non smistati</strong> quando CadRoute non riesce a determinare una destinazione valida. Le cause più comuni sono:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Causa</th><th>Messaggio</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Codice con meno di 5 cifre</td>
|
||||
<td><em>"Nome file non conforme: servono almeno 5 cifre…"</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sottocartella non trovata in destinazione</td>
|
||||
<td><em>"Sottocartella XXX non trovata nella destinazione"</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sottocartella trovata in più percorsi (ambiguo)</td>
|
||||
<td><em>"Sottocartella XXX trovata in N percorsi diversi"</em></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Dove si trovano</h3>
|
||||
<p>I file non smistati vengono copiati nella cartella <code>__NON_SMISTATI</code>:</p>
|
||||
<ul style="margin: 8px 0 12px 20px; color: var(--muted);">
|
||||
<li><strong>Linux:</strong> <code>~/.cadroute/__NON_SMISTATI/</code></li>
|
||||
<li><strong>Windows:</strong> <code>%APPDATA%\CadRoute\__NON_SMISTATI\</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Visualizzare e pulire</h3>
|
||||
<ol class="steps">
|
||||
<li><span>Clicca <strong>Anteprima non smistati</strong> per aprire la lista dei file presenti.</span></li>
|
||||
<li><span>Verifica i file, controlla il motivo dell'esclusione nell'area di output.</span></li>
|
||||
<li><span>Se vuoi svuotare la cartella, clicca <strong>Pulisci cartella</strong> (rosso) e conferma. L'operazione è irreversibile.</span></li>
|
||||
</ol>
|
||||
|
||||
<div class="callout tip">
|
||||
<strong>Suggerimento:</strong> prima di pulire, sposta manualmente i file che vuoi conservare. La pulizia elimina tutto il contenuto della cartella.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── DUPLICATI ─────────────────────────── -->
|
||||
<section id="duplicati">
|
||||
<h2>File duplicati</h2>
|
||||
<p>Un file CAD viene classificato come <strong>duplicato</strong> quando la sua chiave (<code>CODICE.tipo</code>, senza versione) è già presente nella cartella di destinazione al momento dello smistamento.</p>
|
||||
|
||||
<p>I duplicati non sovrascrivono i file già presenti in destinazione: vengono messi da parte nella cartella <code>duplicati/</code> per permettere una revisione manuale.</p>
|
||||
|
||||
<h3>Dove si trovano</h3>
|
||||
<ul style="margin: 8px 0 12px 20px; color: var(--muted);">
|
||||
<li><strong>Linux:</strong> <code>~/.cadroute/duplicati/</code></li>
|
||||
<li><strong>Windows:</strong> <code>%APPDATA%\CadRoute\duplicati\</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Gestione versioni nei duplicati</h3>
|
||||
<p>Anche all'interno della cartella <code>duplicati/</code> viene applicata la logica delle versioni: se arriva una versione più alta di un file già presente nei duplicati, la versione vecchia viene eliminata e sostituita con quella nuova.</p>
|
||||
|
||||
<h3>Visualizzare e pulire</h3>
|
||||
<ol class="steps">
|
||||
<li><span>Clicca <strong>Anteprima duplicati</strong> per aprire la lista dei file presenti.</span></li>
|
||||
<li><span>Verifica se i duplicati sono effettivamente superati o se richiedono un intervento manuale.</span></li>
|
||||
<li><span>Se vuoi svuotare la cartella, clicca <strong>Pulisci cartella</strong> (rosso) e conferma.</span></li>
|
||||
</ol>
|
||||
|
||||
<div class="callout warning">
|
||||
<strong>Attenzione:</strong> la pulizia è definitiva. Assicurati di non aver bisogno dei file prima di procedere.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Evidenzia il link attivo nella sidebar durante lo scroll
|
||||
const sections = document.querySelectorAll('section[id]');
|
||||
const links = document.querySelectorAll('nav a[href^="#"]');
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
links.forEach(l => l.classList.remove('active'));
|
||||
const active = document.querySelector(`nav a[href="#${entry.target.id}"]`);
|
||||
if (active) active.classList.add('active');
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '-20% 0px -70% 0px' });
|
||||
|
||||
sections.forEach(s => observer.observe(s));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
335
renderer/docs/style.css
Normal file
335
renderer/docs/style.css
Normal file
@@ -0,0 +1,335 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--bg: #f6f8fb;
|
||||
--card: #ffffff;
|
||||
--text: #0f172a;
|
||||
--muted: #475569;
|
||||
--accent: #0b5fff;
|
||||
--accent-light: #eef3ff;
|
||||
--border: #dbe2ea;
|
||||
--sidebar-w: 220px;
|
||||
--code-bg: #f1f5f9;
|
||||
--success: #15803d;
|
||||
--warning: #b45309;
|
||||
--danger: #b91c1c;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── SIDEBAR ─────────────────────────────────── */
|
||||
nav {
|
||||
width: var(--sidebar-w);
|
||||
min-width: var(--sidebar-w);
|
||||
background: var(--card);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 24px 0 24px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav-brand {
|
||||
padding: 0 18px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.nav-brand .logo {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
letter-spacing: -0.3px;
|
||||
}
|
||||
|
||||
.nav-brand .version {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
padding: 7px 18px;
|
||||
text-decoration: none;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
border-left: 3px solid transparent;
|
||||
transition: color 100ms, border-color 100ms, background 100ms;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: var(--text);
|
||||
background: var(--accent-light);
|
||||
}
|
||||
|
||||
nav a.active {
|
||||
color: var(--accent);
|
||||
border-left-color: var(--accent);
|
||||
background: var(--accent-light);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.nav-section-label {
|
||||
padding: 14px 18px 4px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.8px;
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* ── MAIN CONTENT ────────────────────────────── */
|
||||
main {
|
||||
margin-left: var(--sidebar-w);
|
||||
flex: 1;
|
||||
padding: 40px 48px 80px;
|
||||
max-width: 860px;
|
||||
}
|
||||
|
||||
/* ── TYPOGRAPHY ──────────────────────────────── */
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 19px;
|
||||
font-weight: 700;
|
||||
margin: 48px 0 14px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
scroll-margin-top: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
margin: 22px 0 8px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--muted);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
p:last-child { margin-bottom: 0; }
|
||||
|
||||
/* ── INTRO CARD ──────────────────────────────── */
|
||||
.intro-card {
|
||||
background: var(--accent-light);
|
||||
border: 1px solid #c7d9ff;
|
||||
border-radius: 12px;
|
||||
padding: 18px 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.intro-card p {
|
||||
color: #1e3a8a;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── STEP LIST ───────────────────────────────── */
|
||||
.steps {
|
||||
list-style: none;
|
||||
counter-reset: step;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.steps li {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: flex-start;
|
||||
counter-increment: step;
|
||||
}
|
||||
|
||||
.steps li::before {
|
||||
content: counter(step);
|
||||
min-width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.steps li span {
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ── CALLOUT ─────────────────────────────────── */
|
||||
.callout {
|
||||
border-radius: 10px;
|
||||
padding: 13px 16px;
|
||||
margin: 16px 0;
|
||||
font-size: 13px;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.callout.info {
|
||||
background: #f0f9ff;
|
||||
border-color: #38bdf8;
|
||||
color: #075985;
|
||||
}
|
||||
|
||||
.callout.warning {
|
||||
background: #fffbeb;
|
||||
border-color: #f59e0b;
|
||||
color: #78350f;
|
||||
}
|
||||
|
||||
.callout.tip {
|
||||
background: #f0fdf4;
|
||||
border-color: #22c55e;
|
||||
color: #14532d;
|
||||
}
|
||||
|
||||
.callout strong { font-weight: 700; }
|
||||
|
||||
/* ── CODE & INLINE CODE ──────────────────────── */
|
||||
code {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 1px 5px;
|
||||
font-family: 'Cascadia Code', 'Consolas', monospace;
|
||||
font-size: 12.5px;
|
||||
color: #be185d;
|
||||
}
|
||||
|
||||
pre.code-block {
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
margin: 12px 0;
|
||||
overflow-x: auto;
|
||||
font-family: 'Cascadia Code', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
pre.code-block code {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* ── BADGE ───────────────────────────────────── */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2px;
|
||||
vertical-align: middle;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.badge.prt { background: #dbeafe; color: #1e40af; }
|
||||
.badge.asm { background: #dcfce7; color: #166534; }
|
||||
.badge.dwr { background: #fef3c7; color: #92400e; }
|
||||
.badge.skip { background: #f1f5f9; color: #475569; }
|
||||
|
||||
/* ── TABLE ───────────────────────────────────── */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 14px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 9px 12px;
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr:nth-child(even) td { background: #fafbfc; }
|
||||
|
||||
/* ── STAT GRID ───────────────────────────────── */
|
||||
.stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: 10px;
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.stat-item .stat-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-item .stat-desc {
|
||||
font-size: 13px;
|
||||
color: var(--text);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.stat-item.ok { border-top: 3px solid #22c55e; }
|
||||
.stat-item.info { border-top: 3px solid var(--accent); }
|
||||
.stat-item.warn { border-top: 3px solid #f59e0b; }
|
||||
.stat-item.muted { border-top: 3px solid #94a3b8; }
|
||||
|
||||
/* ── SEPARATOR ───────────────────────────────── */
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--border);
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
/* ── SECTION ─────────────────────────────────── */
|
||||
section {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
Reference in New Issue
Block a user