diff --git a/index.html b/index.html index 44a9bd5..f068af8 100644 --- a/index.html +++ b/index.html @@ -73,6 +73,23 @@ .dot-orange { width: 6px; height: 6px; border-radius: 50%; background: #f59e0b; flex-shrink: 0; } .breadcrumb { font-size: 0.75rem; color: #94a3b8; display: flex; gap: 6px; align-items: center; margin-bottom: 2px; } .breadcrumb .current { color: #0f172a; font-weight: 500; } + .os-card { + border-radius: 10px; border: 1px solid #e2e8f0; + background: white; cursor: pointer; + transition: all 0.15s; padding: 16px; + } + .os-card:hover { border-color: #0f172a; box-shadow: 0 2px 12px rgba(0,0,0,0.08); } + .os-card .icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } + .os-card .stat { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em; } + .os-card .sublabel { font-size: 0.6875rem; color: #94a3b8; } + .placeholder-card { opacity: 0.6; } + .placeholder-card:hover { opacity: 0.8; } + .home-btn { + padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; font-weight: 500; + cursor: pointer; transition: all 0.12s; color: #64748b; + display: flex; align-items: center; gap: 5px; + } + .home-btn:hover { background: #f1f5f9; color: #0f172a; } @@ -101,33 +118,74 @@ -
-
-
- -

-

+
+ + +
+
+ +
+ +

+

+
- +
-
+ + -
+ + -
+ + -
+ +
+ + +
+

SysCmd

+

+ Référence de commandes multi-plateforme. Cliquez sur un OS pour voir ses commandes. +

+
+ Données disponibles + Catégorie + À venir +
+
+ + + + + +
+ + +
+
+ +
diff --git a/script.js b/script.js index 9e85510..e279ad7 100644 --- a/script.js +++ b/script.js @@ -1,7 +1,8 @@ -/* SYSCMD — arbre avec sections en sous-niveaux */ +/* SYSCMD — arbre avec sections en sous-niveaux + page d'accueil */ function t(fr, en) { return { fr: fr, en: en }; } let currentNode = null; +let currentSection = null; const TREE = { id: 'root', label: t('OS', 'OS'), @@ -35,26 +36,25 @@ const TREE = { ] }; -// ── SECTIONS ── const SEC = {}; -SEC.sys = { label: t('syst\u00e8me', 'system'), commands: [ +SEC.sys = { label: t('système', 'system'), commands: [ { d: t('Version du noyau', 'Kernel version'), c: 'uname -r' }, { d: t('Version OS', 'OS version'), c: 'lsb_release -a' }, - { d: t('Memoire dispo', 'Memory available'), c: 'free -h' }, - { d: t("Nom d'hote", 'Hostname'), c: 'hostnamectl' }, + { d: t('Mémoire dispo', 'Memory available'), c: 'free -h' }, + { d: t("Nom d'hôte", 'Hostname'), c: 'hostnamectl' }, { d: t('Uptime', 'Uptime'), c: 'uptime' }, { d: t('Infos CPU', 'CPU info'), c: 'lscpu' }, { d: t('Date / heure', 'Date / time'), c: 'timedatectl' }, ]}; SEC.apt = { label: t('APT', 'APT'), commands: [ - { d: t('Synchro depots', 'Sync repos'), c: 'sudo apt update' }, - { d: t('Mise a jour', 'Full upgrade'), c: 'sudo apt full-upgrade -y' }, + { d: t('Synchro dépôts', 'Sync repos'), c: 'sudo apt update' }, + { d: t('Mise à jour', 'Full upgrade'), c: 'sudo apt full-upgrade -y' }, { d: t('Installer', 'Install'), c: 'sudo apt install [package]' }, { d: t('Purge', 'Purge'), c: 'sudo apt purge [package]' }, { d: t('Chercher', 'Search'), c: 'apt search [keyword]' }, - { d: t('Paquets installes', 'Installed'), c: 'apt list --installed' }, + { d: t('Paquets installés', 'Installed'), c: 'apt list --installed' }, { d: t('Fichiers fournis', 'Package files'), c: 'dpkg -L [package]' }, ]}; @@ -75,7 +75,7 @@ SEC.systemd = { label: t('systemd', 'systemd'), commands: [ { d: t('Purger logs', 'Purge logs'), c: 'sudo journalctl --vacuum-time=7d' }, ]}; -SEC['win-sys'] = { label: t('systeme', 'system'), commands: [ +SEC['win-sys'] = { label: t('système', 'system'), commands: [ { d: t('(CMD) Version', '(CMD) Version'), c: 'ver' }, { d: t('(CMD) systeminfo', '(CMD) systeminfo'), c: 'systeminfo' }, { d: t('(PS) Get-ComputerInfo', '(PS) Get-ComputerInfo'), c: 'Get-ComputerInfo' }, @@ -93,7 +93,7 @@ SEC['win-process'] = { label: t('processus', 'processes'), commands: [ { d: t('(PS) Get-Process', '(PS) Get-Process'), c: 'Get-Process' }, ]}; -SEC['win-network'] = { label: t('reseau', 'network'), commands: [ +SEC['win-network'] = { label: t('réseau', 'network'), commands: [ { d: t('(CMD) ipconfig', '(CMD) ipconfig'), c: 'ipconfig /all' }, { d: t('(CMD) netstat', '(CMD) netstat'), c: 'netstat -an | findstr LISTEN' }, { d: t('(PS) Test-NetConnection', '(PS) Test-NetConnection'), c: 'Test-NetConnection [ip] -Port [port]' }, @@ -151,109 +151,140 @@ SEC['solaris-zones'] = { label: t('Zones', 'Zones'), commands: [ { d: t('Console', 'Console'), c: 'sudo zlogin -C [zone]' }, ]}; +// ── COLLECT ALL COMMANDS (for global search) ── +var ALL_COMMANDS = []; +(function() { + var seen = {}; + for (var k in SEC) { + if (SEC.hasOwnProperty(k)) { + SEC[k].commands.forEach(function(cmd) { + var key = cmd.c; + if (!seen[key]) { seen[key] = true; ALL_COMMANDS.push(cmd); } + }); + } + } +})(); + +// ── HELPERS ── +function findNode(tree, id) { + if (tree.id === id) return tree; + for (var i = 0; i < (tree.children || []).length; i++) { + var f = findNode(tree.children[i], id); + if (f) return f; + } + return null; +} + +function collectLeafNodes(node, out) { + if (node.children) { + for (var i = 0; i < node.children.length; i++) collectLeafNodes(node.children[i], out); + } else { + out.push(node); + } + return out; +} + +function countNodeCmds(node) { + if (node.sectionIds) { + var n = 0; + for (var i = 0; i < node.sectionIds.length; i++) { + if (SEC[node.sectionIds[i]]) n += SEC[node.sectionIds[i]].commands.length; + } + return n; + } + return 0; +} + // ── RENDER TREE ── function renderTree() { - const el = document.getElementById('tree'); + var el = document.getElementById('tree'); el.innerHTML = ''; renderNode(el, TREE, 0); } function renderNode(container, node, depth) { - const isBranch = node.children && node.children.length > 0; - const isVersion = !!node.sectionIds; - const isEmpty = !isBranch && !isVersion; - - const div = document.createElement('div'); + var isBranch = node.children && node.children.length > 0; + var isVersion = !!node.sectionIds; + var div = document.createElement('div'); if (depth === 0) { - const label = document.createElement('div'); + var label = document.createElement('div'); label.className = 'group-label'; label.textContent = node.label.fr; div.appendChild(label); } else { - const row = document.createElement('div'); - let cls = 'sidebar-link'; + var row = document.createElement('div'); + var cls = 'sidebar-link'; if (depth === 2) cls += ' sidebar-link-3'; if (depth === 3) cls += ' sidebar-link-4'; if (node === currentNode) cls += ' active'; row.className = cls; - // Toggle or spacer if (isBranch) { - const tgl = document.createElement('span'); + var tgl = document.createElement('span'); tgl.className = 'tree-toggle open'; tgl.innerHTML = ''; row.appendChild(tgl); } else { - const sp = document.createElement('span'); + var sp = document.createElement('span'); sp.style.cssText = 'width:16px;flex-shrink:0;display:inline-block'; row.appendChild(sp); } - // Dot - const dot = document.createElement('span'); - if (isVersion || (node._parent && node._parent.sectionIds)) dot.className = 'dot-green'; + var dot = document.createElement('span'); + if (isVersion || node._parent) dot.className = 'dot-green'; else if (isBranch) dot.className = 'dot-orange'; else dot.className = 'dot-gray'; row.appendChild(dot); - // Label - const lbl = document.createElement('span'); + var lbl = document.createElement('span'); lbl.textContent = node.label.fr; row.appendChild(lbl); - // Count (only for non-section leaves that have data) - let cnt = 0; - if (node.sectionIds) { - cnt = node.sectionIds.reduce(function(a, id) { return a + (SEC[id] ? SEC[id].commands.length : 0); }, 0); - } + var cnt = countNodeCmds(node) || (node._parent && node._parent.sectionIds ? SEC[node._sectionId] ? SEC[node._sectionId].commands.length : 0 : 0); if (cnt > 0) { - const b = document.createElement('span'); + var b = document.createElement('span'); b.className = 'ml-auto text-[0.625rem] text-[#94a3b8]'; b.textContent = '' + cnt; row.appendChild(b); } - // Click row.onclick = function(e) { if (isBranch && (e.target.closest('.tree-toggle') || e.target.closest('[data-toggle]'))) { - const tg = row.querySelector('.tree-toggle'); + var tg = row.querySelector('.tree-toggle'); if (tg) { tg.classList.toggle('open'); row.nextElementSibling.classList.toggle('open'); e.stopPropagation(); } return; } + if (isBranch) return; // only leaf nodes select selectNode(node); }; div.appendChild(row); } - // Enfants if (isBranch) { - const kids = document.createElement('div'); + var kids = document.createElement('div'); kids.className = 'tree-children open'; - for (const ch of node.children) { - ch._parent = node; - renderNode(kids, ch, depth + 1); + for (var i = 0; i < node.children.length; i++) { + node.children[i]._parent = node; + renderNode(kids, node.children[i], depth + 1); } div.appendChild(kids); } - // Si c'est une version avec sectionIds, on ajoute les sections comme enfants if (isVersion && node.sectionIds) { - const kids = document.createElement('div'); + var kids = document.createElement('div'); kids.className = 'tree-children'; - - // On déplie si c'est le noeud actif if (node === currentNode || (currentNode && currentNode._parent === node)) { kids.className = 'tree-children open'; } - - node.sectionIds.forEach(function(sid) { - const sec = SEC[sid]; - if (!sec) return; - const snode = { id: node.id + '-' + sid, label: sec.label, _sectionId: sid, _parent: node }; + for (var j = 0; j < node.sectionIds.length; j++) { + var sid = node.sectionIds[j]; + var sec = SEC[sid]; + if (!sec) continue; + var snode = { id: node.id + '-' + sid, label: sec.label, _sectionId: sid, _parent: node }; renderNode(kids, snode, depth + 1); - }); + } div.appendChild(kids); } @@ -263,85 +294,192 @@ function renderNode(container, node, depth) { // ── SELECT NODE ── function selectNode(node) { currentNode = node; + currentSection = null; renderTree(); - const titleEl = document.getElementById('os-title'); - const subEl = document.getElementById('os-sub'); - const cmdsEl = document.getElementById('cmds'); - const searchEl = document.getElementById('search'); - searchEl.value = ''; + document.getElementById('search').value = ''; + hideHome(); + document.getElementById('home-btn').style.display = 'flex'; + + var titleEl = document.getElementById('os-title'); + var subEl = document.getElementById('os-sub'); + var cmdsEl = document.getElementById('cmds'); - // Cas 1: c'est une section (a _sectionId) if (node._sectionId) { - const sec = SEC[node._sectionId]; + var sec = SEC[node._sectionId]; if (sec) { - titleEl.textContent = sec.label.fr; + titleEl.textContent = node._parent.label.fr + ' > ' + sec.label.fr; subEl.textContent = sec.commands.length + ' commandes'; showCmds(sec.commands); } return; } - // Cas 2: c'est une version ou OS avec sectionIds (on affiche la première section) if (node.sectionIds && node.sectionIds.length > 0) { - const sid = node.sectionIds[0]; - const sec = SEC[sid]; + var sid = node.sectionIds[0]; + var sec = SEC[sid]; if (sec) { titleEl.textContent = node.label.fr; - subEl.textContent = sec.commands.length + ' commandes'; + subEl.textContent = node.sectionIds.length + ' sections · ' + countNodeCmds(node) + ' commandes'; showCmds(sec.commands); } return; } - // Cas 3: pas de données titleEl.textContent = node.label.fr; - subEl.textContent = 'Aucune donnee'; + subEl.textContent = 'Aucune donnée'; cmdsEl.innerHTML = '

' + node.label.fr + '

Pas encore de commandes pour cet OS.

'; } -function showCmds(commands) { - const el = document.getElementById('cmds'); - const sv = document.getElementById('search').value.trim().toLowerCase(); +// ── SHOW/HIDE HOME ── +function showHome() { + currentSection = null; + currentNode = null; + renderTree(); + document.getElementById('search').value = ''; + document.getElementById('filter-info').textContent = 'Aucun filtre'; + document.getElementById('home-content').style.display = 'block'; + document.getElementById('tabs').style.display = 'none'; + document.getElementById('stats-bar').style.display = 'none'; + document.getElementById('cmds-container').style.display = 'none'; + document.getElementById('home-btn').style.display = 'none'; + document.getElementById('os-title').textContent = 'SysCmd'; + document.getElementById('os-sub').textContent = ''; + document.getElementById('breadcrumb').innerHTML = ''; + document.getElementById('footer-hint').textContent = 'Cliquez sur un OS pour explorer ses commandes'; + renderHome(); +} - let cmds = commands; +function hideHome() { + document.getElementById('home-content').style.display = 'none'; + document.getElementById('tabs').style.display = 'flex'; + document.getElementById('stats-bar').style.display = 'flex'; + document.getElementById('cmds-container').style.display = 'block'; + document.getElementById('footer-hint').textContent = 'Cliquez sur une commande pour la copier'; +} + +// ── RENDER HOME ── +function renderHome() { + renderStats(); + renderCards(); +} + +function renderStats() { + var totalCmds = ALL_COMMANDS.length; + var totalSections = 0; + for (var k in SEC) if (SEC.hasOwnProperty(k)) totalSections++; + var totalOS = 0; + var leaves = collectLeafNodes(TREE, []); + for (var i = 0; i < leaves.length; i++) if (leaves[i].sectionIds) totalOS++; + + var el = document.getElementById('stats-row'); + el.innerHTML = + '
' + totalCmds + '
commandes
' + + '
' + totalSections + '
sections
' + + '
' + totalOS + '
OS renseignés
' + + '
' + (leaves.length - totalOS) + '
placeholders
'; +} + +var OS_CARDS = [ + { id: 'debian', label: 'Debian', icon: 'D', color: '#0f172a', desc: t('Distribution stable de référence', 'Stable reference distribution') }, + { id: 'ubuntu', label: 'Ubuntu', icon: 'U', color: '#dc2626', desc: t('Basé sur Debian avec versions LTS', 'Debian-based with LTS releases'), isGroup: true }, + { id: 'oracle', label: 'Oracle Linux', icon: 'O', color: '#b9141e', desc: t('Linux Oracle avec UEK + SELinux', 'Oracle Linux with UEK + SELinux') }, + { id: 'windows', label: 'Windows', icon: 'W', color: '#0078d4', desc: t('CMD + PowerShell client/server', 'CMD + PowerShell client/server'), isGroup: true }, + { id: 'solaris', label: 'Solaris', icon: 'S', color: '#b46400', desc: t('Solaris / illumos avec ZFS + DTrace', 'Solaris / illumos with ZFS + DTrace') }, + { id: 'rhel', label: 'RHEL / CentOS', icon: 'R', color: '#94a3b8', desc: t('Enterprise Linux (à venir)', 'Enterprise Linux (pending)'), placeholder: true }, + { id: 'fedora', label: 'Fedora', icon: 'F', color: '#94a3b8', desc: t('Basé sur RHEL (à venir)', 'RHEL-based (pending)'), placeholder: true }, + { id: 'freebsd', label: 'FreeBSD', icon: 'B', color: '#94a3b8', desc: t('BSD Unix (à venir)', 'BSD Unix (pending)'), placeholder: true }, + { id: 'macos', label: 'macOS', icon: 'M', color: '#94a3b8', desc: t('macOS (à venir)', 'macOS (pending)'), placeholder: true }, + { id: 'docker', label: 'Docker', icon: 'D', color: '#94a3b8', desc: t('Containers et orchestration (à venir)', 'Containers and orchestration (pending)'), placeholder: true }, + { id: 'cisco', label: 'Cisco IOS', icon: 'C', color: '#94a3b8', desc: t('Réseau Cisco (à venir)', 'Cisco networking (pending)'), placeholder: true }, + { id: 'mysql', label: 'MySQL / MariaDB', icon: 'S', color: '#94a3b8', desc: t('Bases de données SQL (à venir)', 'SQL databases (pending)'), placeholder: true }, +]; + +function renderCards() { + var el = document.getElementById('card-grid'); + el.innerHTML = ''; + OS_CARDS.forEach(function(card) { + var div = document.createElement('div'); + var isPlaceholder = card.placeholder; + var node = findNode(TREE, card.id); + var cnt = node ? countNodeCmds(node) : 0; + if (cnt > 0) isPlaceholder = false; + + div.className = 'os-card' + (isPlaceholder ? ' placeholder-card' : ''); + + var iconBg = isPlaceholder ? 'bg-[#e2e8f0] text-[#94a3b8]' : 'text-white'; + div.innerHTML = + '
' + + '
' + + '' + card.icon + '' + + '
' + + '
' + + '
' + (card.isGroup ? card.label + ' →' : card.label) + '
' + + '
' + card.desc.fr + '
' + + '
' + + '
' + + '
' + + (cnt > 0 ? '' + cnt + 'commandes' : '' + (isPlaceholder ? 'À venir' : 'via sidebar') + '') + + '
'; + + div.onclick = function() { + if (card.isGroup) { + // Expand group in sidebar + var n = findNode(TREE, card.id); + if (n && n.children && n.children.length > 0) { + selectNode(n.children[0]); + } + } else if (node) { + selectNode(node); + } + }; + + el.appendChild(div); + }); +} + +// ── SHOW COMMANDS ── +function showCmds(commands) { + var el = document.getElementById('cmds'); + var sv = document.getElementById('search').value.trim().toLowerCase(); + + var cmds = commands; if (sv) { cmds = cmds.filter(function(x) { - return x.d.fr.toLowerCase().includes(sv) || x.d.en.toLowerCase().includes(sv) || x.c.toLowerCase().includes(sv); + return x.d.fr.toLowerCase().indexOf(sv) !== -1 || x.d.en.toLowerCase().indexOf(sv) !== -1 || x.c.toLowerCase().indexOf(sv) !== -1; }); } document.getElementById('shown').textContent = '' + cmds.length; if (cmds.length === 0) { - el.innerHTML = '
Aucune commande trouvee
'; + el.innerHTML = '
Aucune commande trouvée
'; return; } el.innerHTML = ''; - cmds.forEach(function(x, i) { - const row = document.createElement('div'); + for (var i = 0; i < cmds.length; i++) { + var x = cmds[i]; + var row = document.createElement('div'); row.className = 'cmd-row' + (i % 2 === 0 ? ' bg-[#fafbfc]' : ''); row.innerHTML = '
' + x.d.fr + '
' + x.c + '
'; - row.querySelector('.cmd-text').onclick = function() { - navigator.clipboard.writeText(x.c).then(function() { - var icon = row.querySelector('.copy-icon'); - icon.innerHTML = ''; - setTimeout(function() { icon.innerHTML = ''; }, 1200); - }); - }; + row.querySelector('.cmd-text').onclick = (function(cmd) { + return function() { + navigator.clipboard.writeText(cmd).then(function() { + var icon = row.querySelector('.copy-icon'); + icon.innerHTML = ''; + setTimeout(function() { icon.innerHTML = ''; }, 1200); + }); + }; + })(x.c); el.appendChild(row); - }); + } } // ── SEARCH ── function filterCommands(val) { - const el = document.getElementById('filter-info'); - if (val.trim()) { - el.textContent = 'Filtre: "' + val + '"'; - } else { - el.textContent = 'Aucun filtre'; - } + var el = document.getElementById('filter-info'); + el.textContent = val.trim() ? 'Filtre: "' + val + '"' : 'Aucun filtre'; if (currentNode && currentNode._sectionId) { showCmds(SEC[currentNode._sectionId].commands); } else if (currentNode && currentNode.sectionIds && currentNode.sectionIds.length > 0) { @@ -349,15 +487,62 @@ function filterCommands(val) { } } +// ── GLOBAL SEARCH ── +function globalSearch(val) { + var el = document.getElementById('global-search-results'); + var cmdsEl = document.getElementById('global-cmds'); + var sv = val.trim().toLowerCase(); + + if (!sv) { + el.style.display = 'none'; + document.getElementById('card-grid').style.display = 'grid'; + document.getElementById('stats-row').style.display = 'grid'; + return; + } + + el.style.display = 'block'; + document.getElementById('card-grid').style.display = 'none'; + document.getElementById('stats-row').style.display = 'none'; + + // Search across all commands + var results = []; + for (var i = 0; i < ALL_COMMANDS.length; i++) { + var x = ALL_COMMANDS[i]; + if (x.d.fr.toLowerCase().indexOf(sv) !== -1 || x.d.en.toLowerCase().indexOf(sv) !== -1 || x.c.toLowerCase().indexOf(sv) !== -1) { + results.push(x); + } + } + + cmdsEl.innerHTML = ''; + if (results.length === 0) { + cmdsEl.innerHTML = '
Aucune commande trouvée
'; + return; + } + + // Header stat + var header = document.createElement('div'); + header.className = 'px-4 py-2 text-xs text-[#94a3b8] border-b border-[#f1f5f9]'; + header.textContent = results.length + ' résultat(s) dans toutes les sections'; + cmdsEl.appendChild(header); + + for (var j = 0; j < results.length; j++) { + var x = results[j]; + var row = document.createElement('div'); + row.className = 'cmd-row' + (j % 2 === 0 ? ' bg-[#fafbfc]' : ''); + row.innerHTML = '
' + x.d.fr + '
' + x.c + '
'; + row.querySelector('.cmd-text').onclick = (function(cmd) { + return function() { + navigator.clipboard.writeText(cmd).then(function() { + var icon = row.querySelector('.copy-icon'); + icon.innerHTML = ''; + setTimeout(function() { icon.innerHTML = ''; }, 1200); + }); + }; + })(x.c); + cmdsEl.appendChild(row); + } +} + // ── INIT ── renderTree(); -selectNode(findNode(TREE, 'ubuntu-2004')); - -function findNode(tree, id) { - if (tree.id === id) return tree; - for (const c of (tree.children || [])) { - const f = findNode(c, id); - if (f) return f; - } - return null; -} +showHome();