02-software: add Atera RMM agent install (Invoke-WebRequest + msiexec /qn), stop UCPD driver before PDF association write, restart after; remove reference to PDF-DefaultApp scheduled task in header 03-system-registry: correct OneDrive uninstall description - intentional (pre-installed consumer version only, no policy key, M365 can reinstall) 04-default-profile: OneDrive RunOnce blocking removed, ShowRecent=0, ShowFrequent=0, FullPath=1 in CabinetState already added in prior session 06-scheduled-tasks: PDF-DefaultApp task removed - PDF set once in step 02 08-activation: add OA3/BIOS embedded key check via SoftwareLicensingService WMI; key priority: config.json > OA3 firmware > GVLK web/spec: update all status badges, remove mustfix flags, deduplicate OneDrive entries across steps 01/03/04, add OA3 row to step-08 web/data/descriptions.json: regenerated (65 items) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1213 lines
49 KiB
HTML
1213 lines
49 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="cs">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Specifikace & anotace – xetup</title>
|
||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||
<style>
|
||
:root {
|
||
--bg: #0f1117;
|
||
--card: #1a1d27;
|
||
--card2: #141720;
|
||
--border: #2a2d3a;
|
||
--text: #e0e0e0;
|
||
--muted: #888;
|
||
--accent: #223B47;
|
||
--accent-bright: #2d5266;
|
||
--green: #2ea043;
|
||
--blue: #58a6ff;
|
||
--yellow: #d29922;
|
||
--red: #da3633;
|
||
--purple: #8b949e;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ---- HEADER ---- */
|
||
header {
|
||
border-bottom: 1px solid var(--border);
|
||
padding: 1rem 2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .75rem;
|
||
position: sticky;
|
||
top: 0;
|
||
background: var(--bg);
|
||
z-index: 100;
|
||
}
|
||
.logo-text { font-size: 1.1rem; font-weight: 700; color: #fff; letter-spacing: -.02em; }
|
||
.logo-sub { font-size: .8rem; color: var(--muted); margin-left: .2rem; }
|
||
header nav { margin-left: auto; display: flex; gap: 1.5rem; }
|
||
header nav a { color: var(--muted); text-decoration: none; font-size: .85rem; transition: color .15s; }
|
||
header nav a:hover, header nav a.active { color: var(--text); }
|
||
|
||
/* ---- LAYOUT ---- */
|
||
.layout {
|
||
display: grid;
|
||
grid-template-columns: 220px 1fr;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 2rem 1.5rem;
|
||
gap: 2rem;
|
||
align-items: start;
|
||
}
|
||
|
||
/* ---- SIDEBAR ---- */
|
||
.sidebar {
|
||
position: sticky;
|
||
top: 57px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
background: var(--card);
|
||
padding: 1rem;
|
||
}
|
||
.sidebar h4 {
|
||
font-size: .72rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: .08em;
|
||
color: var(--muted);
|
||
margin-bottom: .75rem;
|
||
}
|
||
.sidebar a {
|
||
display: block;
|
||
color: var(--muted);
|
||
text-decoration: none;
|
||
font-size: .83rem;
|
||
padding: .3rem .4rem;
|
||
border-radius: 5px;
|
||
transition: background .1s, color .1s;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.sidebar a:hover { background: var(--card2); color: var(--text); }
|
||
.sidebar-divider { border: none; border-top: 1px solid var(--border); margin: .6rem 0; }
|
||
|
||
/* ---- CONTENT ---- */
|
||
.content h1 {
|
||
font-size: 1.7rem;
|
||
font-weight: 800;
|
||
letter-spacing: -.03em;
|
||
color: #fff;
|
||
margin-bottom: .4rem;
|
||
}
|
||
.meta {
|
||
font-size: .82rem;
|
||
color: var(--muted);
|
||
margin-bottom: 2rem;
|
||
display: flex;
|
||
gap: 1.2rem;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
}
|
||
.meta a { color: var(--muted); text-decoration: none; }
|
||
.meta a:hover { color: var(--blue); }
|
||
|
||
/* ---- LEGEND ---- */
|
||
.legend {
|
||
display: flex;
|
||
gap: .6rem;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 2rem;
|
||
padding: .8rem 1rem;
|
||
background: var(--card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
font-size: .8rem;
|
||
}
|
||
.legend span { display: flex; align-items: center; gap: .3rem; color: var(--muted); }
|
||
|
||
/* ---- STEP CARD ---- */
|
||
.step {
|
||
background: var(--card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
margin-bottom: 1.2rem;
|
||
overflow: hidden;
|
||
}
|
||
.step-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .75rem;
|
||
padding: .9rem 1.2rem;
|
||
border-bottom: 1px solid var(--border);
|
||
background: var(--card2);
|
||
}
|
||
.step-num {
|
||
font-size: .72rem;
|
||
font-weight: 700;
|
||
color: var(--muted);
|
||
font-family: monospace;
|
||
min-width: 30px;
|
||
}
|
||
.step-title {
|
||
font-size: .95rem;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
flex: 1;
|
||
}
|
||
.step-body { padding: 1rem 1.2rem; }
|
||
|
||
/* ---- ITEM ROWS ---- */
|
||
.items { width: 100%; border-collapse: collapse; font-size: .83rem; }
|
||
.items td {
|
||
padding: .45rem .6rem;
|
||
border-bottom: 1px solid var(--border);
|
||
vertical-align: top;
|
||
line-height: 1.45;
|
||
}
|
||
.items tr:last-child td { border-bottom: none; }
|
||
.items td:first-child { color: var(--text); width: 55%; }
|
||
.items td:last-child { color: var(--muted); width: 45%; font-size: .78rem; }
|
||
.items tr.flag-mustfix td:first-child { color: #f0853a; }
|
||
.items tr.flag-done td:first-child { color: var(--green); }
|
||
.items tr.flag-todo td:first-child { color: var(--yellow); }
|
||
.items tr.flag-open td:first-child { color: var(--blue); }
|
||
.items tr.flag-warn td:first-child { color: #da3633; }
|
||
|
||
/* ---- BADGES ---- */
|
||
.badge {
|
||
display: inline-block;
|
||
border-radius: 4px;
|
||
padding: .15rem .4rem;
|
||
font-size: .68rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: .04em;
|
||
white-space: nowrap;
|
||
margin-left: .3rem;
|
||
vertical-align: middle;
|
||
}
|
||
.badge-ok { background: #1a2e1a; color: var(--green); border: 1px solid var(--green); }
|
||
.badge-mustfix { background: #2e1c10; color: #f0853a; border: 1px solid #f0853a; }
|
||
.badge-todo { background: #2e250a; color: var(--yellow); border: 1px solid var(--yellow); }
|
||
.badge-open { background: #0d1e30; color: var(--blue); border: 1px solid var(--blue); }
|
||
.badge-warn { background: #2e0d0c; color: #da3633; border: 1px solid #da3633; }
|
||
.badge-future { background: #1e1e2e; color: var(--purple); border: 1px solid var(--purple); }
|
||
.badge-new { background: #0d1e30; color: #79c0ff; border: 1px solid #79c0ff; }
|
||
|
||
/* ---- ROW EXPAND ---- */
|
||
.items td.xp { width: 54px; padding: .25rem .3rem; text-align: right; vertical-align: middle; }
|
||
.xp-btn {
|
||
background: rgba(88,166,255,.08);
|
||
border: 1px solid rgba(88,166,255,.25);
|
||
color: var(--blue);
|
||
cursor: pointer; font-size: .7rem; padding: .15rem .4rem;
|
||
border-radius: 4px; line-height: 1.4; transition: background .1s, border-color .1s;
|
||
white-space: nowrap;
|
||
}
|
||
.xp-btn:hover { background: rgba(88,166,255,.18); border-color: var(--blue); }
|
||
.xp-btn.open { background: rgba(88,166,255,.2); border-color: var(--blue); }
|
||
.items tr.detail-tr td {
|
||
background: rgba(88,166,255,.04);
|
||
border-bottom: 1px solid var(--border);
|
||
padding: .8rem 1rem .8rem 1.2rem;
|
||
font-size: .82rem;
|
||
color: var(--muted);
|
||
}
|
||
.detail-heading {
|
||
font-size: .7rem; text-transform: uppercase; letter-spacing: .08em;
|
||
color: var(--muted); margin-bottom: .4rem;
|
||
}
|
||
.detail-desc-script {
|
||
color: var(--text); line-height: 1.65; font-size: .85rem;
|
||
margin-bottom: .6rem; white-space: pre-wrap; word-break: break-word;
|
||
}
|
||
.detail-desc-script--empty { color: var(--muted); font-style: italic; }
|
||
.detail-note {
|
||
margin-top: .3rem; margin-bottom: .5rem; color: var(--muted);
|
||
font-size: .78rem; font-style: italic; line-height: 1.45;
|
||
}
|
||
.detail-divider {
|
||
border: none; border-top: 1px solid var(--border); margin: .65rem 0;
|
||
}
|
||
/* row comment thread */
|
||
.rcl { margin-bottom: .4rem; }
|
||
.rci { padding: .3rem 0; border-bottom: 1px dashed rgba(255,255,255,.06); font-size: .8rem; }
|
||
.rci:last-child { border-bottom: none; }
|
||
.rci-meta { font-size: .7rem; color: var(--muted); margin-bottom: .1rem; }
|
||
.rci-body { color: var(--text); white-space: pre-wrap; word-break: break-word; }
|
||
.rcf { display: flex; gap: .35rem; align-items: flex-start; margin-top: .5rem; flex-wrap: wrap; }
|
||
.rcf-name {
|
||
width: 110px; background: var(--bg); border: 1px solid var(--border);
|
||
border-radius: 5px; color: var(--text); font-size: .78rem;
|
||
padding: .28rem .45rem; font-family: inherit;
|
||
}
|
||
.rcf-text {
|
||
flex: 1; min-width: 140px; background: var(--bg); border: 1px solid var(--border);
|
||
border-radius: 5px; color: var(--text); font-size: .8rem;
|
||
padding: .28rem .45rem; font-family: inherit; resize: none; min-height: 36px;
|
||
}
|
||
.rcf-name:focus, .rcf-text:focus { outline: none; border-color: var(--accent-bright); }
|
||
.rcf-send {
|
||
background: var(--accent-bright); color: #fff; border: none;
|
||
border-radius: 5px; padding: .28rem .6rem; font-size: .78rem;
|
||
cursor: pointer; white-space: nowrap; line-height: 1.4;
|
||
}
|
||
.rcf-send:hover { opacity: .85; }
|
||
.rcf-send:disabled { opacity: .35; cursor: default; }
|
||
|
||
/* ---- NOTES ---- */
|
||
.note {
|
||
margin-top: .75rem;
|
||
padding: .6rem .8rem;
|
||
background: rgba(88,166,255,.06);
|
||
border-left: 3px solid var(--accent-bright);
|
||
border-radius: 0 5px 5px 0;
|
||
font-size: .8rem;
|
||
color: var(--muted);
|
||
line-height: 1.5;
|
||
}
|
||
.note strong { color: var(--text); }
|
||
.note code {
|
||
font-family: monospace;
|
||
font-size: .78rem;
|
||
background: rgba(255,255,255,.07);
|
||
padding: .1rem .3rem;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
/* ---- STEP FOOTER ---- */
|
||
.step-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: .6rem 1.2rem;
|
||
border-top: 1px solid var(--border);
|
||
background: var(--card2);
|
||
gap: .5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.step-footer code {
|
||
font-family: monospace;
|
||
font-size: .78rem;
|
||
background: rgba(255,255,255,.07);
|
||
padding: .1rem .3rem;
|
||
border-radius: 3px;
|
||
color: var(--muted);
|
||
}
|
||
.step-status { font-size: .78rem; color: var(--muted); }
|
||
|
||
/* ---- SECTION HEADING ---- */
|
||
.section-label {
|
||
font-size: .72rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: .1em;
|
||
color: var(--muted);
|
||
margin: 1.8rem 0 .7rem;
|
||
padding-bottom: .3rem;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.section-label:first-child { margin-top: 0; }
|
||
|
||
/* ---- INLINE CODE ---- */
|
||
code {
|
||
font-family: monospace;
|
||
font-size: .82em;
|
||
background: rgba(255,255,255,.08);
|
||
padding: .1rem .3rem;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
/* ---- COMMENT WIDGET ---- */
|
||
.comment-widget { width: 100%; }
|
||
.comment-toggle {
|
||
background: none;
|
||
border: 1px dashed var(--border);
|
||
border-radius: 5px;
|
||
color: var(--muted);
|
||
font-size: .8rem;
|
||
padding: .25rem .7rem;
|
||
cursor: pointer;
|
||
transition: border-color .15s, color .15s;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .4rem;
|
||
}
|
||
.comment-toggle:hover { border-color: var(--blue); color: var(--blue); }
|
||
.comment-toggle .cnt {
|
||
background: var(--accent-bright);
|
||
color: #fff;
|
||
border-radius: 10px;
|
||
font-size: .7rem;
|
||
padding: .05rem .35rem;
|
||
display: none;
|
||
}
|
||
.comment-toggle .cnt.visible { display: inline; }
|
||
.comment-panel {
|
||
display: none;
|
||
margin-top: .5rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
.comment-panel.open { display: block; }
|
||
.comment-list { padding: .5rem; }
|
||
.comment-item {
|
||
padding: .5rem .6rem;
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: .82rem;
|
||
line-height: 1.5;
|
||
}
|
||
.comment-item:last-child { border-bottom: none; }
|
||
.comment-meta {
|
||
font-size: .72rem;
|
||
color: var(--muted);
|
||
margin-bottom: .25rem;
|
||
}
|
||
.comment-body { color: var(--text); white-space: pre-wrap; word-break: break-word; }
|
||
.comment-empty {
|
||
padding: .6rem;
|
||
font-size: .8rem;
|
||
color: var(--muted);
|
||
text-align: center;
|
||
font-style: italic;
|
||
}
|
||
.comment-form {
|
||
border-top: 1px solid var(--border);
|
||
padding: .6rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .4rem;
|
||
}
|
||
.comment-input-name {
|
||
width: 100%;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 5px;
|
||
color: var(--text);
|
||
font-size: .8rem;
|
||
padding: .35rem .5rem;
|
||
resize: none;
|
||
font-family: inherit;
|
||
}
|
||
.comment-input {
|
||
width: 100%;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 5px;
|
||
color: var(--text);
|
||
font-size: .82rem;
|
||
padding: .4rem .5rem;
|
||
resize: vertical;
|
||
min-height: 60px;
|
||
font-family: inherit;
|
||
line-height: 1.4;
|
||
}
|
||
.comment-input:focus, .comment-input-name:focus {
|
||
outline: none;
|
||
border-color: var(--accent-bright);
|
||
}
|
||
.comment-submit {
|
||
align-self: flex-end;
|
||
background: var(--accent-bright);
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 5px;
|
||
padding: .3rem .8rem;
|
||
font-size: .8rem;
|
||
cursor: pointer;
|
||
transition: opacity .15s;
|
||
}
|
||
.comment-submit:hover { opacity: .85; }
|
||
.comment-submit:disabled { opacity: .4; cursor: default; }
|
||
.comment-error { font-size: .75rem; color: var(--red); }
|
||
|
||
footer {
|
||
border-top: 1px solid var(--border);
|
||
padding: 1.2rem 2rem;
|
||
text-align: center;
|
||
font-size: .8rem;
|
||
color: var(--muted);
|
||
margin-top: 3rem;
|
||
}
|
||
footer a { color: var(--muted); text-decoration: none; }
|
||
footer a:hover { color: var(--text); }
|
||
|
||
@media (max-width: 700px) {
|
||
.layout { grid-template-columns: 1fr; }
|
||
.sidebar { position: static; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div>
|
||
<a href="/" style="text-decoration:none;display:flex;align-items:center;gap:.6rem">
|
||
<img src="/x9-logo.jpeg" alt="X9.cz" style="height:28px;width:28px;border-radius:5px;object-fit:cover;">
|
||
<span class="logo-text">xetup</span><span class="logo-sub">by X9.cz</span>
|
||
</a>
|
||
</div>
|
||
<nav>
|
||
<a href="/spec/" class="active">Specifikace</a>
|
||
<a href="https://git.xetup.x9.cz/x9/xetup">Git</a>
|
||
<a href="https://git.xetup.x9.cz/x9/xetup/issues">Issues</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<div class="layout">
|
||
|
||
<!-- SIDEBAR -->
|
||
<aside class="sidebar">
|
||
<h4>Kroky (scripty)</h4>
|
||
<a href="#step-00">00 – Admin ucet</a>
|
||
<a href="#step-01">01 – Bloatware</a>
|
||
<a href="#step-02">02 – Software (winget)</a>
|
||
<a href="#step-03">03 – Registry (HKLM)</a>
|
||
<a href="#step-04">04 – Default Profile</a>
|
||
<a href="#step-05">05 – Personalizace</a>
|
||
<a href="#step-06">06 – Scheduled Tasks</a>
|
||
<a href="#step-07">07 – BackInfo</a>
|
||
<a href="#step-08">08 – Aktivace Windows</a>
|
||
<hr class="sidebar-divider">
|
||
<h4>Planovane kroky</h4>
|
||
<a href="#step-pc">09 – PC identita + C:\X9</a>
|
||
<a href="#step-net">10 – Network discovery</a>
|
||
<a href="#step-taskbar">Taskbar profily</a>
|
||
<hr class="sidebar-divider">
|
||
<h4>Architektura</h4>
|
||
<a href="#arch-xetup">xetup.exe (Go TUI)</a>
|
||
<a href="#arch-spec">spec.yaml</a>
|
||
</aside>
|
||
|
||
<!-- CONTENT -->
|
||
<div class="content">
|
||
|
||
<h1>Specifikace & anotace</h1>
|
||
<div class="meta">
|
||
<span>Verze: 0.3-draft</span>
|
||
<span>Datum: 2026-04-16</span>
|
||
<span><a href="https://git.xetup.x9.cz/x9/xetup">x9/xetup</a></span>
|
||
<span>Status: aktivni vyvoj</span>
|
||
</div>
|
||
|
||
<div class="legend">
|
||
<span><span class="badge badge-ok">OK</span> hotovo, v provozu</span>
|
||
<span><span class="badge badge-mustfix">Must fix</span> zname chyby, nutno opravit</span>
|
||
<span><span class="badge badge-todo">TODO</span> naplanovano, nerealizovano</span>
|
||
<span><span class="badge badge-open">Open</span> otevrena otazka</span>
|
||
<span><span class="badge badge-warn">Warn</span> potencialni problem</span>
|
||
<span><span class="badge badge-new">New</span> nova feature</span>
|
||
<span><span class="badge badge-future">Future</span> dlouhodoby plan</span>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<p class="section-label">Stavajici kroky (scripty)</p>
|
||
|
||
<!-- STEP 00 -->
|
||
<div class="step" id="step-00">
|
||
<div class="step-header">
|
||
<span class="step-num">00</span>
|
||
<span class="step-title">Admin ucet (adminx9)</span>
|
||
<span class="badge badge-mustfix">Must fix</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>Vytvorit lokalni ucet <code>adminx9</code></td><td>Hotovo</td></tr>
|
||
<tr class="flag-done"><td>Pridat do skupiny Administrators</td><td>Hotovo</td></tr>
|
||
<tr class="flag-done"><td>Skryt z login obrazovky (SpecialAccounts\UserList = 0)</td><td>Hotovo</td></tr>
|
||
<tr class="flag-done"><td>Heslo nevypirsi, uzivatel nesmeni heslo</td><td>Hotovo</td></tr>
|
||
<tr class="flag-mustfix"><td>Zadne heslo (aktualne nastavovano z config.json)</td><td>Zmenit: ucet BEZ hesla (rozhodnuti)</td></tr>
|
||
<tr class="flag-mustfix"><td>FullName = "X9.cz s.r.o." (via ADSI)</td><td>Chybi, doplnit</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Proc bez hesla:</strong> Ucet je skryty pred uzivateli, slouzi pouze MSP adminstraci.
|
||
Heslo v config.json by bylo ulozene citelne.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>00-admin-account.ps1</code></span>
|
||
<div class="comment-widget" data-issue="1"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 01 -->
|
||
<div class="step" id="step-01">
|
||
<div class="step-header">
|
||
<span class="step-num">01</span>
|
||
<span class="step-title">Bloatware removal</span>
|
||
<span class="badge badge-ok">OK</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>AppX balicky – odstraneni pro vsechny uzivatele a provisioned</td><td>Remove-AppxPackage -AllUsers + Remove-AppxProvisionedPackage</td></tr>
|
||
<tr class="flag-done"><td>Zachovano: Microsoft.WindowsCalculator</td><td>Zamerny vyjimek</td></tr>
|
||
<tr class="flag-done"><td>Windows Capabilities (Fax, IE, OpenSSH, WMP, WordPad, …)</td><td>Remove-WindowsCapability</td></tr>
|
||
<tr class="flag-done"><td>Windows Optional Features (PS 2.0, MediaPlayback, Recall, …)</td><td>Disable-WindowsOptionalFeature</td></tr>
|
||
<tr class="flag-warn"><td>Microsoft-RemoteDesktopConnection NESMI byt odstranen</td><td>RDP klient musi zustat funkci. Overit ze neni v seznamu.</td></tr>
|
||
<tr class="flag-warn"><td>OneDrive nesmi byt odstranovano tady</td><td>OneDrive musi zustat instalovatelny pro M365.</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>01-bloatware.ps1</code></span>
|
||
<div class="comment-widget" data-issue="2"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 02 -->
|
||
<div class="step" id="step-02">
|
||
<div class="step-header">
|
||
<span class="step-num">02</span>
|
||
<span class="step-title">Software (winget)</span>
|
||
<span class="badge badge-todo">TODO</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>7-Zip (<code>7zip.7zip</code>)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Adobe Acrobat Reader 64-bit (<code>Adobe.Acrobat.Reader.64-bit</code>)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>OpenVPN Connect (<code>OpenVPNTechnologies.OpenVPNConnect</code>)</td><td>OK</td></tr>
|
||
<tr class="flag-todo"><td>Seznam SW je neuplny – co dalsiho patri dovnitr?</td><td>TODO: doplnit uplny seznam</td></tr>
|
||
<tr class="flag-done"><td>Atera Agent install</td><td>Invoke-WebRequest + <code>msiexec /i /qn</code></td></tr>
|
||
<tr class="flag-done"><td>Adobe PDF default: .pdf -> AcroRd32 po instalaci</td><td>OK – UCPD stop/start kolem zápisu asociace</td></tr>
|
||
<tr class="flag-done"><td>UCPD.sys (kernel driver, od Feb 2024) blokuje UserChoice</td><td>Reseno: Stop-Service ucpd → HKCR zapis → Start-Service ucpd</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Atera Agent URL:</strong><br>
|
||
<code>https://x9.servicedesk.atera.com/api/utils/agent-install/windows/?cid=31&aeid=50b72e7113e54a63ac76b96c54c7e337</code>
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>02-software.ps1</code></span>
|
||
<div class="comment-widget" data-issue="3"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 03 -->
|
||
<div class="step" id="step-03">
|
||
<div class="step-header">
|
||
<span class="step-num">03</span>
|
||
<span class="step-title">System Registry (HKLM)</span>
|
||
<span class="badge badge-todo">TODO</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>Bypass NRO (OOBE\BypassNRO = 1)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Zakaz auto-instalace Teams</td><td>ConfigureChatAutoInstall = 0</td></tr>
|
||
<tr class="flag-done"><td>Zakaz Cloud Optimized Content</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Zakaz Widgets / News and Interests</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Hesla bez expirace (<code>net accounts /maxpwage:UNLIMITED</code>)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Casova zona: Central Europe Standard Time</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Zakaz GameDVR</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Edge – skryt First Run Experience</td><td>HKLM\Policies\Edge\HideFirstRunExperience = 1</td></tr>
|
||
<tr class="flag-done"><td>OneDrive uninstall (intentional)</td><td>OneDriveSetup.exe /uninstall – odstrani pre-installed verzi. M365 si nainstaluje vlastni.</td></tr>
|
||
<tr class="flag-todo"><td>Edge policies – doplnit ~15 dalsich klicu</td><td>Viz seznam nize</td></tr>
|
||
<tr class="flag-todo"><td>Powercfg nastaveni (spotreba energie)</td><td>Pridat: standby-ac 0, monitor-ac 60, standby-dc 30, monitor-dc 15</td></tr>
|
||
<tr class="flag-todo"><td>Proxy auto-detect zakaz (AutoDetect = 0)</td><td>HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Edge policies k doplneni:</strong>
|
||
DefaultBrowserSettingEnabled = 0, NewTabPageContentEnabled = 0,
|
||
ImportOnEachLaunch = 0, ShowRecommendationsEnabled = 0,
|
||
PersonalizationReportingEnabled = 0, SpotlightExperiencesAndRecommendationsEnabled = 0,
|
||
DiagnosticData = 0, EdgeShoppingAssistantEnabled = 0, EdgeCollectionsEnabled = 0,
|
||
HubsSidebarEnabled = 0, ShowMicrosoftRewards = 0, SearchSuggestEnabled = 0 a dalsi.
|
||
</div>
|
||
<div class="note">
|
||
<strong>Powercfg prikazy:</strong><br>
|
||
<code>powercfg /change standby-timeout-ac 0</code> (neusne na nabijeni)<br>
|
||
<code>powercfg /change monitor-timeout-ac 60</code> (monitor zhasne po 60 min)<br>
|
||
<code>powercfg /change standby-timeout-dc 30</code><br>
|
||
<code>powercfg /change monitor-timeout-dc 15</code>
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>03-system-registry.ps1</code></span>
|
||
<div class="comment-widget" data-issue="4"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 04 -->
|
||
<div class="step" id="step-04">
|
||
<div class="step-header">
|
||
<span class="step-num">04</span>
|
||
<span class="step-title">Default Profile (NTUSER.DAT)</span>
|
||
<span class="badge badge-ok">OK</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>Taskbar: zarovnat vlevo (TaskbarAl = 0)</td><td>Win11 default je center</td></tr>
|
||
<tr class="flag-done"><td>Taskbar: skryt Search, Copilot, Task View, Widgets, Chat</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Taskbar: zobrazit vsechny ikonky v tray (Scheduled task)</td><td>ShowAllTrayIcons</td></tr>
|
||
<tr class="flag-done"><td>Taskbar: vyprazdnit pinlist (TaskbarLayoutModification.xml)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Explorer: zobrazovat pripony souboru (HideFileExt = 0)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Explorer: otevrit na This PC (LaunchTo = 1)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Start menu: vyprazdnit piny (Win11)</td><td>ConfigureStartPins = {"pinnedList":[]}</td></tr>
|
||
<tr class="flag-done"><td>Start menu: zakaz Bing vyhledavani</td><td>DisableSearchBoxSuggestions = 1</td></tr>
|
||
<tr class="flag-done"><td>Copilot: zakaz (TurnOffWindowsCopilot = 1)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>NumLock zapnout pri startu (InitialKeyboardIndicators = 2)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Accent barva na titulnich listech (ColorPrevalence = 1)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>OneDrive RunOnce klic je tady – smazat</td><td>Opraveno – blok odstranen ze scriptu (brani reinstalaci pres M365)</td></tr>
|
||
<tr class="flag-done"><td>Explorer: ShowRecent = 0, ShowFrequent = 0</td><td>Skryt nedavne a caste soubory v Quick Access</td></tr>
|
||
<tr class="flag-done"><td>Explorer: FullPath = 1 (CabinetState)</td><td>Zobrazovat plnou cestu v titulku okna Explorera</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Metoda:</strong> <code>reg load HKU\DefaultProfile C:\Users\Default\NTUSER.DAT</code>
|
||
→ zapsat zmeny → <code>reg unload HKU\DefaultProfile</code>.<br>
|
||
Tato operace musi probihat PRED prvnim prihlasenim uzivatele.
|
||
Aktualne prihlaseny uzivatel dostava zmeny pres primy zapis do HKCU.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>04-default-profile.ps1</code></span>
|
||
<div class="comment-widget" data-issue="5"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 05 -->
|
||
<div class="step" id="step-05">
|
||
<div class="step-header">
|
||
<span class="step-num">05</span>
|
||
<span class="step-title">Personalizace (barvy, tapeta)</span>
|
||
<span class="badge badge-ok">OK</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>System tema (taskbar, Start): Dark</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Aplikacni tema: Light</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Accent barva: #223B47 (tmave modroseda)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Accent barva na Start a taskbaru: ano</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Pruhlednost: vypnuta</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Tapeta: jednobarevna #223B47 (bez obrazku)</td><td>BackInfo prepise tapetu svym BMP</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
BackInfo.exe (STEP 07) prepise tapetu BMP se systemovymi informacemi.
|
||
Jednobarevna tapeta je fallback pro pripad, ze BackInfo nedobehne nebo se nespusti.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>05-personalization.ps1</code></span>
|
||
<div class="comment-widget" data-issue="6"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 06 -->
|
||
<div class="step" id="step-06">
|
||
<div class="step-header">
|
||
<span class="step-num">06</span>
|
||
<span class="step-title">Scheduled Tasks</span>
|
||
<span class="badge badge-ok">OK</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>ShowAllTrayIcons – pri logonu + kazdou 1 min</td><td>Win11 automaticky skryva tray ikony</td></tr>
|
||
<tr class="flag-done"><td>UnlockStartLayout – jednou po aplikaci layoutu</td><td>Odemkne Start menu pro uzivatelske zmeny</td></tr>
|
||
<tr class="flag-done"><td>PDF-DefaultApp pri kazdem logonu – odstranen</td><td>PDF asociace nastavena jednou v kroku 02 (UCPD stop/start). Task nebyl nutny.</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>06-scheduled-tasks.ps1</code></span>
|
||
<div class="comment-widget" data-issue="7"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 07 -->
|
||
<div class="step" id="step-07">
|
||
<div class="step-header">
|
||
<span class="step-num">07</span>
|
||
<span class="step-title">BackInfo (systemovy info na tapete)</span>
|
||
<span class="badge badge-mustfix">Must fix</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-mustfix"><td><code>07-desktop-info.ps1</code> SMAZAT – stary pristup</td><td>Nahradit deploym. krokem pro BackInfo.exe</td></tr>
|
||
<tr class="flag-mustfix"><td>Zkopirovat <code>assets/Backinfo/</code> do <code>C:\Program Files\Backinfo\</code></td><td>Pridat do master scriptu</td></tr>
|
||
<tr class="flag-mustfix"><td>Spustit <code>backinfo_W11.ps1</code> (detekce OS, registry, Startup)</td><td>Pridat do master scriptu</td></tr>
|
||
<tr class="flag-done"><td>BackInfo.exe v assets/Backinfo/ k dispozici</td><td>Hotovo – jen deploy krok chybi</td></tr>
|
||
<tr class="flag-done"><td>BackInfo auto-start pri kazdem logonu via Startup shortcut</td><td>Zaridi backinfo_W11.ps1</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>BackInfo.ini konfiguruje:</strong> hostname (velky, centrovan), uzivatelske jmeno,
|
||
OS verze, HW info (CPU, RAM, disk), sitove informace (IP, hostname).<br><br>
|
||
<strong>Proc BackInfo misto vlastniho PS:</strong>
|
||
BackInfo.exe podporuje Win10 i Win11 bez specialnich hacku, je stabilni a uz je v assets.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>07-desktop-info.ps1</code> (ke smazani) → novy inline krok v Deploy-Windows.ps1</span>
|
||
<div class="comment-widget" data-issue="8"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 08 -->
|
||
<div class="step" id="step-08">
|
||
<div class="step-header">
|
||
<span class="step-num">08</span>
|
||
<span class="step-title">Windows aktivace</span>
|
||
<span class="badge badge-ok">OK</span>
|
||
<span class="badge badge-open">Open</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-done"><td>OA3 BIOS/UEFI klic – kontrola embedded key</td><td>WMI: SoftwareLicensingService.OA3xOriginalProductKey</td></tr>
|
||
<tr class="flag-done"><td>Klic z config.json (<code>activation.productKey</code>)</td><td>OK – priorita nad OA3 a GVLK</td></tr>
|
||
<tr class="flag-done"><td>Fallback na GVLK (KMS client key) dle edice OS</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Volitelny KMS server (<code>activation.kmsServer</code>)</td><td>OK</td></tr>
|
||
<tr class="flag-done"><td>Preskocit pokud jiz aktivovano</td><td>OK</td></tr>
|
||
<tr class="flag-open"><td>Typ klice: MAK vs KMS vs retail?</td><td>Zavisi na klientovi – otevrena otazka</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: <code>08-activation.ps1</code></span>
|
||
<div class="comment-widget" data-issue="9"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<p class="section-label">Nove kroky (planovane)</p>
|
||
|
||
<!-- STEP 09 -->
|
||
<div class="step" id="step-pc">
|
||
<div class="step-header">
|
||
<span class="step-num">09</span>
|
||
<span class="step-title">PC identita – Rename + C:\X9</span>
|
||
<span class="badge badge-new">New</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-todo"><td>Rename-Computer dle parametru z TUI nebo config.json</td><td>Finalni krok pred restartem – PC name + popis</td></tr>
|
||
<tr class="flag-todo"><td>Nastavit popis pocitace (Computer Description)</td><td>Via WMI nebo registry HKLM\SYSTEM\...\ComputerName</td></tr>
|
||
<tr class="flag-todo"><td>Vytvorit <code>C:\X9\</code> adresarovou strukturu</td><td>Pro logy, skripty, assets</td></tr>
|
||
<tr class="flag-todo"><td>Vlastni ikonka pro <code>C:\X9\</code> slozku</td><td>Desktop.ini + X9-ikona.ico</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
Rename-Computer vyzaduje restart. Tento krok musi byt posledni pred finalnim shrnutim.
|
||
Technik vi, ze po deployi nasleduje restart.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: novy <code>09-pc-identity.ps1</code></span>
|
||
<div class="comment-widget" data-issue="12"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 10 -->
|
||
<div class="step" id="step-net">
|
||
<div class="step-header">
|
||
<span class="step-num">10</span>
|
||
<span class="step-title">Network discovery + firewall</span>
|
||
<span class="badge badge-new">New</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-todo"><td>Nastavit sitovy profil jako Private (ne Public)</td><td><code>Set-NetConnectionProfile -NetworkCategory Private</code></td></tr>
|
||
<tr class="flag-todo"><td>Povolit ping (ICMP) pro diagnostiku</td><td>Firewall rule: Enable ICMPv4/ICMPv6</td></tr>
|
||
<tr class="flag-todo"><td>Zapnout Network Discovery pro Private profil</td><td><code>netsh advfirewall</code> nebo <code>Set-NetFirewallRule</code></td></tr>
|
||
</table>
|
||
<div class="note">
|
||
Pozor: Sitovy profil (Private/Public) se muze zmenit po kazdem prihlaseni k jine siti.
|
||
Zvazit scheduled task pri logonu pro opakovanou korekci profilu.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Script: novy <code>10-network.ps1</code></span>
|
||
<div class="comment-widget" data-issue="10"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TASKBAR -->
|
||
<div class="step" id="step-taskbar">
|
||
<div class="step-header">
|
||
<span class="step-num">---</span>
|
||
<span class="step-title">Taskbar pinned apps (profily)</span>
|
||
<span class="badge badge-new">New</span>
|
||
<span class="badge badge-future">Future</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-todo"><td><code>-ProfileType</code> parametr: admin vs user varianta</td><td>Ruzna sada pinnutych appek dle role uzivatele</td></tr>
|
||
<tr class="flag-todo"><td>XML layout pro "admin": Explorer, PS, Edge, Notepad++, …</td><td>TaskbarLayoutModification.xml</td></tr>
|
||
<tr class="flag-todo"><td>XML layout pro "user": Edge, Outlook, Teams, Explorer, …</td><td>Odlisna sada pro bezneho zamestnance</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
Win11 24H2 zmenil zpusob aplikace Taskbar layoutu (ProvisionedLayoutModification.xml vs. starsi TaskbarLayoutModification.xml).
|
||
Nutno overit kompatibilitu s ruznymy buildy pred implementaci.
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Zacleneni: STEP 04 nebo vlastni script</span>
|
||
<div class="comment-widget" data-issue="13"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<p class="section-label">Architektura (budoucnost)</p>
|
||
|
||
<!-- ARCH XETUP -->
|
||
<div class="step" id="arch-xetup">
|
||
<div class="step-header">
|
||
<span class="step-num">Arc</span>
|
||
<span class="step-title">xetup.exe – Go TUI launcher</span>
|
||
<span class="badge badge-future">Future</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-todo"><td>Single binary (go:embed scripty + assets)</td><td>Offline provoz, jedna stazitelna .exe</td></tr>
|
||
<tr class="flag-todo"><td>TUI form (huh/bubbletea): PC name, popis, product key</td><td>Interaktivni zadani dat technikem</td></tr>
|
||
<tr class="flag-todo"><td>Checklist kroku (on/off per-script) + ulozit do config.json</td><td>Opakovatelne nasazeni u stejneho klienta</td></tr>
|
||
<tr class="flag-todo"><td>Live log output behem spousteni PS scriptu</td><td>Stdout z powershell.exe v realnem case</td></tr>
|
||
<tr class="flag-todo"><td>Finalni summary OK/ERROR</td><td>Na konci nasazeni</td></tr>
|
||
<tr class="flag-todo"><td>Self-update: stahnout novou verzi z xetup.x9.cz</td><td>Overit hash pred spustenim</td></tr>
|
||
<tr class="flag-future"><td>config.json: per-klient preset (prefix jmena PC, SW, klic)</td><td>Lezi vedle .exe na USB klienta</td></tr>
|
||
<tr class="flag-future"><td>OpenVPN soubor + doménovy join + domén. uzivatel pro profil</td><td>Rozsireni TUI formulare v budoucnu</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Struktura:</strong> <code>cmd/xetup/</code>, <code>internal/config/</code>,
|
||
<code>internal/spec/</code>, <code>internal/tui/</code>, <code>internal/runner/</code><br><br>
|
||
<strong>Go zavislosti:</strong>
|
||
bubbletea (TUI framework), huh (forms), lipgloss (styling)
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Status: navrh, zatim zadny kod</span>
|
||
<div class="comment-widget" data-issue="11"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ARCH SPEC -->
|
||
<div class="step" id="arch-spec">
|
||
<div class="step-header">
|
||
<span class="step-num">Arc</span>
|
||
<span class="step-title">spec.yaml – single source of truth</span>
|
||
<span class="badge badge-future">Future</span>
|
||
</div>
|
||
<div class="step-body">
|
||
<table class="items">
|
||
<tr class="flag-todo"><td>Popis vsech kroku: id, label, script, default</td><td>xetup.exe cte spec.yaml pro TUI checklist</td></tr>
|
||
<tr class="flag-todo"><td>Pole "requires" (napr. activation vyzaduje productKey)</td><td>TUI upozorni pokud chybi</td></tr>
|
||
<tr class="flag-future"><td>Auto-generovana dokumentace z spec.yaml</td><td>CI akce: spec.yaml → tato stranka</td></tr>
|
||
<tr class="flag-future"><td>spec.yaml jako SSOT pro tuto stranku i deploy skripty</td><td>Idealni stav: stranka vzdy odpovida kodu</td></tr>
|
||
</table>
|
||
<div class="note">
|
||
<strong>Navrh struktury spec.yaml:</strong><br>
|
||
<code style="display:block; white-space:pre; line-height:1.6">steps:
|
||
- id: admin-account
|
||
label: "Admin account (adminx9)"
|
||
script: 00-admin-account.ps1
|
||
default: true
|
||
- id: activation
|
||
label: "Windows activation"
|
||
script: 08-activation.ps1
|
||
default: true
|
||
requires: [productKey]</code>
|
||
</div>
|
||
</div>
|
||
<div class="step-footer">
|
||
<span class="step-status">Status: navrh, zatim zadny soubor</span>
|
||
<div class="comment-widget" data-issue="14"></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /content -->
|
||
</div><!-- /layout -->
|
||
|
||
<footer>
|
||
© 2026 <a href="https://x9.cz">X9.cz s.r.o.</a>
|
||
·
|
||
<a href="https://git.xetup.x9.cz/x9/xetup">Forgejo</a>
|
||
·
|
||
<a href="/">xetup.x9.cz</a>
|
||
</footer>
|
||
|
||
<script>
|
||
(function() {
|
||
const API = '/forgejo-api';
|
||
const REPO = 'x9/xetup';
|
||
const TOKEN = 'e67f674af71847c4349b79b51d2b66a1ea41d031';
|
||
const HEADS = { 'Authorization': 'token ' + TOKEN, 'Content-Type': 'application/json' };
|
||
|
||
function timeAgo(iso) {
|
||
const s = Math.floor((Date.now() - new Date(iso)) / 1000);
|
||
if (s < 60) return 'prave ted';
|
||
if (s < 3600) return Math.floor(s/60) + ' min';
|
||
if (s < 86400)return Math.floor(s/3600) + ' hod';
|
||
return Math.floor(s/86400) + ' dni';
|
||
}
|
||
|
||
function renderComments(list, listEl) {
|
||
listEl.innerHTML = '';
|
||
if (!list.length) {
|
||
listEl.innerHTML = '<div class="comment-empty">Zatim zadne komentare. Bud prvni.</div>';
|
||
return;
|
||
}
|
||
list.forEach(c => {
|
||
const el = document.createElement('div');
|
||
el.className = 'comment-item';
|
||
const name = c.user.login === 'xetup-bot'
|
||
? (c.body.match(/^\*\*(.+?)\*\*/) ? c.body.match(/^\*\*(.+?)\*\*/)[1] : 'anon')
|
||
: c.user.login;
|
||
const body = c.body.replace(/^\*\*.+?\*\*\n/, '');
|
||
el.innerHTML =
|
||
'<div class="comment-meta">' + name + ' · ' + timeAgo(c.created_at) + '</div>' +
|
||
'<div class="comment-body">' + body.replace(/</g,'<').replace(/>/g,'>') + '</div>';
|
||
listEl.appendChild(el);
|
||
});
|
||
}
|
||
|
||
function initWidget(el) {
|
||
const issue = el.dataset.issue;
|
||
if (!issue || issue === '0') return;
|
||
|
||
el.innerHTML =
|
||
'<button class="comment-toggle" title="Komentare k teto sekci">' +
|
||
'💬 Komentare <span class="cnt"></span>' +
|
||
'</button>' +
|
||
'<div class="comment-panel">' +
|
||
'<div class="comment-list"><div class="comment-empty">Nacitam...</div></div>' +
|
||
'<div class="comment-form">' +
|
||
'<input class="comment-input-name" placeholder="Tvoje jmeno (volitelne)" maxlength="40">' +
|
||
'<textarea class="comment-input" placeholder="Napiste komentar..."></textarea>' +
|
||
'<div style="display:flex;gap:.5rem;align-items:center;justify-content:flex-end">' +
|
||
'<span class="comment-error"></span>' +
|
||
'<button class="comment-submit">Odeslat</button>' +
|
||
'</div>' +
|
||
'</div>' +
|
||
'</div>';
|
||
|
||
const toggle = el.querySelector('.comment-toggle');
|
||
const panel = el.querySelector('.comment-panel');
|
||
const listEl = el.querySelector('.comment-list');
|
||
const nameEl = el.querySelector('.comment-input-name');
|
||
const textEl = el.querySelector('.comment-input');
|
||
const submitEl= el.querySelector('.comment-submit');
|
||
const errEl = el.querySelector('.comment-error');
|
||
const cntEl = el.querySelector('.cnt');
|
||
|
||
let loaded = false;
|
||
let comments = [];
|
||
|
||
function load() {
|
||
fetch(API + '/repos/' + REPO + '/issues/' + issue + '/comments', { headers: HEADS })
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
comments = Array.isArray(data) ? data : [];
|
||
renderComments(comments, listEl);
|
||
if (comments.length) {
|
||
cntEl.textContent = comments.length;
|
||
cntEl.classList.add('visible');
|
||
}
|
||
loaded = true;
|
||
})
|
||
.catch(() => { listEl.innerHTML = '<div class="comment-empty">Chyba nacitani.</div>'; });
|
||
}
|
||
|
||
toggle.addEventListener('click', () => {
|
||
const open = panel.classList.toggle('open');
|
||
if (open && !loaded) load();
|
||
});
|
||
|
||
submitEl.addEventListener('click', () => {
|
||
const text = textEl.value.trim();
|
||
if (!text) { errEl.textContent = 'Komentar nesmi byt prazdny.'; return; }
|
||
errEl.textContent = '';
|
||
submitEl.disabled = true;
|
||
submitEl.textContent = 'Odesilam...';
|
||
|
||
const name = nameEl.value.trim() || 'anon';
|
||
const body = '**' + name + '**\n' + text;
|
||
|
||
fetch(API + '/repos/' + REPO + '/issues/' + issue + '/comments', {
|
||
method: 'POST', headers: HEADS, body: JSON.stringify({ body: body })
|
||
})
|
||
.then(r => { if (!r.ok) throw new Error(r.status); return r.json(); })
|
||
.then(c => {
|
||
comments.push(c);
|
||
renderComments(comments, listEl);
|
||
cntEl.textContent = comments.length;
|
||
cntEl.classList.add('visible');
|
||
textEl.value = '';
|
||
nameEl.value = '';
|
||
submitEl.disabled = false;
|
||
submitEl.textContent = 'Odeslat';
|
||
})
|
||
.catch(() => {
|
||
errEl.textContent = 'Chyba odeslani. Zkus znovu.';
|
||
submitEl.disabled = false;
|
||
submitEl.textContent = 'Odeslat';
|
||
});
|
||
});
|
||
}
|
||
|
||
document.querySelectorAll('.comment-widget').forEach(initWidget);
|
||
|
||
// ---- DESCRIPTIONS FROM scripts/*.ps1 headers ----
|
||
let DESCRIPTIONS = {};
|
||
fetch('/data/descriptions.json')
|
||
.then(r => r.json())
|
||
.then(d => { DESCRIPTIONS = d; })
|
||
.catch(() => {});
|
||
|
||
// Map spec step id -> script id
|
||
const STEP_SCRIPT = {
|
||
'step-00': '00-admin-account',
|
||
'step-01': '01-bloatware',
|
||
'step-02': '02-software',
|
||
'step-03': '03-system-registry',
|
||
'step-04': '04-default-profile',
|
||
'step-05': '05-personalization',
|
||
'step-06': '06-scheduled-tasks',
|
||
'step-07': '07-desktop-info',
|
||
'step-08': '08-activation',
|
||
};
|
||
|
||
function getItemDesc(stepId, slug) {
|
||
const scriptId = STEP_SCRIPT[stepId];
|
||
if (!scriptId || !DESCRIPTIONS[scriptId]) return null;
|
||
return DESCRIPTIONS[scriptId].items[slug] || null;
|
||
}
|
||
|
||
// ---- ROW-LEVEL EXPAND + COMMENTS ----
|
||
|
||
function rSlug(txt) {
|
||
return txt.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').substring(0, 40);
|
||
}
|
||
|
||
function esc(s) { return s.replace(/</g,'<').replace(/>/g,'>'); }
|
||
|
||
function renderRowComments(all, slug, listEl) {
|
||
const prefix = '[row:' + slug + ']';
|
||
const relevant = all.filter(c => c.body.startsWith(prefix));
|
||
listEl.innerHTML = '';
|
||
if (!relevant.length) {
|
||
listEl.innerHTML = '<div style="font-size:.75rem;color:var(--muted);font-style:italic;padding:.2rem 0">Zatim zadne komentare.</div>';
|
||
return;
|
||
}
|
||
relevant.forEach(c => {
|
||
const body = c.body.slice(prefix.length + 1);
|
||
const m = body.match(/^\*\*(.+?)\*\*\n([\s\S]*)$/);
|
||
const name = m ? m[1] : 'anon';
|
||
const text = m ? m[2] : body;
|
||
const el = document.createElement('div');
|
||
el.className = 'rci';
|
||
el.innerHTML = '<div class="rci-meta">' + esc(name) + ' · ' + timeAgo(c.created_at) + '</div>' +
|
||
'<div class="rci-body">' + esc(text) + '</div>';
|
||
listEl.appendChild(el);
|
||
});
|
||
}
|
||
|
||
|
||
function enhanceRows() {
|
||
document.querySelectorAll('.step').forEach(step => {
|
||
const widget = step.querySelector('.comment-widget');
|
||
const issueNum = widget ? widget.dataset.issue : null;
|
||
const stepId = step.id;
|
||
|
||
step.querySelectorAll('.items tr:not(.detail-tr)').forEach(tr => {
|
||
const cells = tr.querySelectorAll('td');
|
||
if (!cells.length) return;
|
||
const rowText = cells[0].textContent.trim();
|
||
const noteText = cells[1] ? cells[1].textContent.trim() : '';
|
||
const slug = rSlug(rowText);
|
||
|
||
// Expand button as last cell
|
||
const xpCell = document.createElement('td');
|
||
xpCell.className = 'xp';
|
||
const btn = document.createElement('button');
|
||
btn.className = 'xp-btn';
|
||
btn.innerHTML = 'detail ▸';
|
||
btn.title = 'Zobrazit detail a komentare';
|
||
xpCell.appendChild(btn);
|
||
tr.appendChild(xpCell);
|
||
|
||
// Detail row
|
||
const dtr = document.createElement('tr');
|
||
dtr.className = 'detail-tr';
|
||
dtr.style.display = 'none';
|
||
const dtd = document.createElement('td');
|
||
dtd.colSpan = 3;
|
||
// Description from script header (loaded async - may not be ready yet,
|
||
// will be filled in when detail opens)
|
||
const scriptDesc = () => getItemDesc(stepId, slug);
|
||
|
||
dtd.innerHTML =
|
||
// description from script header
|
||
'<div class="detail-heading">Co to dela</div>' +
|
||
'<div class="detail-desc-script"></div>' +
|
||
// brief note from col 2 as secondary context
|
||
(noteText ? '<div class="detail-note">' + esc(noteText) + '</div>' : '') +
|
||
'<hr class="detail-divider">' +
|
||
// comments
|
||
'<div class="detail-heading">Komentare</div>' +
|
||
'<div class="rcl"></div>' +
|
||
'<div class="rcf">' +
|
||
'<input class="rcf-name" placeholder="Jmeno" maxlength="40">' +
|
||
'<textarea class="rcf-text" placeholder="Komentar k tomuto radku..." rows="2"></textarea>' +
|
||
'<button class="rcf-send">Odeslat</button>' +
|
||
'</div>';
|
||
dtr.appendChild(dtd);
|
||
tr.after(dtr);
|
||
|
||
let loaded = false;
|
||
let allComments = [];
|
||
const getAll = () => allComments;
|
||
|
||
btn.addEventListener('click', () => {
|
||
const opening = dtr.style.display === 'none';
|
||
dtr.style.display = opening ? '' : 'none';
|
||
btn.classList.toggle('open', opening);
|
||
btn.innerHTML = opening ? 'detail ▾' : 'detail ▸';
|
||
|
||
if (opening) {
|
||
// Fill script description (JSON may now be loaded)
|
||
const descEl = dtd.querySelector('.detail-desc-script');
|
||
const desc = scriptDesc();
|
||
if (desc) {
|
||
descEl.textContent = desc;
|
||
descEl.className = 'detail-desc-script';
|
||
} else {
|
||
descEl.textContent = 'Popis bude dostupny po spusteni tools/extract-docs.py.';
|
||
descEl.className = 'detail-desc-script detail-desc-script--empty';
|
||
}
|
||
}
|
||
|
||
if (opening && !loaded && issueNum) {
|
||
loaded = true;
|
||
const listEl = dtd.querySelector('.rcl');
|
||
listEl.innerHTML = '<div style="font-size:.75rem;color:var(--muted)">Nacitam...</div>';
|
||
fetch(API + '/repos/' + REPO + '/issues/' + issueNum + '/comments', { headers: HEADS })
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
allComments = Array.isArray(data) ? data : [];
|
||
renderRowComments(allComments, slug, listEl);
|
||
})
|
||
.catch(() => {
|
||
listEl.innerHTML = '<div style="font-size:.75rem;color:var(--red)">Chyba nacitani.</div>';
|
||
});
|
||
}
|
||
});
|
||
|
||
dtd.querySelector('.rcf-send').addEventListener('click', function() {
|
||
const nameEl = dtd.querySelector('.rcf-name');
|
||
const textEl = dtd.querySelector('.rcf-text');
|
||
const text = textEl.value.trim();
|
||
if (!text || !issueNum) return;
|
||
this.disabled = true;
|
||
const name = nameEl.value.trim() || 'anon';
|
||
const body = '[row:' + slug + ']\n**' + name + '**\n' + text;
|
||
fetch(API + '/repos/' + REPO + '/issues/' + issueNum + '/comments', {
|
||
method: 'POST', headers: HEADS, body: JSON.stringify({ body: body })
|
||
})
|
||
.then(r => r.json())
|
||
.then(c => {
|
||
allComments.push(c);
|
||
renderRowComments(allComments, slug, dtd.querySelector('.rcl'));
|
||
textEl.value = ''; nameEl.value = '';
|
||
this.disabled = false;
|
||
})
|
||
.catch(() => { this.disabled = false; });
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
enhanceRows();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|