Add ez-assistant and kerberos service folders

This commit is contained in:
kelin
2026-02-11 14:56:03 -05:00
parent e4e8ae1b87
commit 9ccfb36923
4471 changed files with 746463 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
.markdown {
margin-top: 18px;
line-height: 1.7;
}
.mdx-content > h1:first-of-type {
display: none;
}
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4 {
font-family: var(--font-pixel);
letter-spacing: 0.04em;
line-height: 1.15;
}
.markdown h1 {
font-size: clamp(28px, 4vw, 44px);
margin: 26px 0 10px;
}
.markdown h2 {
font-size: 22px;
margin: 26px 0 10px;
}
.markdown h3 {
font-size: 18px;
margin: 22px 0 8px;
}
.markdown p {
margin: 0 0 14px;
}
.markdown a {
color: var(--link);
text-decoration: none;
border-bottom: 1px dotted color-mix(in oklab, var(--link) 65%, transparent);
}
.markdown a:hover {
color: var(--link2);
border-bottom-color: color-mix(in oklab, var(--link2) 75%, transparent);
}
.markdown hr {
border: 0;
height: 1px;
background: linear-gradient(
90deg,
transparent,
color-mix(in oklab, var(--frame-border) 30%, transparent),
transparent
);
margin: 26px 0;
}
.markdown blockquote {
margin: 18px 0;
padding: 14px 14px;
border-radius: var(--radius-sm);
background: color-mix(in oklab, var(--panel) 70%, transparent);
border-left: 6px solid color-mix(in oklab, var(--accent) 60%, transparent);
color: var(--muted);
}
.markdown ul,
.markdown ol {
margin: 0 0 14px 22px;
}
.markdown li {
margin: 4px 0;
}
.markdown img {
max-width: 100%;
height: auto;
border-radius: 12px;
border: 1px solid color-mix(in oklab, var(--frame-border) 20%, transparent);
box-shadow: 0 12px 0 -8px rgba(0, 0, 0, 0.18);
}
.showcase-link {
position: relative;
display: inline-flex;
align-items: center;
gap: 6px;
}
.showcase-preview {
position: absolute;
left: 50%;
top: 100%;
width: min(420px, 80vw);
padding: 8px;
border-radius: 14px;
background: color-mix(in oklab, var(--panel) 92%, transparent);
border: 1px solid color-mix(in oklab, var(--frame-border) 30%, transparent);
box-shadow: 0 18px 40px -18px rgba(0, 0, 0, 0.55);
transform: translate(-50%, 10px) scale(0.98);
opacity: 0;
visibility: hidden;
pointer-events: none;
z-index: 20;
transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
}
.showcase-preview img {
width: 100%;
height: auto;
border-radius: 10px;
border: 1px solid color-mix(in oklab, var(--frame-border) 25%, transparent);
box-shadow: none;
}
.showcase-link:hover .showcase-preview,
.showcase-link:focus-within .showcase-preview {
opacity: 1;
visibility: visible;
transform: translate(-50%, 6px) scale(1);
}
@media (hover: none) {
.showcase-preview {
display: none;
}
}
.markdown code {
font-family: var(--font-body);
font-size: 0.95em;
padding: 0.15em 0.35em;
border-radius: 8px;
background: color-mix(in oklab, var(--panel) 70%, var(--code-bg));
border: 1px solid color-mix(in oklab, var(--frame-border) 18%, transparent);
}
.markdown pre {
background: var(--code-bg);
color: var(--code-fg);
padding: 14px 14px;
border-radius: var(--radius-sm);
border: 1px solid color-mix(in oklab, var(--frame-border) 18%, transparent);
overflow-x: auto;
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--code-accent) 12%, transparent);
}
.markdown pre code {
background: transparent;
border: 0;
padding: 0;
color: inherit;
}
.markdown table {
width: 100%;
border-collapse: collapse;
margin: 16px 0 22px;
border: 1px solid color-mix(in oklab, var(--frame-border) 20%, transparent);
border-radius: var(--radius-sm);
overflow: hidden;
}
.markdown th,
.markdown td {
padding: 10px 10px;
border-bottom: 1px solid color-mix(in oklab, var(--frame-border) 15%, transparent);
vertical-align: top;
}
.markdown th {
background: color-mix(in oklab, var(--panel2) 85%, transparent);
font-family: var(--font-pixel);
letter-spacing: 0.06em;
}

