xetup/web/spec/index.html
X9 Dev be412e99bc docs(spec): update spec page to reflect current reality
- Sidebar: all 13 steps listed in order (00-12), arch updated
- Step 03: add Edge new tab page policy rows (quick links, background, content)
- Step 04: taskbar pin row updated — explicit Explorer+Edge pins, PinListPlacement=Replace
- Step 05: add Wallpaper="" fix row (black desktop for new users)
- Steps 09/10/11: correct IDs (step-09/10/11), moved out of "Planovane" section
- Step 12: new Windows Update card with PSWindowsUpdate details
- Arch: xetup.exe rewritten — Walk GUI (Win32), no OpenGL, VMware-safe, step strip, auto-reboot
- Version bump: 0.3-draft -> 0.4
- JS STEP_SCRIPT: updated IDs to match new step anchor names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 15:51:14 +02:00

1382 lines
59 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Specifikace &amp; 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); }
/* ---- NEW REQUEST WIDGET ---- */
.req-list {
margin-bottom: .8rem;
}
.req-item {
padding: .55rem .7rem;
border: 1px solid var(--border);
border-radius: 7px;
margin-bottom: .45rem;
font-size: .83rem;
line-height: 1.5;
background: var(--card2);
}
.req-item-meta { font-size: .72rem; color: var(--muted); margin-bottom: .2rem; }
.req-item-body { color: var(--text); white-space: pre-wrap; word-break: break-word; }
.req-form { display: flex; flex-direction: column; gap: .45rem; }
.req-name {
width: 220px;
background: var(--bg); border: 1px solid var(--border); border-radius: 5px;
color: var(--text); font-size: .8rem; padding: .35rem .5rem; font-family: inherit;
}
.req-text {
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: 70px; font-family: inherit; line-height: 1.4;
}
.req-name:focus, .req-text:focus { outline: none; border-color: var(--accent-bright); }
.req-submit {
align-self: flex-start;
background: var(--accent-bright); color: #fff; border: none;
border-radius: 5px; padding: .32rem .9rem; font-size: .8rem; cursor: pointer;
transition: opacity .15s;
}
.req-submit:hover { opacity: .85; }
.req-submit:disabled { opacity: .4; cursor: default; }
.req-error { font-size: .75rem; color: var(--red); margin-top: .1rem; }
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 &ndash; Admin ucet</a>
<a href="#step-01">01 &ndash; Bloatware</a>
<a href="#step-02">02 &ndash; Software (winget)</a>
<a href="#step-03">03 &ndash; Registry (HKLM)</a>
<a href="#step-04">04 &ndash; Default Profile</a>
<a href="#step-05">05 &ndash; Personalizace</a>
<a href="#step-06">06 &ndash; Scheduled Tasks</a>
<a href="#step-07">07 &ndash; BackInfo</a>
<a href="#step-08">08 &ndash; Aktivace Windows</a>
<a href="#step-09">09 &ndash; PC identita</a>
<a href="#step-10">10 &ndash; Network discovery</a>
<a href="#step-11">11 &ndash; Dell Command | Update</a>
<a href="#step-12">12 &ndash; Windows Update</a>
<hr class="sidebar-divider">
<h4>Architektura</h4>
<a href="#arch-xetup">xetup.exe (Go GUI)</a>
<a href="#arch-spec">spec.yaml</a>
<hr class="sidebar-divider">
<a href="#new-requests">+ Novy pozadavek</a>
</aside>
<!-- CONTENT -->
<div class="content">
<h1>Specifikace &amp; anotace</h1>
<div class="meta">
<span>Verze: 0.4</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-ok">OK</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-done"><td>Zadne heslo (aktualne nastavovano z config.json)</td><td>Opraveno &ndash; prazdny SecureString, config.json heslo odstranen</td></tr>
<tr class="flag-done"><td>FullName = "X9.cz s.r.o." (via ADSI)</td><td>Opraveno &ndash; ADSI SetInfo() po vytvoreni uctu</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 &ndash; 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>
</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-ok">OK</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-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 -&gt; AcroRd32 po instalaci</td><td>OK &ndash; 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 &rarr; HKCR zapis &rarr; 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&amp;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-ok">OK</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 &ndash; skryt First Run Experience + zakaz default browser prompt</td><td>HideFirstRunExperience=1, DefaultBrowserSettingEnabled=0</td></tr>
<tr class="flag-done"><td>Edge policies &ndash; panel oblibeny, vyhledavac Google</td><td>FavoritesBarEnabled=1, DefaultSearchProviderName=Google, ManagedSearchEngines</td></tr>
<tr class="flag-done"><td>Edge policies &ndash; tlacitka zobrazit (Historie, Stahnout)</td><td>DownloadsButtonEnabled=1, HistoryButtonEnabled=1</td></tr>
<tr class="flag-done"><td>Edge policies &ndash; tlacitka skryt (Home, Kolekce, Split, Drop, Screenshot, Share, Zpetna vazba)</td><td>HomeButtonEnabled=0, SplitScreenEnabled=0, EdgeEDropEnabled=0, WebCaptureEnabled=0, ShareAllowed=0, FeedbackSurveysEnabled=0, EdgeCollectionsEnabled=0</td></tr>
<tr class="flag-done"><td>Edge policies &ndash; obsah a telemetrie</td><td>NewTabPageContentEnabled=0, ShowRecommendationsEnabled=0, EdgeShoppingAssistantEnabled=0, DiagnosticData=0, &hellip;</td></tr>
<tr class="flag-done"><td>OneDrive uninstall (intentional)</td><td>OneDriveSetup.exe /uninstall &ndash; odstrani pre-installed verzi. M365 si nainstaluje vlastni.</td></tr>
<tr class="flag-done"><td>Powercfg nastaveni (spotreba energie)</td><td>standby-ac 0, monitor-ac 60, standby-dc 30, monitor-dc 15</td></tr>
<tr class="flag-done"><td>Proxy auto-detect zakaz (AutoDetect = 0)</td><td>HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings</td></tr>
<tr class="flag-done"><td>Edge nova karta &ndash; zakaz rychlych odkazu</td><td>NewTabPageQuickLinksEnabled = 0</td></tr>
<tr class="flag-done"><td>Edge nova karta &ndash; zakaz pozadi</td><td>NewTabPageBackgroundEnabled = 0</td></tr>
<tr class="flag-done"><td>Edge nova karta &ndash; zakaz obsahu / feedu</td><td>NewTabPageAllowedBackgroundTypes = 3 (pouze plna barva)</td></tr>
</table>
</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: explicitni pinlist (TaskbarLayoutModification.xml)</td><td>default/user: Pruzkumnik + Edge; admin: Pruzkumnik + Edge + PowerShell. <code>PinListPlacement="Replace"</code> &ndash; prazdny seznam by dovoloval Windows pridat Store a dalsi vychozi.</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 &ndash; smazat</td><td>Opraveno &ndash; 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>
&rarr; zapsat zmeny &rarr; <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>
<tr class="flag-done"><td>Wallpaper="" v default hive (oprava cerne tapety)</td><td>Bez tohoto klice novy uzivatel zdedi neplatnou cestu k tapete a dostane cernou plochu. Prazdny retezec = solid barva z <code>Control Panel\Colors\Background</code></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 &ndash; pri logonu + kazdou 1 min</td><td>Win11 automaticky skryva tray ikony</td></tr>
<tr class="flag-done"><td>UnlockStartLayout &ndash; jednou po aplikaci layoutu</td><td>Odemkne Start menu pro uzivatelske zmeny</td></tr>
<tr class="flag-done"><td>PDF-DefaultApp pri kazdem logonu &ndash; 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-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td><code>07-desktop-info.ps1</code> SMAZAT &ndash; stary pristup</td><td>Nahrazeno novym <code>07-backinfo.ps1</code></td></tr>
<tr class="flag-done"><td>Zkopirovat <code>assets/Backinfo/</code> do <code>C:\Program Files\Backinfo\</code></td><td>Implementovano v 07-backinfo.ps1</td></tr>
<tr class="flag-done"><td>Spustit <code>backinfo_W11.ps1</code> (detekce OS, registry, Startup)</td><td>Logika inlinovana v 07-backinfo.ps1</td></tr>
<tr class="flag-done"><td>BackInfo.exe v assets/Backinfo/ k dispozici</td><td>Hotovo</td></tr>
<tr class="flag-done"><td>BackInfo auto-start pri kazdem logonu via Startup shortcut</td><td>Shortcut do ProgramData\StartUp vytvori 07-backinfo.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-backinfo.ps1</code></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 &ndash; 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 &ndash; 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 &ndash; 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>
<!-- STEP 09 -->
<div class="step" id="step-09">
<div class="step-header">
<span class="step-num">09</span>
<span class="step-title">PC identita &ndash; Rename + C:\X9</span>
<span class="badge badge-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td>Rename-Computer dle parametru z GUI nebo config.json</td><td><code>deployment.pcName</code> v config.json; preskoci pokud neni nastaveno</td></tr>
<tr class="flag-done"><td>Nastavit popis pocitace (Computer Description)</td><td>LanmanServer\Parameters\SrvComment; default "X9 deployment"</td></tr>
<tr class="flag-done"><td>Vytvorit <code>C:\X9\</code> adresarovou strukturu</td><td>C:\X9\Logs, Scripts, Assets</td></tr>
<tr class="flag-done"><td>Vlastni ikonka pro <code>C:\X9\</code> slozku</td><td>Desktop.ini + X9-ikona.ico z assets\Logo\</td></tr>
</table>
<div class="note">
Rename-Computer vyzaduje restart. Tento krok bezi jako posledni pred finalnim shrnutim &ndash; po nem nasleduje automaticky odpocet a restart v GUI.
</div>
</div>
<div class="step-footer">
<span class="step-status">Script: <code>09-pc-identity.ps1</code></span>
<div class="comment-widget" data-issue="12"></div>
</div>
</div>
<!-- STEP 10 -->
<div class="step" id="step-10">
<div class="step-header">
<span class="step-num">10</span>
<span class="step-title">Network discovery + firewall</span>
<span class="badge badge-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td>Nastavit sitovy profil jako Private (ne Public)</td><td>Set-NetConnectionProfile pro vsechny pripojene adaptery</td></tr>
<tr class="flag-done"><td>Povolit ping (ICMP) pro diagnostiku</td><td>Enable-NetFirewallRule: FPS-ICMP4-ERQ-In + FPS-ICMP6-ERQ-In</td></tr>
<tr class="flag-done"><td>Zapnout Network Discovery pro Private profil</td><td>Set-NetFirewallRule + netsh advfirewall jako fallback</td></tr>
</table>
</div>
<div class="step-footer">
<span class="step-status">Script: <code>10-network.ps1</code></span>
<div class="comment-widget" data-issue="10"></div>
</div>
</div>
<!-- STEP 11 -->
<div class="step" id="step-11">
<div class="step-header">
<span class="step-num">11</span>
<span class="step-title">Dell Command | Update</span>
<span class="badge badge-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td>Detekce Dell hardware (<code>Win32_ComputerSystem</code>)</td><td>Non-Dell stroj krok preskoci bez chyby &ndash; stejny skript pro vsechny HW</td></tr>
<tr class="flag-done"><td>Instalace Dell Command | Update via winget</td><td><code>Dell.CommandUpdate.Universal</code> &ndash; silent, Win10 + Win11</td></tr>
<tr class="flag-done"><td>Spusteni vsech aktualizaci: drivery, firmware, BIOS</td><td><code>dcu-cli.exe /applyUpdates -silent -reboot=disable</code></td></tr>
<tr class="flag-done"><td>BIOS/firmware se staging &ndash; dokonci se pri restartu</td><td>Restart na konci deploymetu vse dokonci</td></tr>
</table>
<div class="note">
Non-Dell stroje: krok se preskoci automaticky, zadna chyba. Dell Latitude, OptiPlex,
Precision, Vostro, XPS &ndash; vsechny podporovane DCU Universal.<br><br>
<strong>Casova narocnost:</strong> 5&ndash;20 minut podle poctu dostupnych aktualizaci a rychlosti sitoveho pripojeni.
</div>
</div>
<div class="step-footer">
<span class="step-status">Script: <code>11-dell-update.ps1</code></span>
<div class="comment-widget" data-issue="16"></div>
</div>
</div>
<!-- STEP 12 -->
<div class="step" id="step-12">
<div class="step-header">
<span class="step-num">12</span>
<span class="step-title">Windows Update</span>
<span class="badge badge-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td>Instalace NuGet providera + PSWindowsUpdate modulu ze PSGallery</td><td>Install-PackageProvider + Install-Module PSWindowsUpdate -Force</td></tr>
<tr class="flag-done"><td>Prvni pruchod aktualizaci (bez rebootu)</td><td>Install-WindowsUpdate -AcceptAll -IgnoreReboot</td></tr>
<tr class="flag-done"><td>X9-WindowsUpdate scheduled task &ndash; pokracovani po restartu</td><td>Bezi pri kazdem logonu jako SYSTEM; po pruchodu bez novych update se task sam smaze</td></tr>
<tr class="flag-done"><td>Automaticky restart po skonceni deploymetu (GUI odpocet)</td><td>xetup.exe zobrazi 60s odpocet + tlacitka "Restartovat ted" / "Zrusit restart"</td></tr>
</table>
<div class="note">
Windows Update typicky vyzaduje vice restartovacich kolu. Prvni pruchod probehne behem deploymetu,
dalsi kola zaridti scheduled task, ktery se po dokonceni sam odregistruje.<br><br>
<strong>Pozor:</strong> Prvni restart spusti jak Windows Update task, tak Dell firmware staging (krok 11).
</div>
</div>
<div class="step-footer">
<span class="step-status">Script: <code>12-windows-update.ps1</code></span>
<div class="comment-widget" data-issue="17"></div>
</div>
</div>
<!-- TASKBAR PROFILY - detail implementace kroku 04 -->
<div class="step" id="step-taskbar">
<div class="step-header">
<span class="step-num">04+</span>
<span class="step-title">Taskbar pinned apps (profily)</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><code>-ProfileType</code> parametr: default / admin / user</td><td>Parametr GUI nebo config.json; predano do skriptu 04</td></tr>
<tr class="flag-done"><td>XML layout pro "default" a "user": Pruzkumnik + Edge</td><td><code>PinListPlacement="Replace"</code> &ndash; explicitni seznam zamezuje pridani Store a dalsich vychozich appek</td></tr>
<tr class="flag-done"><td>XML layout pro "admin": Pruzkumnik + Edge + PowerShell</td><td>TaskbarLayoutModification.xml; PowerShell.lnk z System32</td></tr>
<tr class="flag-open"><td>Win11 24H2 kompatibilita layoutu</td><td>24H2 vyzaduje ProvisionedLayoutModification.xml &ndash; nutno otestovat na realne instalaci</td></tr>
</table>
<div class="note">
Layout se pri aplikaci zamkne. UnlockStartLayout task (krok 06) ho odemkne 5 min po startu, aby uzivatel mohl dale upravovat.
</div>
</div>
<div class="step-footer">
<span class="step-status">Integrovan do <code>04-default-profile.ps1</code></span>
<div class="comment-widget" data-issue="13"></div>
</div>
</div>
<!-- ============================================================ -->
<p class="section-label">Architektura</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 &ndash; Go GUI (Walk / Win32)</span>
<span class="badge badge-ok">OK</span>
</div>
<div class="step-body">
<table class="items">
<tr class="flag-done"><td>Single binary (go:embed scripty + assets)</td><td><code>embed.go</code> + <code>cmd/xetup/main.go</code>; ~5 MB .exe, zadne externi zavislosti</td></tr>
<tr class="flag-done"><td>Walk GUI &ndash; Win32 nativni ovladky, bez OpenGL</td><td>Funguje na VMware ESXi / SVGA II (Fyne/OpenGL nefungovalo). <code>internal/gui/gui.go</code></td></tr>
<tr class="flag-done"><td>Formular: PC jmeno, popis, product key, profil</td><td>Faze 1 &ndash; config form pred spustenim</td></tr>
<tr class="flag-done"><td>Checklist kroku (on/off per-feature) + nacist / ulozit config.json</td><td>Tlacitka "Nacist config..." a "Ulozit config..." pro per-klient presety</td></tr>
<tr class="flag-done"><td>Live log + prubehovy strip kroku (0&ndash;12)</td><td>Barevne indikatory: · cekajici, &#x25ba; bezici (modra), &#x2713; OK (zelena), &#x2717; chyba (cervena)</td></tr>
<tr class="flag-done"><td>Summary s odpoctem a automatickym restartem</td><td>Faze 3 &ndash; 60s odpocet, tlacitka "Restartovat ted" / "Zrusit restart"</td></tr>
<tr class="flag-done"><td>UAC requireAdministrator (app.manifest + rsrc)</td><td>Windows vyzada elevaci pri spusteni; ComCtl32 v6 + DPI awareness</td></tr>
<tr class="flag-done"><td>PowerShell okno skryte na pozadi</td><td>SysProcAttr.HideWindow = true; PS chyby filtrovane (At line:, CategoryInfo atd.)</td></tr>
<tr class="flag-todo"><td>Self-update: stahnout novou verzi z xetup.x9.cz/dl</td><td>Overit hash pred spustenim</td></tr>
<tr class="flag-future"><td>config.json: per-klient preset na USB</td><td>Jmeno PC prefix, SW seznam, klic &ndash; lezi vedle .exe</td></tr>
</table>
<div class="note">
<strong>Struktura:</strong> <code>cmd/xetup/</code>, <code>internal/config/</code>, <code>internal/gui/</code>, <code>internal/runner/</code><br><br>
<strong>Go zavislosti:</strong> github.com/lxn/walk (Win32 GUI), golang.org/x/sys<br><br>
<strong>Build:</strong> <code>GOOS=windows GOARCH=amd64 go build -ldflags="-H windowsgui" ./cmd/xetup/</code><br>
CI (Forgejo Actions) sestavi a publikuje automaticky pri kazdem push na main.
</div>
</div>
<div class="step-footer">
<span class="step-status">Status: v provozu &ndash; <a href="https://xetup.x9.cz/dl" style="color:var(--blue)">xetup.x9.cz/dl</a></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 &ndash; 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 &rarr; 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>
<!-- ============================================================ -->
<p class="section-label">Nove nastaveni &ndash; pozadavky</p>
<!-- NEW REQUESTS -->
<div class="step" id="new-requests">
<div class="step-header">
<span class="step-num" style="font-size:1.1rem">+</span>
<span class="step-title">Novy pozadavek na automatizaci</span>
<span class="badge badge-new">Pozadavky</span>
</div>
<div class="step-body">
<p style="color:var(--muted);font-size:.84rem;line-height:1.5;margin-bottom:1rem">
Chcete automatizovat neco, co skript zatim neresi?
Napiste pozadavek sem &ndash; ulozi se do repozitare.
Technicky tym ho projde a zaradi do planu.
</p>
<div class="req-list" id="req-list">
<div style="font-size:.8rem;color:var(--muted);font-style:italic">Nacitam pozadavky...</div>
</div>
<div class="req-form" id="req-form">
<input class="req-name" id="req-name" placeholder="Vase jmeno (volitelne)" maxlength="60">
<textarea class="req-text" id="req-text" placeholder="Co by mel automat delat? Popiste konkretne, idealne i proc." rows="3"></textarea>
<button class="req-submit" id="req-submit">Odeslat pozadavek</button>
<span class="req-error" id="req-error"></span>
</div>
</div>
</div>
</div><!-- /content -->
</div><!-- /layout -->
<footer>
&copy; 2026 <a href="https://x9.cz">X9.cz s.r.o.</a>
&nbsp;&middot;&nbsp;
<a href="https://git.xetup.x9.cz/x9/xetup">Forgejo</a>
&nbsp;&middot;&nbsp;
<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 + ' &middot; ' + timeAgo(c.created_at) + '</div>' +
'<div class="comment-body">' + body.replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</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">' +
'&#128172; 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-backinfo',
'step-08': '08-activation',
'step-09': '09-pc-identity',
'step-10': '10-network',
'step-11': '11-dell-update',
'step-12': '12-windows-update',
};
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,'&lt;').replace(/>/g,'&gt;'); }
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) + ' &middot; ' + 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; });
});
});
});
}
// ---- NEW-REQUEST WIDGET (issue #15) ----
(function() {
const ISSUE = 15;
const listEl = document.getElementById('req-list');
const nameEl = document.getElementById('req-name');
const textEl = document.getElementById('req-text');
const submitEl = document.getElementById('req-submit');
const errorEl = document.getElementById('req-error');
if (!listEl) return;
function renderRequests(data) {
listEl.innerHTML = '';
if (!data.length) {
listEl.innerHTML = '<div style="font-size:.8rem;color:var(--muted);font-style:italic;margin-bottom:.5rem">Zatim zadne pozadavky. Bud prvni.</div>';
return;
}
data.forEach(c => {
const m = c.body.match(/^\*\*(.+?)\*\*\n([\s\S]*)$/);
const name = m ? m[1] : (c.user.login === 'xetup-bot' ? 'anon' : c.user.login);
const text = m ? m[2] : c.body;
const el = document.createElement('div');
el.className = 'req-item';
el.innerHTML =
'<div class="req-item-meta">' + name.replace(/</g,'&lt;') + ' &middot; ' + timeAgo(c.created_at) + '</div>' +
'<div class="req-item-body">' + text.replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</div>';
listEl.appendChild(el);
});
}
fetch(API + '/repos/' + REPO + '/issues/' + ISSUE + '/comments', { headers: HEADS })
.then(r => r.json())
.then(d => renderRequests(Array.isArray(d) ? d : []))
.catch(() => { listEl.innerHTML = '<div style="font-size:.8rem;color:var(--red)">Chyba nacitani.</div>'; });
submitEl.addEventListener('click', () => {
const text = textEl.value.trim();
if (!text) { errorEl.textContent = 'Pozadavek nesmi byt prazdny.'; return; }
errorEl.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 => {
textEl.value = ''; nameEl.value = '';
submitEl.disabled = false;
submitEl.textContent = 'Odeslat pozadavek';
// re-fetch to show updated list
return fetch(API + '/repos/' + REPO + '/issues/' + ISSUE + '/comments', { headers: HEADS });
})
.then(r => r.json())
.then(d => renderRequests(Array.isArray(d) ? d : []))
.catch(() => {
errorEl.textContent = 'Chyba odeslani. Zkus znovu.';
submitEl.disabled = false;
submitEl.textContent = 'Odeslat pozadavek';
});
});
})();
enhanceRows();
})();
</script>
</body>
</html>