View File

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 16 16" role="img" aria-label="Pixel lobster">
<rect width="16" height="16" fill="none"/>
<!-- outline -->
<g fill="#3a0a0d">
<rect x="1" y="5" width="1" height="3"/>
<rect x="2" y="4" width="1" height="1"/>
<rect x="2" y="8" width="1" height="1"/>
<rect x="3" y="3" width="1" height="1"/>
<rect x="3" y="9" width="1" height="1"/>
<rect x="4" y="2" width="1" height="1"/>
<rect x="4" y="10" width="1" height="1"/>
<rect x="5" y="2" width="6" height="1"/>
<rect x="11" y="2" width="1" height="1"/>
<rect x="12" y="3" width="1" height="1"/>
<rect x="12" y="9" width="1" height="1"/>
<rect x="13" y="4" width="1" height="1"/>
<rect x="13" y="8" width="1" height="1"/>
<rect x="14" y="5" width="1" height="3"/>
<rect x="5" y="11" width="6" height="1"/>
<rect x="4" y="12" width="1" height="1"/>
<rect x="11" y="12" width="1" height="1"/>
<rect x="3" y="13" width="1" height="1"/>
<rect x="12" y="13" width="1" height="1"/>
<rect x="5" y="14" width="6" height="1"/>
</g>
<!-- body -->
<g fill="#ff4f40">
<rect x="5" y="3" width="6" height="1"/>
<rect x="4" y="4" width="8" height="1"/>
<rect x="3" y="5" width="10" height="1"/>
<rect x="3" y="6" width="10" height="1"/>
<rect x="3" y="7" width="10" height="1"/>
<rect x="4" y="8" width="8" height="1"/>
<rect x="5" y="9" width="6" height="1"/>
<rect x="5" y="12" width="6" height="1"/>
<rect x="6" y="13" width="4" height="1"/>
</g>
<!-- claws -->
<g fill="#ff775f">
<rect x="1" y="6" width="2" height="1"/>
<rect x="2" y="5" width="1" height="1"/>
<rect x="2" y="7" width="1" height="1"/>
<rect x="13" y="6" width="2" height="1"/>
<rect x="13" y="5" width="1" height="1"/>
<rect x="13" y="7" width="1" height="1"/>
</g>
<!-- eyes -->
<g fill="#081016">
<rect x="6" y="5" width="1" height="1"/>
<rect x="9" y="5" width="1" height="1"/>
</g>
<g fill="#f5fbff">
<rect x="6" y="4" width="1" height="1"/>
<rect x="9" y="4" width="1" height="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="420" viewBox="0 0 800 420" role="img" aria-label="padel-cli availability output">
<rect width="800" height="420" rx="24" fill="#0b0f14" />
<rect x="24" y="24" width="752" height="372" rx="18" fill="#0f172a" stroke="#263246" stroke-width="2" />
<text x="48" y="72" fill="#9ca3af" font-size="18" font-family="Fragment Mono, ui-monospace, SFMono-Regular, Menlo, monospace">
<tspan x="48" dy="0">$ padel search --location "Barcelona" --date 2026-01-08 --time 18:00-22:00</tspan>
<tspan x="48" dy="30" fill="#e5e7eb">Available courts (3):</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">- Vall d'Hebron 19:00 Court 2 (90m) EUR 34</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">- Badalona 20:30 Court 1 (60m) EUR 28</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">- Gracia 21:00 Court 4 (90m) EUR 36</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="420" viewBox="0 0 800 420" role="img" aria-label="GoHome Roborock status output">
<rect width="800" height="420" rx="24" fill="#0b0f14" />
<rect x="24" y="24" width="752" height="372" rx="18" fill="#111827" stroke="#263246" stroke-width="2" />
<text x="48" y="72" fill="#9ca3af" font-size="18" font-family="Fragment Mono, ui-monospace, SFMono-Regular, Menlo, monospace">
<tspan x="48" dy="0">$ gohome roborock status --device "Living Room"</tspan>
<tspan x="48" dy="30" fill="#e5e7eb">Device: Roborock Q Revo</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">State: cleaning (zone)</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">Battery: 78%</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">Dustbin: 42%</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">Water tank: 61%</tspan>
<tspan x="48" dy="28" fill="#e5e7eb">Last clean: 2026-01-06 19:42</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@@ -0,0 +1,473 @@
:root {
--font-body: "Fragment Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-pixel: "Pixelify Sans", system-ui, sans-serif;
--radius: 14px;
--radius-sm: 10px;
--border: 2px;
--shadow-px: 0 0 0 var(--border) var(--frame-border), 0 12px 0 -4px rgba(0, 0, 0, 0.25);
--scanline-size: 6px;
--scanline-opacity: 0.08;
}
html[data-theme="light"],
html[data-theme="auto"] {
--bg0: #fbf4e7;
--bg1: #fffaf0;
--panel: #fffdf8;
--panel2: #fff6e5;
--text: #10221c;
--muted: #3e5a50;
--faint: #6b7f77;
--link: #0f6b4c;
--link2: #ff4f40;
--accent: #ff4f40;
--accent2: #00b88a;
--frame-border: #1b2e27;
--code-bg: #0b1713;
--code-fg: #eafff6;
--code-accent: #67ff9b;
}
html[data-theme="dark"] {
--bg0: #0b1a22;
--bg1: #0a1720;
--panel: #0e231f;
--panel2: #102a24;
--text: #c9eadc;
--muted: #8ab8aa;
--faint: #699b8d;
--link: #6fe8c7;
--link2: #ff7b63;
--accent: #ff4f40;
--accent2: #5fdfa2;
--frame-border: #6fbfa8;
--code-bg: #091814;
--code-fg: #d7f5e8;
--code-accent: #5fdfa2;
}
@media (prefers-color-scheme: dark) {
html[data-theme="auto"] {
--bg0: #0b1a22;
--bg1: #0a1720;
--panel: #0e231f;
--panel2: #102a24;
--text: #c9eadc;
--muted: #8ab8aa;
--faint: #699b8d;
--link: #6fe8c7;
--link2: #ff7b63;
--accent: #ff4f40;
--accent2: #5fdfa2;
--frame-border: #6fbfa8;
--code-bg: #091814;
--code-fg: #d7f5e8;
--code-accent: #5fdfa2;
}
}
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
min-height: 100%;
}
body {
margin: 0;
font-family: var(--font-body);
color: var(--text);
background:
radial-gradient(1100px 700px at 20% -10%, color-mix(in oklab, var(--accent) 18%, transparent), transparent 55%),
radial-gradient(900px 600px at 95% 10%, color-mix(in oklab, var(--accent2) 14%, transparent), transparent 60%),
radial-gradient(900px 600px at 50% 120%, color-mix(in oklab, var(--link) 10%, transparent), transparent 55%),
linear-gradient(180deg, var(--bg0), var(--bg1));
overflow-x: hidden;
}
body::before,
body::after {
display: none;
}
.skip-link {
position: absolute;
top: 10px;
left: 10px;
z-index: 10;
padding: 10px 12px;
border-radius: 999px;
background: var(--panel);
color: var(--text);
text-decoration: none;
transform: translateY(-130%);
box-shadow: var(--shadow-px);
}
.skip-link:focus {
transform: translateY(0);
outline: none;
}
.shell {
position: sticky;
top: 0;
z-index: 100;
padding: 22px 16px 10px;
}
.shell__frame {
max-width: 1120px;
margin: 0 auto;
border-radius: var(--radius);
background:
linear-gradient(180deg, color-mix(in oklab, var(--panel2) 88%, transparent), color-mix(in oklab, var(--panel) 92%, transparent));
box-shadow: var(--shadow-px);
border: var(--border) solid var(--frame-border);
overflow: hidden;
}
.shell__titlebar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 14px 12px;
background:
linear-gradient(90deg, color-mix(in oklab, var(--accent) 26%, transparent), transparent 42%),
linear-gradient(180deg, color-mix(in oklab, var(--panel) 90%, #000 10%), color-mix(in oklab, var(--panel2) 90%, #000 10%));
}
.brand {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.brand__logo {
flex: 0 0 auto;
width: 40px;
height: 40px;
image-rendering: pixelated;
filter: drop-shadow(0 2px 0 color-mix(in oklab, var(--frame-border) 80%, transparent));
}
.brand__text {
min-width: 0;
}
.brand__name {
font-family: var(--font-pixel);
letter-spacing: 0.14em;
font-weight: 700;
font-size: 18px;
line-height: 1.1;
text-shadow: 0 1px 0 color-mix(in oklab, var(--frame-border) 55%, transparent);
}
.brand__hint {
margin-top: 2px;
font-size: 12px;
color: var(--muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.titlebar__actions {
display: flex;
align-items: center;
gap: 10px;
}
.titlebar__cta {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px 8px 10px;
border-radius: 12px;
background:
linear-gradient(140deg, color-mix(in oklab, var(--accent) 10%, transparent), transparent 60%),
color-mix(in oklab, var(--panel) 92%, transparent);
border: var(--border) solid color-mix(in oklab, var(--frame-border) 80%, transparent);
color: var(--text);
text-decoration: none;
box-shadow:
0 6px 0 -3px rgba(0, 0, 0, 0.25),
inset 0 0 0 1px color-mix(in oklab, var(--panel2) 55%, transparent);
}
.titlebar__cta:hover {
border-color: color-mix(in oklab, var(--accent2) 45%, transparent);
box-shadow:
0 0 0 2px color-mix(in oklab, var(--accent2) 30%, transparent),
0 6px 0 -3px rgba(0, 0, 0, 0.25);
}
.titlebar__cta:active {
transform: translateY(1px);
box-shadow: 0 4px 0 -3px rgba(0, 0, 0, 0.25);
}
.titlebar__cta:focus-visible {
outline: 3px solid color-mix(in oklab, var(--accent2) 60%, transparent);
outline-offset: 2px;
}
.titlebar__cta--accent {
background:
linear-gradient(120deg, color-mix(in oklab, var(--accent) 22%, transparent), transparent 70%),
color-mix(in oklab, var(--panel) 88%, transparent);
border-color: color-mix(in oklab, var(--accent) 60%, var(--frame-border));
}
.titlebar__cta-label {
font-family: var(--font-pixel);
letter-spacing: 0.12em;
font-size: 12px;
text-transform: uppercase;
}
.titlebar__cta-meta {
display: inline-flex;
align-items: center;
justify-content: center;
height: 20px;
padding: 0 8px;
border-radius: 999px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
background: var(--code-bg);
color: var(--code-accent);
border: 1px solid color-mix(in oklab, var(--code-accent) 30%, transparent);
box-shadow: inset 0 0 12px color-mix(in oklab, var(--code-accent) 25%, transparent);
}
.theme-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 9px 10px;
border-radius: 12px;
background: color-mix(in oklab, var(--panel) 92%, transparent);
border: var(--border) solid var(--frame-border);
color: var(--text);
cursor: pointer;
box-shadow: 0 6px 0 -3px rgba(0, 0, 0, 0.25);
user-select: none;
}
.theme-toggle:active {
transform: translateY(1px);
box-shadow: 0 4px 0 -3px rgba(0, 0, 0, 0.25);
}
.theme-toggle__key {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 28px;
border-radius: 9px;
background: var(--code-bg);
color: var(--code-accent);
border: 1px solid color-mix(in oklab, var(--code-accent) 30%, transparent);
font-weight: 700;
font-size: 12px;
letter-spacing: 0.06em;
text-shadow: 0 0 14px color-mix(in oklab, var(--code-accent) 55%, transparent);
}
.theme-toggle__label {
font-family: var(--font-pixel);
letter-spacing: 0.12em;
font-size: 12px;
text-transform: uppercase;
}
.theme-toggle:focus-visible {
outline: 3px solid color-mix(in oklab, var(--accent2) 60%, transparent);
outline-offset: 2px;
}
.shell__nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 10px 14px 12px;
border-top: 1px solid color-mix(in oklab, var(--frame-border) 25%, transparent);
background: linear-gradient(180deg, transparent, color-mix(in oklab, var(--panel2) 78%, transparent));
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.nav__link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 999px;
text-decoration: none;
color: var(--text);
background: color-mix(in oklab, var(--panel) 85%, transparent);
border: 1px solid color-mix(in oklab, var(--frame-border) 20%, transparent);
}
.nav__link:hover {
border-color: color-mix(in oklab, var(--accent2) 45%, transparent);
box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent2) 30%, transparent);
}
.nav__chev {
color: var(--accent);
font-weight: 700;
}
.shell__status {
display: inline-flex;
align-items: center;
gap: 10px;
color: var(--muted);
font-size: 12px;
white-space: nowrap;
}
.status__dot {
width: 10px;
height: 10px;
border-radius: 999px;
background: radial-gradient(circle at 30% 30%, var(--accent2), color-mix(in oklab, var(--accent2) 30%, #000));
box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent2) 18%, transparent), 0 0 18px color-mix(in oklab, var(--accent2) 50%, transparent);
}
.content {
padding: 18px 16px 48px;
}
.terminal {
position: relative;
max-width: 1120px;
margin: 0 auto;
border-radius: var(--radius);
border: var(--border) solid var(--frame-border);
background: linear-gradient(180deg, color-mix(in oklab, var(--panel) 92%, transparent), color-mix(in oklab, var(--panel2) 86%, transparent));
box-shadow: var(--shadow-px);
padding: 18px 16px 16px;
}
.terminal__prompt {
display: block;
padding: 10px 12px;
border-radius: var(--radius-sm);
background: var(--code-bg);
color: var(--code-fg);
border: 1px solid color-mix(in oklab, var(--frame-border) 18%, transparent);
overflow-x: auto;
white-space: nowrap;
}
.prompt__user {
color: var(--code-accent);
text-shadow: 0 0 16px color-mix(in oklab, var(--code-accent) 52%, transparent);
}
.prompt__host {
color: color-mix(in oklab, var(--code-fg) 92%, var(--accent2));
}
.prompt__path {
color: color-mix(in oklab, var(--code-fg) 78%, var(--link));
}
.prompt__cmd {
margin-left: 8px;
color: color-mix(in oklab, var(--code-fg) 90%, var(--accent));
}
.terminal__summary {
margin: 12px 2px 0;
color: var(--muted);
font-size: 14px;
line-height: 1.5;
}
.terminal__meta {
margin: 12px 2px 0;
padding: 12px 12px;
border-radius: var(--radius-sm);
border: 1px dashed color-mix(in oklab, var(--frame-border) 25%, transparent);
background: color-mix(in oklab, var(--panel) 76%, transparent);
color: var(--faint);
font-size: 13px;
}
.terminal__meta summary {
cursor: pointer;
font-family: var(--font-pixel);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text);
}
.terminal__meta ul {
margin: 10px 0 0 18px;
}
.terminal__footer {
margin-top: 22px;
padding-top: 16px;
border-top: 1px solid color-mix(in oklab, var(--frame-border) 20%, transparent);
color: var(--muted);
font-size: 13px;
}
.footer__actions {
margin-top: 14px;
display: flex;
justify-content: flex-end;
}
.terminal__footer a {
color: var(--link);
text-decoration: none;
border-bottom: 1px dotted color-mix(in oklab, var(--link) 55%, transparent);
}
.terminal__footer a:hover {
color: var(--link2);
border-bottom-color: color-mix(in oklab, var(--link2) 75%, transparent);
}
.footer__hint {
margin-top: 8px;
color: var(--faint);
}
kbd {
font-family: var(--font-body);
font-size: 0.9em;
padding: 2px 6px;
border-radius: 8px;
border: 1px solid color-mix(in oklab, var(--frame-border) 18%, transparent);
background: color-mix(in oklab, var(--panel) 65%, transparent);
box-shadow: 0 6px 0 -4px rgba(0, 0, 0, 0.18);
}
@supports not (color: color-mix(in oklab, black, white)) {
body::before,
body::after {
opacity: 0.2;
}
}

View File

@@ -0,0 +1,55 @@
const THEME_STORAGE_KEY = "moltbot:theme";
function safeGet(key) {
try {
return localStorage.getItem(key);
} catch {
return null;
}
}
function safeSet(key, value) {
try {
localStorage.setItem(key, value);
} catch {
// ignore
}
}
function preferredTheme() {
const stored = safeGet(THEME_STORAGE_KEY);
if (stored === "light" || stored === "dark") return stored;
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function applyTheme(theme) {
document.documentElement.dataset.theme = theme;
const toggle = document.querySelector("[data-theme-toggle]");
const label = document.querySelector("[data-theme-label]");
if (toggle instanceof HTMLButtonElement) toggle.setAttribute("aria-pressed", theme === "dark" ? "true" : "false");
if (label) label.textContent = theme === "dark" ? "dark" : "light";
}
function toggleTheme() {
const current = document.documentElement.dataset.theme === "dark" ? "dark" : "light";
const next = current === "dark" ? "light" : "dark";
safeSet(THEME_STORAGE_KEY, next);
applyTheme(next);
}
applyTheme(preferredTheme());
document.addEventListener("click", (event) => {
const target = event.target;
const button = target instanceof Element ? target.closest("[data-theme-toggle]") : null;
if (button) toggleTheme();
});
document.addEventListener("keydown", (event) => {
if (event.key === "F2") {
event.preventDefault();
toggleTheme();
}
});