Initial commit: DOSSIER Hugo website
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
# Hugo build output
|
||||
/public/
|
||||
/resources/_gen/
|
||||
.hugo_build.lock
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Backups
|
||||
*.bak
|
||||
@@ -0,0 +1,683 @@
|
||||
/* ─────────────────────────────────────────────────────────────
|
||||
DOSSIER — Theme-Overrides für Hextra
|
||||
Petrol-Grün auf dunklem Grund (Architektur-Studio-Ästhetik)
|
||||
───────────────────────────────────────────────────────────── */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400&display=swap');
|
||||
|
||||
/* Krungthep — DOSSIER-Display-Font (Mac-System-Font lokal gebundelt) */
|
||||
@font-face {
|
||||
font-family: 'Krungthep';
|
||||
src: url('/fonts/Krungthep.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* — Primary-HSL: Petrol #5fa896 — */
|
||||
:root {
|
||||
--primary-hue: 165deg;
|
||||
--primary-saturation: 28%;
|
||||
--primary-lightness: 51%;
|
||||
|
||||
/* DOSSIER-Palette */
|
||||
--dossier-bg: #0e1413;
|
||||
--dossier-surface: #161d1c;
|
||||
--dossier-surface2: #1b2422;
|
||||
--dossier-dark: #0a100f;
|
||||
--dossier-dark2: #1f2826;
|
||||
--dossier-accent: #5fa896;
|
||||
--dossier-accent-2: #4a8a7c;
|
||||
--dossier-accent-3: #2f5d54;
|
||||
--dossier-text: #e6e8e6;
|
||||
--dossier-text-2: #b4bcb8;
|
||||
--dossier-text-3: #828a86;
|
||||
--dossier-text-4: #5a625e;
|
||||
--dossier-border: #232b29;
|
||||
--dossier-border-2: #2e3633;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--primary-hue: 165deg;
|
||||
--primary-saturation: 38%;
|
||||
--primary-lightness: 51%;
|
||||
--color-dark: var(--dossier-bg);
|
||||
}
|
||||
|
||||
/* — Body & Backgrounds — */
|
||||
.dark body {
|
||||
background: var(--dossier-bg);
|
||||
color: var(--dossier-text);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.dark ::selection {
|
||||
background: rgba(95, 168, 150, 0.30);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* — Typografie — Headings serifig, Body monospaced — */
|
||||
.dark .hextra-toc,
|
||||
.dark .content,
|
||||
.dark .prose {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.dark h1, .dark h2, .dark h3, .dark h4,
|
||||
.hextra-home h1, .hextra-home h2, .hextra-home h3,
|
||||
.hx\:text-2xl, .hx\:text-3xl, .hx\:text-4xl, .hx\:text-5xl, .hx\:text-6xl {
|
||||
font-family: 'Playfair Display', Georgia, serif !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Navbar-Logo: DOSSIER in Krungthep/Archivo Black — nur navbar! */
|
||||
.hextra-navbar-title,
|
||||
nav .hextra-navbar-title,
|
||||
nav .hextra-max-navbar-width .hx\:font-bold {
|
||||
font-family: Krungthep, 'Archivo Black', sans-serif !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
font-weight: 900 !important;
|
||||
}
|
||||
|
||||
/* — Navbar dunkel mit Border — */
|
||||
.dark .nav-container {
|
||||
background: rgba(14, 20, 19, 0.85) !important;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--dossier-border) !important;
|
||||
}
|
||||
|
||||
.dark .nav-container-blur {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* — Sidebar — */
|
||||
.dark aside.sidebar-container,
|
||||
.dark .sidebar-container {
|
||||
background: var(--dossier-bg) !important;
|
||||
border-right: 1px solid var(--dossier-border);
|
||||
}
|
||||
|
||||
.dark .sidebar-container a {
|
||||
color: var(--dossier-text-2);
|
||||
}
|
||||
|
||||
.dark .sidebar-container a:hover {
|
||||
color: var(--dossier-accent);
|
||||
background: var(--dossier-surface);
|
||||
}
|
||||
|
||||
.dark .sidebar-active-item,
|
||||
.dark .sidebar-container .sidebar-active-item {
|
||||
background: rgba(95, 168, 150, 0.12) !important;
|
||||
color: var(--dossier-accent) !important;
|
||||
border-color: rgba(95, 168, 150, 0.20) !important;
|
||||
}
|
||||
|
||||
/* — Links — */
|
||||
.dark a {
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.dark .content a,
|
||||
.dark .prose a {
|
||||
color: var(--dossier-accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dark .content a:hover,
|
||||
.dark .prose a:hover {
|
||||
color: #6db5a4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Hextra-Card-Links (<cards>-Shortcode) sollen NIE unterstrichen werden */
|
||||
.hextra-card,
|
||||
.hextra-card:hover,
|
||||
.hextra-card:focus,
|
||||
.hextra-card:active,
|
||||
.hextra-card *,
|
||||
.hextra-card:hover * {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
a.hextra-card:hover,
|
||||
a.hextra-card:focus {
|
||||
outline: none;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/* — Buttons / Hero-Button — */
|
||||
.hextra-hero-button {
|
||||
background: var(--dossier-accent) !important;
|
||||
color: #0a1715 !important;
|
||||
border-radius: 20px !important;
|
||||
padding: 13px 28px !important;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important;
|
||||
font-size: 11px !important;
|
||||
font-weight: 500 !important;
|
||||
letter-spacing: 0.07em !important;
|
||||
text-transform: uppercase !important;
|
||||
box-shadow: 0 6px 18px rgba(95, 168, 150, 0.22);
|
||||
transition: all 0.18s;
|
||||
}
|
||||
|
||||
.hextra-hero-button:hover {
|
||||
background: #6db5a4 !important;
|
||||
box-shadow: 0 10px 26px rgba(95, 168, 150, 0.32);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* — DOSSIER Hero-Buttons — eigene Pills im RAPPORT-Layout-Pattern — */
|
||||
.dossier-hero-actions {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.dossier-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
border-radius: 999px;
|
||||
padding: 14px 30px;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none !important;
|
||||
cursor: pointer;
|
||||
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Primary — Petrol-Akzent mit Tiefe */
|
||||
.dossier-btn-primary {
|
||||
background: var(--dossier-accent);
|
||||
color: #0a1715 !important;
|
||||
border: 1px solid var(--dossier-accent);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.10) inset,
|
||||
0 2px 4px rgba(0,0,0,0.30),
|
||||
0 8px 20px rgba(0,0,0,0.40),
|
||||
0 16px 40px rgba(95,168,150,0.22);
|
||||
}
|
||||
|
||||
.dossier-btn-primary:hover {
|
||||
background: #6db5a4;
|
||||
border-color: #6db5a4;
|
||||
color: #0a1715 !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.10) inset,
|
||||
0 4px 8px rgba(0,0,0,0.36),
|
||||
0 14px 28px rgba(0,0,0,0.42),
|
||||
0 24px 56px rgba(95,168,150,0.32);
|
||||
}
|
||||
|
||||
.dossier-btn-primary:active {
|
||||
transform: translateY(0);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255,255,255,0.10) inset,
|
||||
0 2px 4px rgba(0,0,0,0.30),
|
||||
0 4px 12px rgba(0,0,0,0.36);
|
||||
}
|
||||
|
||||
/* Secondary — Outline */
|
||||
.dossier-btn-secondary {
|
||||
background: transparent;
|
||||
color: var(--dossier-text-2) !important;
|
||||
border: 1.5px solid var(--dossier-border-2);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.20);
|
||||
}
|
||||
|
||||
.dossier-btn-secondary:hover {
|
||||
background: rgba(255,255,255,0.04);
|
||||
border-color: var(--dossier-text-3);
|
||||
color: var(--dossier-text) !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 6px 14px rgba(0,0,0,0.28),
|
||||
0 12px 28px rgba(0,0,0,0.20);
|
||||
}
|
||||
|
||||
.dossier-btn-secondary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* — Hero-Badge — */
|
||||
.hextra-badge {
|
||||
background: var(--dossier-surface) !important;
|
||||
border: 1px solid var(--dossier-border-2) !important;
|
||||
border-radius: 20px !important;
|
||||
padding: 5px 14px !important;
|
||||
font-size: 10px !important;
|
||||
letter-spacing: 0.12em !important;
|
||||
color: var(--dossier-text-3) !important;
|
||||
text-transform: uppercase !important;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important;
|
||||
}
|
||||
|
||||
/* — Feature-Cards — */
|
||||
.hextra-feature-card {
|
||||
background: var(--dossier-surface) !important;
|
||||
border: 1px solid var(--dossier-border) !important;
|
||||
border-radius: 14px !important;
|
||||
transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s !important;
|
||||
}
|
||||
|
||||
.hextra-feature-card:hover {
|
||||
border-color: var(--dossier-border-2) !important;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 32px rgba(0, 0, 0, 0.40), 0 2px 8px rgba(0, 0, 0, 0.28) !important;
|
||||
}
|
||||
|
||||
.hextra-feature-card h3 {
|
||||
font-family: 'Playfair Display', serif !important;
|
||||
color: var(--dossier-text) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.hextra-feature-card p {
|
||||
color: var(--dossier-text-3) !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 1.8 !important;
|
||||
}
|
||||
|
||||
/* — Generic Cards (Hextra <cards> shortcode) — */
|
||||
.dark .hextra-card {
|
||||
background: var(--dossier-surface);
|
||||
border: 1px solid var(--dossier-border);
|
||||
border-radius: 12px;
|
||||
transition: border-color 0.2s, transform 0.2s;
|
||||
}
|
||||
|
||||
.dark .hextra-card:hover {
|
||||
border-color: var(--dossier-accent-2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* — Code-Blocks — nur innerer pre/highlight bekommt Box, äusserer Wrapper bleibt transparent — */
|
||||
.dark .hextra-code-block {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.dark .hextra-code-block pre,
|
||||
.dark .hextra-code-block .highlight,
|
||||
.dark .highlight pre,
|
||||
.dark pre.chroma {
|
||||
background: var(--dossier-dark) !important;
|
||||
border: 1px solid var(--dossier-border) !important;
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
|
||||
/* Doppelten Border vermeiden wenn pre & highlight beide gestyled werden */
|
||||
.dark .hextra-code-block .highlight {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.dark code, .dark pre, .dark pre code, .dark kbd, .dark samp, .dark tt {
|
||||
font-family: ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', monospace !important;
|
||||
}
|
||||
|
||||
.dark code {
|
||||
color: var(--dossier-accent) !important;
|
||||
background: rgba(95, 168, 150, 0.08) !important;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.dark pre code {
|
||||
color: var(--dossier-text-2) !important;
|
||||
background: transparent !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* — Callouts — */
|
||||
.dark .hextra-callout {
|
||||
background: var(--dossier-surface) !important;
|
||||
border-color: var(--dossier-border-2) !important;
|
||||
}
|
||||
|
||||
/* — Footer — */
|
||||
.dark .hextra-footer,
|
||||
.dark footer {
|
||||
background: var(--dossier-dark);
|
||||
border-top: 1px solid var(--dossier-border-2);
|
||||
color: var(--dossier-text-4);
|
||||
}
|
||||
|
||||
.dark .hextra-footer a {
|
||||
color: var(--dossier-text-3);
|
||||
}
|
||||
|
||||
.dark .hextra-footer a:hover {
|
||||
color: var(--dossier-accent);
|
||||
}
|
||||
|
||||
/* — TOC — */
|
||||
.dark .hextra-toc a {
|
||||
color: var(--dossier-text-3);
|
||||
}
|
||||
|
||||
.dark .hextra-toc a:hover,
|
||||
.dark .hextra-toc .active {
|
||||
color: var(--dossier-accent) !important;
|
||||
}
|
||||
|
||||
/* Hextra-Sticky-Bottom-Fades (TOC "Nach oben" + Sidebar-Footer) — */
|
||||
/* der hardcoded #111-Fade passt nicht zum petrol-dunklen DOSSIER-Bg */
|
||||
.hextra-toc div:has(> #backToTop),
|
||||
[data-toggle-animation] {
|
||||
background: var(--dossier-bg) !important;
|
||||
box-shadow: none !important;
|
||||
border-top-color: var(--dossier-border) !important;
|
||||
}
|
||||
|
||||
/* — Search — */
|
||||
.dark .hextra-search-wrapper input {
|
||||
background: var(--dossier-surface) !important;
|
||||
border: 1px solid var(--dossier-border) !important;
|
||||
color: var(--dossier-text) !important;
|
||||
}
|
||||
|
||||
.dark .hextra-search-wrapper input:focus {
|
||||
border-color: var(--dossier-accent-2) !important;
|
||||
}
|
||||
|
||||
/* — Tabellen — */
|
||||
.dark table {
|
||||
border-color: var(--dossier-border) !important;
|
||||
}
|
||||
|
||||
.dark thead {
|
||||
background: var(--dossier-surface) !important;
|
||||
}
|
||||
|
||||
.dark th, .dark td {
|
||||
border-color: var(--dossier-border) !important;
|
||||
}
|
||||
|
||||
/* — Hero-Section Hintergrund-Glow — */
|
||||
.hextra-home::before,
|
||||
body.hextra-home::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 10%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 720px;
|
||||
height: 720px;
|
||||
background: radial-gradient(circle, rgba(95, 168, 150, 0.08) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* — DOSSIER-Logo-Karte (für hero) — Pill-Form — */
|
||||
.dossier-logo-card {
|
||||
background: var(--dossier-surface);
|
||||
border: 1px solid var(--dossier-border-2);
|
||||
border-radius: 999px;
|
||||
padding: 28px 64px 26px;
|
||||
display: inline-block;
|
||||
box-shadow: 6px 0 20px rgba(0, 0, 0, 0.40), 0 6px 16px rgba(0, 0, 0, 0.28);
|
||||
text-align: center;
|
||||
margin: 0 auto 32px;
|
||||
}
|
||||
|
||||
.dossier-logo-text {
|
||||
font-family: Krungthep, 'Archivo Black', sans-serif;
|
||||
font-size: 42px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--dossier-text);
|
||||
line-height: 0.95;
|
||||
}
|
||||
|
||||
.dossier-logo-sub {
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.15em;
|
||||
color: var(--dossier-accent);
|
||||
text-transform: uppercase;
|
||||
margin-top: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* — Hero-Meta-Pillen — */
|
||||
.dossier-meta {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 32px;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.dossier-meta-item {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--dossier-text-4);
|
||||
text-transform: uppercase;
|
||||
padding: 0 10px;
|
||||
border-right: 1px solid var(--dossier-border);
|
||||
}
|
||||
|
||||
.dossier-meta-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* — Status-Badges (in Arbeit / Geplant / Stabil) — */
|
||||
.dossier-status {
|
||||
display: inline-block;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
padding: 2px 9px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.dossier-status.active {
|
||||
background: rgba(95, 168, 150, 0.12);
|
||||
color: var(--dossier-accent);
|
||||
border: 1px solid rgba(95, 168, 150, 0.20);
|
||||
}
|
||||
|
||||
.dossier-status.planned {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--dossier-text-3);
|
||||
border: 1px solid var(--dossier-border-2);
|
||||
}
|
||||
|
||||
.dossier-status.stable {
|
||||
background: rgba(95, 168, 150, 0.20);
|
||||
color: #e6e8e6;
|
||||
border: 1px solid var(--dossier-accent-2);
|
||||
}
|
||||
|
||||
/* — Stack-Bar (am Footer) — */
|
||||
.dossier-stack-bar {
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid var(--dossier-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 32px;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.dossier-stack-label {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--dossier-text-4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dossier-stack-items {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dossier-stack-item {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--dossier-text-3);
|
||||
background: var(--dossier-surface);
|
||||
border: 1px solid var(--dossier-border);
|
||||
border-radius: 6px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
/* — Reduce default content max-width slightly for monospace lines — */
|
||||
.content article {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────────────────
|
||||
HOME-HERO — zentriertes Layout wie auf der alten Website
|
||||
───────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Inhalt der home Page zentrieren (override hextra-home.html) */
|
||||
.hextra-home {
|
||||
align-items: center !important;
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Subtitle zentriert, schmaler max-width, Playfair Display wie alt */
|
||||
.hextra-home > p,
|
||||
.hextra-home .not-prose.hx\:text-xl {
|
||||
font-family: 'Playfair Display', serif !important;
|
||||
font-size: clamp(16px, 2.2vw, 24px) !important;
|
||||
font-weight: 400 !important;
|
||||
color: var(--dossier-text-2) !important;
|
||||
max-width: 560px !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
line-height: 1.55 !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Buttons immer in einer zentrierten Zeile */
|
||||
.hextra-home > div.hx\:flex,
|
||||
.hextra-home > .hx\:flex {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Hero-Button matches DOSSIER design */
|
||||
.hextra-home .hextra-hero-button,
|
||||
a.hextra-hero-button {
|
||||
background: var(--dossier-accent);
|
||||
color: #0a1715 !important;
|
||||
border-radius: 20px;
|
||||
padding: 13px 28px;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif !important;
|
||||
font-size: 11px !important;
|
||||
font-weight: 500 !important;
|
||||
letter-spacing: 0.07em !important;
|
||||
text-transform: uppercase !important;
|
||||
box-shadow: 0 6px 18px rgba(95, 168, 150, 0.22);
|
||||
transition: all 0.18s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
/* Feature-Grid wieder linksbündig (text in cards) */
|
||||
.hextra-home .hextra-feature-grid,
|
||||
.hextra-home .dossier-stack-bar {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Cards & Stack-Bar Inhalte: text wieder normal ausrichten */
|
||||
.hextra-home .hextra-feature-card * {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dossier-stack-bar {
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Section-Heading & Eyebrow vor dem Feature-Grid auch zentrieren */
|
||||
.hextra-home h2,
|
||||
.hextra-home > div > h2 {
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* DOSSIER-Logo bleibt zentriert */
|
||||
.hextra-home .dossier-logo-card {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Hero-Badge zentrieren */
|
||||
.hextra-home .hextra-badge,
|
||||
.hextra-home > p:has(.hextra-badge) {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
/* Hero-Background-Glow — sanfter Petrol-Halo */
|
||||
body:has(.hextra-home)::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 720px;
|
||||
height: 720px;
|
||||
background: radial-gradient(circle, rgba(95, 168, 150, 0.10) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* DOSSIER-Logo grösser und expressiver */
|
||||
.dossier-logo-text {
|
||||
font-family: 'Krungthep', 'Archivo Black', sans-serif !important;
|
||||
font-size: 48px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--dossier-text);
|
||||
line-height: 0.95;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Navbar-Logo — DOSSIER-Schriftzug in Krungthep, etwas grösser */
|
||||
.hextra-navbar-title,
|
||||
nav a[href="/"] span,
|
||||
nav [class*="font-bold"] {
|
||||
font-family: 'Krungthep', 'Archivo Black', sans-serif !important;
|
||||
letter-spacing: -0.02em !important;
|
||||
font-weight: 400 !important;
|
||||
font-size: 23px !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
/* DOSSIER-Meta-Pillen zentriert */
|
||||
.dossier-meta {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: DOSSIER
|
||||
layout: hextra-home
|
||||
toc: false
|
||||
---
|
||||
|
||||
{{< hextra/hero-badge >}}
|
||||
<div class="hx:w-2 hx:h-2 hx:rounded-full" style="background: #5fa896; box-shadow: 0 0 8px rgba(95,168,150,0.55);"></div>
|
||||
<span>Pre-Release 0.1.0 · Aktiv in Entwicklung</span>
|
||||
{{< /hextra/hero-badge >}}
|
||||
|
||||
<div class="hx:mt-8 hx:mb-6">
|
||||
<div class="dossier-logo-card">
|
||||
<div class="dossier-logo-text">DOSSIER</div>
|
||||
<div class="dossier-logo-sub">Rhino 8 Plugin · Teil von OpenBureau</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-12">
|
||||
{{< hextra/hero-subtitle >}}
|
||||
Ein Design-Studio für Rhino — smarte Bauteile, Schnitte, Plan-Vorlagen, Materialien, Symbole und SIA-416-Räume. Modell-Qualität und Planabgabe direkt aus 3D, ohne BIM-Cloud.
|
||||
{{< /hextra/hero-subtitle >}}
|
||||
</div>
|
||||
|
||||
<div class="dossier-hero-actions">
|
||||
<a href="/docs/" class="dossier-btn dossier-btn-primary">Erste Schritte</a>
|
||||
<a href="https://git.kgva.ch/karim/DOSSIER" class="dossier-btn dossier-btn-secondary">Quellcode ↗</a>
|
||||
</div>
|
||||
|
||||
<div class="dossier-meta hx:mb-12">
|
||||
<span class="dossier-meta-item">AGPL-3.0</span>
|
||||
<span class="dossier-meta-item">Rhino 8+</span>
|
||||
<span class="dossier-meta-item">macOS & Windows</span>
|
||||
<span class="dossier-meta-item">CPython 3.9</span>
|
||||
<span class="dossier-meta-item">Open Source</span>
|
||||
</div>
|
||||
|
||||
<div class="hx:mt-12 hx:mb-8 hx:text-center">
|
||||
<p style="font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--dossier-accent); margin-bottom: 12px;">ANSATZ</p>
|
||||
<h2 style="font-family: 'Playfair Display', serif; font-size: clamp(26px, 3.5vw, 40px); font-weight: 400; line-height: 1.2; margin: 0 auto 16px; max-width: 580px;">Aus Rhino ein vollständiges Design-Studio machen</h2>
|
||||
<p style="font-size: 13px; line-height: 1.8; color: var(--dossier-text-3); max-width: 620px; margin: 0 auto;">Rhino ist stark in der Geometrie — Dossier ergänzt das, was bis zur Planabgabe fehlt: smarte Bauteile, Schnitte und Ansichten, projektweite Material- und Symbol-Library, SIA-Räume, Layout-Set und Beschriftung. Kein BIM-Datenbank-Overhead, keine Cloud — die `.3dm` bleibt die einzige Quelle.</p>
|
||||
</div>
|
||||
|
||||
{{< hextra/feature-grid >}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Smart-Elemente"
|
||||
subtitle="Wände, Decken, Dächer, Öffnungen, Treppen und Tragwerk als parametrische Bauteile mit Source ↔ Volume Pattern. Wand v1 mit Polyline-Achse, Chain-Anchor und nativen Rhino-Grips — Cmd+Z über alle Joints stabil."
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(95,168,150,0.10),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Geschosse & Ebenen"
|
||||
subtitle="Multi-Geschoss-Clipping mit Top-View-Z-Guard und Snap-Bar pro Geschoss. Layer-Hierarchie (10_GRUNDRISSE/EG/WÄNDE, …) wird automatisch aufgebaut und mit den Smart-Elementen verknüpft."
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(95,168,150,0.08),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Schnitte & Ansichten"
|
||||
subtitle="Schnitt-Perspektive mit voller Section-Style-API — Hatch-Pattern für Schnittflächen, Hidden-Line-Removal, Schnittlinien-Stil pro Layer. Anlass für die CPython-3-Migration."
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(95,168,150,0.12),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Project-Settings (5 Tabs)"
|
||||
subtitle="Zentraler Dialog für Voreinstellungen + Projektdaten, Materialien (PBR + Texturen), Linientypen, Schraffuren und Symbole. Daten leben im .3dm — keine externen Konfigs."
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(95,168,150,0.10),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Material-Library"
|
||||
subtitle="ArchiCAD-style List/Detail mit Auto-Regen, Materialvorschau und Material/Ebene-Separation. Linetypes per .lin- und Hatches per .pat-Import direkt ins Projekt."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Symbol-Library"
|
||||
subtitle="2D+3D Pair-Files mit Satellite-Picker. Multi-Format-Import (.3dm/.dwg/.obj/.fbx/.dae/.stl), automatische Base64-PNG-Thumbnails und CRUD direkt aus Rhino."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="SIA-416 Flächen"
|
||||
subtitle="Räume mit HNF / NNF / FF / VF-Kategorisierung. Im Standardmodus transparent, im Flächenmodus farbliche Überlagerung. CSV-Export und aggregierte Schemata direkt aus der Klassifikation."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Layouts & PDF-Export"
|
||||
subtitle="Plan-Sets aus Named Views, Titelblock-Vorlagen mit Bürodaten aus dem Projekt, Stapelexport als Vektor-PDF oder PNG. Benennung und Reihenfolge folgen dem Planverzeichnis."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Massstab & Display-Modes"
|
||||
subtitle="Viewport-Skala 1:N mit Auto-DPI über CoreGraphics, PlotWeight-Synchronisation und Display-Mode-Kopplung an Ausschnitte. Display-Mode Dossier-Plan (Hidden-Line, weisser Hintergrund, Section-Hatch) in aktiver Entwicklung."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Gestaltung & Overrides"
|
||||
subtitle="Regelbasierte Overrides (Bedingung → Aktion), Plot-Sync für Farbe / Lineweight / Linetype / Hatch und Preset-Verwaltung cross-doc."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Ausschnitte & Kamera"
|
||||
subtitle="Viewport-Snapshots (Kamera + Display + Layer-Sichtbarkeit) als wiederherstellbare States. Verknüpfung an Massstab und Layout."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Swisstopo & OSM"
|
||||
subtitle="Schweizer Landeskarten-Tiles und OSM-Daten als georeferenzierter Hintergrund. Adress-Prefill aus Projektdaten, Terrain-Import als Höhenmodell und m.ü.M-Bezug für die Situation."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="DOSSIER-Text Editor"
|
||||
subtitle="WYSIWYG Rich-Text Editor für TextEntities mit RTF-Export. Fett / Italic / Underline / Strike, Tab-Stops und Multi-Font-Support im Modellraum."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Dimensionen"
|
||||
subtitle="Bemassungs-Panel für Wand-Dicken, Geschoss-Höhen und Öffnungs-Masse. Konsistente Stile, automatische Aktualisierung bei Modell-Änderungen."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Werkzeuge"
|
||||
subtitle="Batch-Operationen für wiederkehrende Aufgaben — Layer-Bereinigung, Section-Style-Reset, Joint-Cache-Clear, Material-Index-Refresh."
|
||||
>}}
|
||||
|
||||
{{< hextra/feature-card
|
||||
title="Tauri-Launcher"
|
||||
subtitle="Standalone-App für Projekt-Verwaltung, Settings-Sync, Auto-Updates und Window-Layouts. System-Tray, file-based IPC zu Rhino — beide Apps laufen unabhängig voneinander."
|
||||
icon="sparkles"
|
||||
>}}
|
||||
|
||||
{{< /hextra/feature-grid >}}
|
||||
|
||||
<div class="dossier-stack-bar">
|
||||
<span class="dossier-stack-label">Aufgebaut auf</span>
|
||||
<div class="dossier-stack-items">
|
||||
<span class="dossier-stack-item">RhinoCommon</span>
|
||||
<span class="dossier-stack-item">CPython 3.9</span>
|
||||
<span class="dossier-stack-item">React + Vite</span>
|
||||
<span class="dossier-stack-item">Eto.Forms WebView</span>
|
||||
<span class="dossier-stack-item">Tauri 2</span>
|
||||
<span class="dossier-stack-item">AGPL-3.0</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: Dokumentation
|
||||
linkTitle: Dokumentation
|
||||
next: docs/erste-schritte
|
||||
weight: 1
|
||||
sidebar:
|
||||
open: true
|
||||
---
|
||||
|
||||
Willkommen zur DOSSIER-Dokumentation. DOSSIER ist ein **Rhino 8 Plugin** für architektonisches Entwerfen mit smarten Bauteilen — Teil von **OpenBureau**.
|
||||
|
||||
{{< cards >}}
|
||||
{{< card link="erste-schritte" title="Erste Schritte" icon="play" subtitle="Installation, Setup, erster Workflow." >}}
|
||||
{{< card link="../features" title="Features" icon="cube" subtitle="Alle Panels und ihre Funktionen." >}}
|
||||
{{< card link="../launcher" title="Launcher" icon="desktop-computer" subtitle="Standalone-App für Projekte und Settings." >}}
|
||||
{{< card link="architektur" title="Architektur" icon="template" subtitle="Module, Bridge-Pattern, Sticky-Storage." >}}
|
||||
{{< card link="roadmap" title="Roadmap" icon="map" subtitle="Erledigt, in Arbeit, geplant." >}}
|
||||
{{< card link="../faq" title="FAQ" icon="question-mark-circle" subtitle="Häufige Fragen und Antworten." >}}
|
||||
{{< /cards >}}
|
||||
|
||||
## Auf einen Blick
|
||||
|
||||
Dossier bündelt den gesamten architektonischen Workflow in Rhino:
|
||||
|
||||
- **Modellieren** mit smarten Bauteilen (Source ↔ Volume Pattern, Wand v1 mit Polyline-Achse)
|
||||
- **Strukturieren** über Geschosse, Multi-Geschoss-Clipping und eine automatische Layer-Hierarchie
|
||||
- **Schneiden & Anschauen** mit der vollen Section-Style-API und Schnitt-Perspektive
|
||||
- **Material- & Symbol-verwalten** projektzentral aus den Project-Settings (PBR-Texturen, 2D+3D-Symbol-Pairs)
|
||||
- **Auswerten** nach SIA 416 (HNF / NNF / FF / VF)
|
||||
- **Darstellen** mit Massstab, Display-Modes und Overrides
|
||||
- **Beschriften** mit Bemassung, Raumstempeln und Rich-Text
|
||||
- **Ausgeben** als Plan-Set in PDF oder PNG
|
||||
|
||||
## Laufzeit
|
||||
|
||||
DOSSIER läuft als **CPython 3.9** über Rhinos neuen Python-3-Engine. Die UI wird in einem React-WebView-Panel über Rhinos Eto.Forms-Layer eingebettet (inline via `LoadHtml`).
|
||||
|
||||
Lese die [Architektur-Übersicht](architektur) für Details zum Bridge-Pattern und den Sticky-Storage-Konventionen.
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: Architektur
|
||||
linkTitle: Architektur
|
||||
weight: 2
|
||||
---
|
||||
|
||||
DOSSIER ist als **Plugin-Verbund** aufgebaut: jedes Feature lebt in einem eigenen Modul, alle teilen sich ein gemeinsames Bridge-Pattern für die React-↔-Python-Kommunikation.
|
||||
|
||||
## Module-Map
|
||||
|
||||
| Modul | LOC | Rolle |
|
||||
|----------------------|------:|----------------------------------------------------------------|
|
||||
| `panel_base.py` | 697 | Fundament: BaseBridge, WebView-IO, Panel-Registration, Icons |
|
||||
| `rhinopanel.py` | 798 | EBENEN — Zeichnungsebenen, Layer-Hierarchie, Presets |
|
||||
| `elemente.py` | 7'244 | ELEMENTE — Wände, Decken, Öffnungen, Treppen, Tragwerk, Räume |
|
||||
| `gestaltung.py` | 1'635 | GESTALTUNG — Selektions-Attribute (Farbe, Lineweight, Hatch) |
|
||||
| `oberleiste.py` | 981 | OBERLEISTE — Top-Bar, Display, Massstab, Snaps, Settings |
|
||||
| `massstab.py` | 1'096 | MASSSTAB — Viewport 1:N, Auto-DPI, PlotWeight |
|
||||
| `overrides.py` | 797 | Engine — regelbasierte Overrides (Bedingung → Aktion) |
|
||||
| `overrides_panel.py` | 226 | UI für Overrides-Engine |
|
||||
| `ausschnitte.py` | 708 | AUSSCHNITTE — Viewport-Snapshots (Kamera + Display + Layer) |
|
||||
| `dimensionen.py` | 613 | DIMENSIONEN — Bemassung (Wand-Dicken, Geschoss-Höhen, …) |
|
||||
| `layouts.py` | 749 | LAYOUTS — Plan-Editor, Titelblock, PDF-Export |
|
||||
| `werkzeuge.py` | 58 | WERKZEUGE — Quick-Tools (Batch) |
|
||||
| `layer_builder.py` | 436 | Helper — Ebenen-Hierarchie aufbauen, Sublayer-Sync |
|
||||
| `startup.py` | 136 | Init — liest `dossier.project.json`, lädt Module selektiv |
|
||||
|
||||
## Tragende Patterns
|
||||
|
||||
### Bridge-Pattern (Pflicht für jedes Panel)
|
||||
|
||||
```python
|
||||
class MyBridge(panel_base.BaseBridge):
|
||||
def __init__(self):
|
||||
panel_base.BaseBridge.__init__(self, "mymodule")
|
||||
|
||||
def _on_ready(self):
|
||||
self.send("STATE_SYNC", {...}) # WebView fertig geladen
|
||||
|
||||
def handle(self, data):
|
||||
t = data.get("type")
|
||||
if t == "ACTION": self._do_action()
|
||||
|
||||
def _bridge_factory():
|
||||
b = MyBridge()
|
||||
_install_listeners(b) # Rhino-Events registrieren
|
||||
return b
|
||||
|
||||
panel_base.register_and_open(
|
||||
"mymodule", "MY PANEL", PANEL_GUID_STR,
|
||||
_bridge_factory,
|
||||
icon_spec=("foundation", "#5fa896"), # Material-Icon + Petrol
|
||||
min_size=(400, 300),
|
||||
)
|
||||
```
|
||||
|
||||
### React ↔ Python Kommunikation
|
||||
|
||||
- **React → Python**: `document.title = "RHINOMSG::{json}"` — gepollt im Idle-Handler
|
||||
- **Python → React**: `bridge.send(type, payload)` → `webview.ExecuteScript("window.onRhinoMessage(…)")`
|
||||
- **Chunking**: Messages > 200 KB werden in `panel_base.handle_raw` automatisch gesplittet und reassembliert. Subklassen kümmern sich nicht drum.
|
||||
|
||||
### Source ↔ Volume Pattern
|
||||
|
||||
Jedes Smart-Element hat:
|
||||
|
||||
1. eine **Source-Geometrie** (Achse / Outline / Punkt) — vom User editierbar
|
||||
2. ein generiertes **Volume** (Brep) — automatisch regeneriert bei Source-Änderungen
|
||||
|
||||
Beispiel Wand: Source = Achs-Linie, Volume = Brep mit Dicke × Höhe.
|
||||
|
||||
### Sticky-Storage (Cross-Module-State)
|
||||
|
||||
Konventionen für `sc.sticky`-Keys:
|
||||
|
||||
- `"{modul}_bridge"` — Bridge-Instanz
|
||||
- `"{modul}_listeners"` — Bool-Flag: Listener bereits registriert?
|
||||
- `"_dossier_*"` — globale States (z.B. `_dossier_joints_cache`)
|
||||
- `"{modul}_*_cache"` — Modul-Cache
|
||||
|
||||
### Listener-Hookup (Idempotent)
|
||||
|
||||
```python
|
||||
def _install_listeners(bridge):
|
||||
flag = "mymodule_listeners"
|
||||
sc.sticky["mymodule_bridge"] = bridge
|
||||
if sc.sticky.get(flag): return # Schon registriert
|
||||
Rhino.RhinoApp.Idle += _on_idle
|
||||
Rhino.RhinoDoc.ActiveDocumentChanged += _on_view_change
|
||||
sc.sticky[flag] = True
|
||||
```
|
||||
|
||||
## Datenhaltung
|
||||
|
||||
- **Geschosse** in `doc.Strings["dossier_ebenen"]` als JSON
|
||||
- **Smart-Elemente** als Rhino-Objekte mit UserStrings — `dossier_element_id`, `dossier_element_type`, …
|
||||
- **Section-Styles** über `Rhino.DocObjects.SectionStyle()` + `layer.SetCustomSectionStyle()`
|
||||
- **Settings**: `~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json`
|
||||
|
||||
Eine `.3dm`-Datei bleibt eine Datei — keine externen Datenbanken.
|
||||
|
||||
## Layer-Hierarchie
|
||||
|
||||
```text
|
||||
10_GRUNDRISSE
|
||||
└── EG
|
||||
├── 20_WAENDE
|
||||
├── 30_DECKEN
|
||||
├── 31_DAECHER
|
||||
└── 40_TREPPEN
|
||||
└── 1OG (gleiche Sublayer)
|
||||
20_SCHNITTE
|
||||
30_ANSICHTEN
|
||||
00_RASTER · 01_VERMESSUNG · 40_SITUATION · 90_REFERENZEN · 99_KONSTRUKTION
|
||||
```
|
||||
|
||||
## Cross-Module-Pfade
|
||||
|
||||
| Sender → Empfänger | Trigger | Effekt |
|
||||
|-----------------------------|-------------------------------|---------------------------------------------------------|
|
||||
| `rhinopanel` → `elemente` | Apply von Ebenen-Struktur | `elemente_bridge._regenerate_all()` regeneriert Wände/Decken |
|
||||
| `elemente` → `rhinopanel` | Wand/Decken-Delete | `ebenen_bridge_ref._send_state()` |
|
||||
| `oberleiste` → `overrides` | Preset-Auswahl in Topbar | `overrides_bridge._send_state()` |
|
||||
| `massstab` ↔ `ausschnitte` | Viewport-/Zoom-Wechsel | Bi-direktional Skala lesen / setzen |
|
||||
| `gestaltung` ↔ `rhinopanel` | Hatch-Pattern auf Selektion | Pattern + Scale + Rotation-Signatur vergleichen |
|
||||
|
||||
## Konventionen
|
||||
|
||||
- **Python-Identifier ohne Umlaute** — `ue/oe/ae` statt `ü/ö/ä` in Code-Bezeichnern, Layer-Codes, UserString-Keys. UI-Strings dürfen Umlaute.
|
||||
- **`LoadHtml`-inline** statt `file://`-URL — Rhinos WKWebView blockiert sonst `<script type="module">` durch CORS.
|
||||
- **TextEntity-RTF** — Rhinos eingebauter Parser unterstützt nur `\b \i \ul \strike \fN \tab {}` plus Newline-via-`\par`. **Kein `\fs`** (eine TextEntity hat global eine Schriftgröße).
|
||||
- **Sticky-Reads** immer mit `is not None`-Check.
|
||||
|
||||
## Launcher-Anbindung
|
||||
|
||||
Der **Dossier-Launcher** (`launcher/`) ist eine separate Tauri-2-App. IPC zu Rhino läuft **dateibasiert**, nicht über Socket:
|
||||
|
||||
- Settings: `~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json`
|
||||
- Live-Push: `pendingApplyLayout`-Key, `oberleiste.tick_idle()` pollt und cleart
|
||||
- System-Tray mit Quick-Open der letzten 5 Projekte
|
||||
|
||||
Rhino läuft ohne Launcher, Launcher läuft ohne Rhino.
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: Erste Schritte
|
||||
linkTitle: Erste Schritte
|
||||
weight: 1
|
||||
next: docs/architektur
|
||||
---
|
||||
|
||||
DOSSIER in **5 Minuten** zum Laufen bringen.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
| Tool | Version |
|
||||
|--------|------------------------------------------|
|
||||
| Rhino | 8 (Mac · Windows getestet, primär Mac) |
|
||||
| Python | CPython 3.9 (Rhino 8 Script-Editor-Engine) |
|
||||
| Node | ≥ 20 (nur für UI-Builds — fertige Distribution braucht das nicht) |
|
||||
|
||||
Optional — für den Standalone-Launcher:
|
||||
|
||||
| Tool | Version |
|
||||
|---------------|------------------------------------------------|
|
||||
| Rust toolchain| ≥ 1.77 (`rustup`) |
|
||||
| Build-Tools | siehe [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/) |
|
||||
|
||||
## Installation
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Repository klonen
|
||||
|
||||
```bash
|
||||
git clone https://git.kgva.ch/karim/DOSSIER.git
|
||||
cd DOSSIER
|
||||
npm install
|
||||
```
|
||||
|
||||
### Frontend bauen
|
||||
|
||||
```bash
|
||||
npm run build # → dist/index.html (inline-fähig)
|
||||
```
|
||||
|
||||
### Plugin in Rhino starten
|
||||
|
||||
In Rhino 8 den Initialisierer über `_ScriptEditor` öffnen — die Datei `rhino/startup.py` laden und auf **Run** klicken. Sie liest die `dossier.project.json` neben der `.3dm` und lädt nur die dort gelisteten Module (oder alle, falls keine Projekt-Datei vorhanden).
|
||||
|
||||
Für automatisches Laden bei jedem Rhino-Start: Rhino-Optionen → *General* → *Run these commands every time a model is opened*:
|
||||
|
||||
```text
|
||||
_-RunPythonScript "<DOSSIER_PFAD>/rhino/startup.py"
|
||||
```
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**`<DOSSIER_PFAD>`** durch den absoluten Pfad zum geklonten Repository ersetzen — z.B. `/Users/dein_user/STUDIO/DOSSIER` auf macOS oder `C:\Users\dein_user\STUDIO\DOSSIER` auf Windows. Rhino expandiert `~` in diesem Kontext nicht.
|
||||
{{< /callout >}}
|
||||
|
||||
Der Launcher trägt diesen Eintrag automatisch mit dem korrekten Pfad ein.
|
||||
|
||||
### Auto-Reset nach Änderungen
|
||||
|
||||
Bei Python-Änderungen die Panels neu laden:
|
||||
|
||||
```text
|
||||
_RunPythonScript <DOSSIER_PFAD>/rhino/_reset_panels.py
|
||||
```
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## Erster Workflow
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Projekt anlegen
|
||||
|
||||
In Rhino `DossierInit` aufrufen. Bürodaten, Projektnummer und Phasenbezeichnung werden im `.3dm` hinterlegt und in jedem Plankopf eingesetzt.
|
||||
|
||||
### Geschosse definieren
|
||||
|
||||
Im **EBENEN**-Panel Geschosse mit Name (`EG`, `1OG`, …), Höhe und OKFF anlegen. Dossier baut daraus die Layer-Hierarchie:
|
||||
|
||||
```text
|
||||
10_GRUNDRISSE
|
||||
└── EG
|
||||
├── 20_WAENDE
|
||||
├── 30_DECKEN
|
||||
├── 31_DAECHER
|
||||
└── 40_TREPPEN
|
||||
└── 1OG (gleiche Sublayer)
|
||||
```
|
||||
|
||||
### Smart-Elemente platzieren
|
||||
|
||||
Im **ELEMENTE**-Panel das gewünschte Bauteil wählen — Wand, Decke, Öffnung, Treppe, Tragwerk oder Raum. Source-Geometrie zeichnen (Achse, Outline, Punkt), Volumen wird automatisch generiert.
|
||||
|
||||
### Plan generieren
|
||||
|
||||
**Named Views** für Grundrisse, Schnitte und Axonometrien speichern. Im **LAYOUTS**-Panel Vorlage wählen, Views auf das Blatt ziehen, Plannummer vergeben. Dossier erzeugt das Layout mit Titelblock und passt den Massstab automatisch an.
|
||||
|
||||
### Export
|
||||
|
||||
Stapelexport aller Pläne als PDF oder PNG aus dem **LAYOUTS**-Panel. Optional mit Plannummer-Suffix im Dateinamen und Vektordaten für die Druckfreigabe.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## Wo geht's weiter?
|
||||
|
||||
{{< cards >}}
|
||||
{{< card link="../features" title="Features" subtitle="Alle Panels im Detail." >}}
|
||||
{{< card link="architektur" title="Architektur" subtitle="Module, Bridges, Sticky-Storage." >}}
|
||||
{{< card link="../faq" title="FAQ" subtitle="Häufige Fragen." >}}
|
||||
{{< /cards >}}
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Roadmap
|
||||
linkTitle: Roadmap
|
||||
weight: 3
|
||||
toc: true
|
||||
---
|
||||
|
||||
DOSSIER ist ein **Design-Studio für Rhino**, keine BIM-Cloud. Der Fokus liegt auf **3D-Modell-Qualität** und sauberer Planabgabe direkt aus dem Modell — keine externe Datenbank, keine Zwangs-Synchronisation, keine Cloud-Lock-in.
|
||||
|
||||
Diese Seite hält den Status der grösseren Bauteile fest. Detail-Issues und Bugfixes laufen auf [Gitea](https://git.kgva.ch/karim/DOSSIER/issues).
|
||||
|
||||
## Erledigt
|
||||
|
||||
Die folgenden Bauteile sind im aktiven Einsatz und Teil der Pre-Release 0.1.0:
|
||||
|
||||
### Wand-System v1
|
||||
|
||||
Polyline-Wand mit **Chain-Anchor**, **Cmd+Z** über alle Joints stabil, native Rhino-**Grips** für Achs-Editierung. Siehe [Smart-Elemente](../../features/smart-elemente).
|
||||
|
||||
### Schnitte & Ansichten
|
||||
|
||||
Schnitt-Perspektive plus die volle **SectionStyle-API** (Schnittlinien-Stil, Section-Hatch, Hidden-Line-Removal). War der **konkrete Anlass** für die Migration von IronPython 2.7 zu CPython 3. Siehe [Schnitte & Ansichten](../../features/schnitte-ansichten).
|
||||
|
||||
### Geschoss-Management
|
||||
|
||||
**Multi-Geschoss-Clipping** mit konfigurierbaren Modi, **Top-View Z-Guard** gegen versehentliche Z-Drifts und **Snap-Bar** pro Geschoss. Siehe [Geschosse & Ebenen](../../features/geschosse).
|
||||
|
||||
### Project-Settings (5 Tabs)
|
||||
|
||||
Zentraler Dialog für Voreinstellungen + Projektdaten, Materialien, Linientypen, Schraffuren und Symbole. Siehe [Project-Settings](../../features/project-settings).
|
||||
|
||||
### Material/Library-System
|
||||
|
||||
**ArchiCAD-style List/Detail**-UI, **Auto-Regen** über die Smart-Element-Hierarchie, **`.lin`/`.pat`-Import** für Linetypes und Hatches, bewusste **Material/Ebene-Separation**. Siehe [Material-Library](../../features/materialien).
|
||||
|
||||
### Symbol-Library — Phase A + B
|
||||
|
||||
**2D+3D Pair-Files**, **Satellite-Picker** als persistenter Floating-Window, **Multi-Format-Import** (`.3dm` · `.dwg` · `.obj` · `.fbx` · `.dae` · `.stl`), **Auto-Thumbnails** als Base64-PNG und volle **CRUD**-Operationen. Siehe [Symbol-Library](../../features/symbol-library).
|
||||
|
||||
### Swisstopo-Integration
|
||||
|
||||
**Adress-Prefill** aus den Projektdaten, **Terrain-Import** aus dem AlMo-DOM-Layer und **m.ü.M** in der Plankopf-Variable. Siehe [Swisstopo & OSM](../../features/swisstopo-osm).
|
||||
|
||||
### Launcher (Tauri 2)
|
||||
|
||||
Standalone-App für Projekt-Management, **Auto-Update** über `tauri-plugin-updater`, **System-Tray** mit Quick-Open und **file-based IPC** zu Rhino. Siehe [Launcher](../../launcher).
|
||||
|
||||
## In Arbeit
|
||||
|
||||
### Display-Mode "Dossier Plan"
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
Game-Changer für Plan-Qualität direkt aus 3D — kein Umweg über separate 2D-CAD-Dateien.
|
||||
{{< /callout >}}
|
||||
|
||||
Drei kombinierte Effekte:
|
||||
|
||||
- **Hidden-Line-Removal** über die aktive Section-Plane (Rhino-natives Verfahren)
|
||||
- **Weisser Hintergrund**, Layer-Display-Farben werden zu reinem Schwarz remappt
|
||||
- **Section-Hatch** aus der Layer-Property-Tabelle gerendert (siehe [Schnitte & Ansichten](../../features/schnitte-ansichten))
|
||||
|
||||
Ziel: Wettbewerbs- und Konkurrenz-Pläne ohne Plot-Konvertierung über AutoCAD oder ArchiCAD. Siehe [Massstab & Display-Modes](../../features/massstab).
|
||||
|
||||
## Geplant
|
||||
|
||||
### Raumstempel-Redesign (4 Stufen)
|
||||
|
||||
Der aktuelle Raum-Stempel zeigt Bezeichnung + Fläche, hat aber einen **Wert-Bug** beim Refresh nach Outline-Änderung. Geplant:
|
||||
|
||||
1. **Stufe 1 — Wert-Bug fixen** — Centroid-Berechnung und Fläche werden synchron neu gelesen, Cache-Invalidation greift bei Polyline-Replace.
|
||||
2. **Stufe 2 — Massstäblich-Modus** — Stempel-Schrift folgt dem aktiven Massstab (1:50 → grössere Schrift als 1:100), nicht der Welt-Geometrie.
|
||||
3. **Stufe 3 — Settings-Dialog** — pro Projekt definieren, welche Felder am Stempel erscheinen (Nr · Bezeichnung · SIA · Fläche · Höhe · Material).
|
||||
4. **Stufe 4 — Wettbewerb-Features** — alternative Stempel-Sets für Wettbewerbsabgaben (anonymisierte Codes, vereinfachte Geometrie).
|
||||
|
||||
### Custom Linetype-Editor
|
||||
|
||||
Visueller **Pattern-Editor** für `.lin`-kompatible Linetypes — Drag-Handles für Strich/Lücke-Längen, Live-Preview im Massstab des aktiven Layouts. Der `.lin`-Import existiert bereits (siehe [Materialien](../../features/materialien)); der Editor schreibt in denselben Datentyp.
|
||||
|
||||
**User-Pause** — kommt nach dem Display-Mode "Dossier Plan".
|
||||
|
||||
### PBR-Erweiterungen
|
||||
|
||||
Drei konkrete Lücken im aktuellen Material-Editor:
|
||||
|
||||
- **Separate Roughness-Textur** — heute nur Roughness-Slider, geplant ist eine zusätzliche Roughness-Map (PNG, Graustufen)
|
||||
- **UV-Rotation** — Texturen können bisher nur skaliert werden, nicht rotiert
|
||||
- **Bump-Strength-Slider** — Normal-Map-Intensität soll feinjustierbar werden, statt nur ein/aus
|
||||
|
||||
### Library Phase C — Cloud-Sync
|
||||
|
||||
Symbol-Library erweitern um **Team-Sharing** über GitHub-Releases:
|
||||
|
||||
```text
|
||||
Bürobibliothek-Repo (GitHub)
|
||||
├── releases/v1.4.0/
|
||||
│ ├── symbols/sofa-3sitz.3dm
|
||||
│ ├── symbols/sofa-3sitz.2d.3dm
|
||||
│ └── manifest.json (semver, thumbnails, kategorien)
|
||||
```
|
||||
|
||||
Im PROJECT-SETTINGS-Symbole-Tab wird die Bürobibliothek-URL gesetzt, DOSSIER pullt periodisch Releases und mergt sie mit der projekt-lokalen Library. Pull-Request-Workflow für neue Symbole vom Team.
|
||||
|
||||
### Satellite-Windows-Restyle
|
||||
|
||||
Alle Satellite-Floating-Windows (Symbol-Picker, Material-Picker, Hatch-Picker, …) auf einheitliches **Pill-Style** umstellen — runde Ecken, weicher Schatten, einheitliche Header-Höhe. Konsistente UX über alle Picker-Tools.
|
||||
|
||||
## Strategischer Anker
|
||||
|
||||
DOSSIER bleibt:
|
||||
|
||||
- **Lokal** — `.3dm` + `dossier.project.json` ist alles, kein Server zwingend nötig
|
||||
- **Schweizer-Standard-aware** — SIA 102 / SIA 416, Swisstopo, m.ü.M direkt eingebaut
|
||||
- **AGPL-3.0** — Open Source, keine Telemetrie, keine versteckten Kosten
|
||||
- **Rhino-nativ** — kein eigenes Datei-Format, alle Daten in Rhinos eigenen Tabellen (Materials, Linetypes, Hatches, UserStrings)
|
||||
|
||||
Was DOSSIER **nicht** ist:
|
||||
|
||||
- Keine BIM-Cloud, kein zentraler Server
|
||||
- Keine IFC-Schema-Konformität (es ist kein BIM-Tool)
|
||||
- Kein Ersatz für ArchiCAD / Revit — Dossier bündelt Rhino-Workflows, ersetzt aber keine Statik-Software, kein Mengen-Auswertungs-Tool, keine Quantity-Take-Off
|
||||
|
||||
Wenn dir ein Feature fehlt, das in diesen Rahmen passt → [Issue auf Gitea](https://git.kgva.ch/karim/DOSSIER/issues).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: FAQ
|
||||
linkTitle: FAQ
|
||||
weight: 4
|
||||
toc: true
|
||||
---
|
||||
|
||||
Häufige Fragen zu DOSSIER. Bei Bugs oder weiteren Fragen → [Issue auf Gitea](https://git.kgva.ch/karim/DOSSIER).
|
||||
|
||||
## Für wen ist Dossier gedacht?
|
||||
|
||||
Für **Architekturbüros**, die Rhino bereits im Entwurf und in der Ausführungsplanung einsetzen und den gesamten Weg bis zur fertigen Planabgabe im Modell halten möchten — ohne Umweg über separate CAD-Programme.
|
||||
|
||||
## Was ist die Idee?
|
||||
|
||||
Rhino aus der reinen Modellierumgebung in ein **vollständiges Design-Studio** überführen. Smart-Elemente, Plan-Verwaltung, Vorlagen, Beschriftung und Ausgabe gehören in einem Architekturprojekt zusammen — Dossier bündelt sie an einem Ort. Wo Konventionen aus der Branche existieren (SIA 416, Layer-Naming), werden sie kuratiert übernommen, statt das Rad neu zu erfinden.
|
||||
|
||||
## Welche Rhino-Version wird unterstützt?
|
||||
|
||||
**Rhino 8** für **macOS** (primär getestet) und **Windows** (untestet, aber kompatibel). Eine Portierung auf ältere Versionen ist nicht geplant — Dossier verwendet die neue CPython-3-Engine und Section-Style-APIs, die erst mit Rhino 8 verfügbar sind.
|
||||
|
||||
## Ist Dossier kostenlos?
|
||||
|
||||
**Ja.** Der gesamte Quellcode steht unter **GNU AGPL-3.0-or-later**. Keine Telemetrie, keine versteckten Kosten. Eigene Builds aus dem Repo oder die fertige Distribution.
|
||||
|
||||
## Wie ersetzt Dossier den klassischen Layout-Workflow?
|
||||
|
||||
Dossier baut **auf** Rhinos Layout-Konzept auf und legt darüber die projektbezogene Verwaltung, die in einem vollständigen Plan-Workflow fehlt: Plan-Sets, zentrale Vorlagen, Titelblock-Master und Stapelexport. Manueller Layout-Aufbau pro Blatt entfällt.
|
||||
|
||||
## Wie funktioniert die SIA-416-Flächenauswertung?
|
||||
|
||||
Jeder Raum trägt eine **Kategorie** (HNF, NNF, FF, VF) als Metadatum. Diese Information bleibt für die normale Plandarstellung **unsichtbar** — die Räume sind im Standardmodus transparent. Erst ein eigener **Flächen-Anzeigemodus** überlagert die Geometrie mit den definierten Kategoriefarben.
|
||||
|
||||
Die Klassifikation selbst dient primär der **Schema- und Listenerzeugung**: Flächentabellen, aggregierte HNF/NNF/FF/VF-Summen und der Raum-CSV-Export greifen direkt darauf zu. Siehe [Räume (SIA 416)](../features/raeume-sia416).
|
||||
|
||||
## Wo werden die Plandaten gespeichert?
|
||||
|
||||
Im **`.3dm`-Dokument selbst**, als zusätzliche Metadaten an Named Views, Layouts und Layern. Es entstehen **keine externen Datenbanken** — eine Datei bleibt eine Datei. Backup-Strategie ist trivial: `.3dm` kopieren.
|
||||
|
||||
## Welche Python-Runtime läuft?
|
||||
|
||||
**CPython 3.9** über Rhinos neue Script-Editor-Engine (Rhino 8). Die Migration von IronPython 2.7 ist abgeschlossen (verifiziert 2026-05-17 auf Mac Rhino 8.31). Verifikation in Rhino:
|
||||
|
||||
```text
|
||||
_RunPythonScript <DOSSIER_PFAD>/rhino/startup.py
|
||||
# Erste Zeile sollte zeigen: [STARTUP] Python: 3.x ...
|
||||
```
|
||||
|
||||
## Ist Dossier stabil genug für den Produktivbetrieb?
|
||||
|
||||
**Noch nicht.** Aktuell befindet sich Dossier in der **Pre-Release-Phase 0.1.0**. Funktionen können sich ändern, Bugs sind möglich. Eigene Backups der `.3dm`-Dateien sind empfohlen. Smart-Elemente in echten Projekten haben aber bereits 6+ Monate produktiven Einsatz hinter sich.
|
||||
|
||||
## Werden Wände und Decken bei Modell-Änderungen automatisch nachgeführt?
|
||||
|
||||
**Ja.** Das Source-↔-Volume-Pattern triggert Regeneration bei jeder Source-Geometrie-Änderung. Wand-Joints, Decken-Aussparungen und Öffnungs-Cutouts werden mitgeführt — ohne manuelles Refresh.
|
||||
|
||||
Caches haben jedoch bekannte Stale-Risiken (siehe [Werkzeuge](../features/werkzeuge)) — Undo/Redo invalidiert den Joint-Cache aktuell **nicht** automatisch.
|
||||
|
||||
## Kann ich zum Projekt beitragen?
|
||||
|
||||
**Ja.** Issues und Pull Requests sind willkommen auf [Gitea](https://git.kgva.ch/karim/DOSSIER). Besonders wertvoll sind:
|
||||
|
||||
- konkrete Bug-Reports mit Minimal-`.3dm`
|
||||
- Workflow-Verbesserungen aus dem realen Büroalltag
|
||||
- Titelblock-Vorlagen für andere Büros
|
||||
- Übersetzungen / Internationalisierung
|
||||
|
||||
Lies vorher die [Architektur-Übersicht](../docs/architektur).
|
||||
|
||||
## Wo kriege ich Hilfe?
|
||||
|
||||
| Kanal | Verwendung |
|
||||
|--------------------|-------------------------------------------|
|
||||
| **Gitea Issues** | Bugs, Feature-Wünsche, allgemeine Fragen |
|
||||
| **karimgabrielevarano.xyz** | Entwickler-Kontakt |
|
||||
| **GitHub Discussions** | (nicht eingerichtet) |
|
||||
|
||||
## Warum Open Source?
|
||||
|
||||
Bürowissen sollte nicht in proprietären Tools eingesperrt sein. Plan-Workflows sind in der Branche gut etabliert — ein Tool, das diese Konventionen sauber umsetzt, gehört allen.
|
||||
|
||||
AGPL-3.0 stellt sicher, dass Verbesserungen wieder ins Projekt fliessen.
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Features
|
||||
linkTitle: Features
|
||||
weight: 2
|
||||
sidebar:
|
||||
open: true
|
||||
---
|
||||
|
||||
Alle DOSSIER-Panels im Überblick. Jedes Feature ist ein eigenes Modul mit React-UI und Python-Backend.
|
||||
|
||||
{{< cards >}}
|
||||
{{< card link="geschosse" title="Geschosse & Ebenen" icon="template" subtitle="Layer-Hierarchie, Multi-Geschoss-Clipping, Snap-Bar, Top-View Z-Guard." >}}
|
||||
{{< card link="smart-elemente" title="Smart-Elemente" icon="cube" subtitle="Wände v1 (Polyline + Chain-Anchor + Grips) · Decken · Dächer · Öffnungen · Treppen · Tragwerk." >}}
|
||||
{{< card link="schnitte-ansichten" title="Schnitte & Ansichten" icon="scissors" subtitle="Schnitt-Perspektive, Section-Style-API, Hidden-Line, Hatch-Pattern." >}}
|
||||
{{< card link="project-settings" title="Project-Settings" icon="cog" subtitle="5 Tabs — Voreinstellungen, Materialien, Linientypen, Hatches, Symbole." >}}
|
||||
{{< card link="materialien" title="Material-Library" icon="color-swatch" subtitle="PBR + Texturen, .lin/.pat-Import, Material/Ebene-Separation, Auto-Regen." >}}
|
||||
{{< card link="symbol-library" title="Symbol-Library" icon="collection" subtitle="2D+3D Pair-Files, Satellite-Picker, Multi-Format-Import, Auto-Thumbnails." >}}
|
||||
{{< card link="raeume-sia416" title="Räume (SIA 416)" icon="view-grid" subtitle="HNF / NNF / FF / VF, Flächentabellen, CSV-Export." >}}
|
||||
{{< card link="layouts" title="Layouts & PDF-Export" icon="document-duplicate" subtitle="Plan-Sets, Titelblock-Vorlagen, Stapelexport." >}}
|
||||
{{< card link="massstab" title="Massstab & Display" icon="search" subtitle="Viewport 1:N, Auto-DPI, Display-Modes (Dossier-Plan in Entwicklung)." >}}
|
||||
{{< card link="gestaltung" title="Gestaltung & Overrides" icon="adjustments" subtitle="Farbe · Lineweight · Linetype · Hatch · Regeln." >}}
|
||||
{{< card link="ausschnitte" title="Ausschnitte & Kamera" icon="camera" subtitle="Viewport-Snapshots, Detail-Views, Kamera-Sets." >}}
|
||||
{{< card link="dimensionen" title="Dimensionen" icon="lightning-bolt" subtitle="Bemassungs-Panel, Wand-Dicken, Geschoss-Höhen." >}}
|
||||
{{< card link="swisstopo-osm" title="Swisstopo & OSM" icon="map" subtitle="Landeskarten-Tiles, OSM, Adress-Prefill, Terrain, m.ü.M." >}}
|
||||
{{< card link="text-editor" title="DOSSIER-Text Editor" icon="pencil" subtitle="WYSIWYG Rich-Text für TextEntities mit RTF-Export." >}}
|
||||
{{< card link="werkzeuge" title="Werkzeuge" icon="cog" subtitle="Batch-Tools — Cache-Clear, Section-Reset, Layer-Bereinigung." >}}
|
||||
{{< /cards >}}
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Ausschnitte & Kamera
|
||||
linkTitle: Ausschnitte
|
||||
weight: 11
|
||||
---
|
||||
|
||||
Das **AUSSCHNITTE**-Panel speichert Viewport-Snapshots — Kamera-Position + Display-Mode + Layer-Sichtbarkeit als wiederherstellbare States.
|
||||
|
||||
## Was wird gespeichert?
|
||||
|
||||
Pro Ausschnitt:
|
||||
|
||||
- **Kamera** — Position, Target, Up-Vector, FOV oder Parallel-Projection
|
||||
- **Display-Mode** — Wireframe, Shaded, Dossier-Plan, …
|
||||
- **Layer-Sichtbarkeit** — pro Layer on/off, Sublayer-Override
|
||||
- **Massstab** — gespeichert mit dem Snapshot (über `massstab.py`)
|
||||
- **Clipping-Plane** — optional, mit Position und Normal
|
||||
- **Section-Style** — falls aktiv
|
||||
|
||||
## KAMERA-Panel
|
||||
|
||||
Eigenes Sub-Panel für reine Kamera-Verwaltung — ohne Layer und Display:
|
||||
|
||||
- Standard-Ansichten (Grundriss, Schnitt, Axo, ISO)
|
||||
- Eigene Kamera-Sets pro Geschoss
|
||||
- Multi-Cam-Toolbar für schnellen Wechsel
|
||||
|
||||
## Detail-Views
|
||||
|
||||
Ausschnitte sind die Grundlage für **Detail-Views** im Layout. Workflow:
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Ausschnitt im Modell anlegen
|
||||
|
||||
In Rhino auf die gewünschte Stelle zoomen, Layer und Display einstellen, im AUSSCHNITTE-Panel auf **Speichern**.
|
||||
|
||||
### Benennen und kategorisieren
|
||||
|
||||
Bezeichnung (`Anschluss Wand-Decke`), Massstab (1:20), Geschoss-Zuordnung.
|
||||
|
||||
### Auf Layout ziehen
|
||||
|
||||
Im LAYOUTS-Panel den Ausschnitt auswählen, auf Layout-Blatt platzieren. Massstab wird übernommen, Titelblock-Feld automatisch ausgefüllt.
|
||||
|
||||
### Beschriftung
|
||||
|
||||
Bemassungen direkt im Modellraum — durch die View-Verknüpfung erscheinen sie nur in diesem Ausschnitt im Layout.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## AUSSCHNITT-SETTINGS
|
||||
|
||||
Pro Ausschnitt einstellbar:
|
||||
|
||||
- **Linienstärken-Override** — z.B. Detail mit dickeren Linien
|
||||
- **Hatch-Sichtbarkeit** — pro Ausschnitt ein/aus
|
||||
- **Annotation-Layer** — separater Layer für Detail-Bemassung
|
||||
- **Auto-Refresh** — bei Modell-Änderung Snapshot neu rendern
|
||||
|
||||
## Bi-direktional mit MASSSTAB
|
||||
|
||||
```text
|
||||
AUSSCHNITTE ──save with scale──▶ MASSSTAB
|
||||
▲ │
|
||||
└────read scale on restore─────┘
|
||||
```
|
||||
|
||||
Beim Wechsel eines Ausschnitts wird die gespeicherte Skala über das MASSSTAB-Panel angewendet — inkl. PlotWeight-Synchronisation.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Dimensionen
|
||||
linkTitle: Dimensionen
|
||||
weight: 12
|
||||
---
|
||||
|
||||
Das **DIMENSIONEN**-Panel zeigt und editiert Bemassungs-Stile und Objekt-Informationen für die aktuelle Selektion.
|
||||
|
||||
## Objekt-Info
|
||||
|
||||
Für die Selektion werden angezeigt:
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|-------------------|------------------------------------------------|
|
||||
| Position | X / Y / Z im Welt-Koordinatensystem |
|
||||
| Abmessungen | Bounding-Box-Dimensionen |
|
||||
| Element-Typ | Wand, Decke, Öffnung, Raum, … (falls Smart) |
|
||||
| Geschoss | Aus `dossier_geschoss_id` UserString |
|
||||
| Material | Index + Hex-Farbe |
|
||||
| Layer | Vollständiger Pfad |
|
||||
| User-Strings | Alle `dossier_*` Keys + Werte |
|
||||
|
||||
Bei Multi-Selektion werden Summen / Mittelwerte angezeigt.
|
||||
|
||||
## Bemassungs-Stile
|
||||
|
||||
DOSSIER liefert konsistente Bemassungsstile:
|
||||
|
||||
- **Architektur 1:100** — kleine Pfeile, Lineweight 0.18 mm
|
||||
- **Architektur 1:50** — Standard für Konstruktionspläne
|
||||
- **Detail 1:20** — grössere Schrift, mehr Hierarchie
|
||||
- **Statik** — andere Schrift, technische Beschriftung
|
||||
|
||||
Stile sind im Dokument gespeichert und können als Vorlage in andere Projekte übertragen werden.
|
||||
|
||||
## Wand-Dicken-Bemassung
|
||||
|
||||
Spezial-Funktion: über alle Wände eines Geschosses automatisch Dicken-Bemassung erzeugen — mit konfigurierbarer Versatzdistanz und Ausrichtung.
|
||||
|
||||
## Geschoss-Höhen
|
||||
|
||||
Vertikale Bemassung zwischen OKFF-Linien — entweder als manuelle Bemassung in einem Schnitt oder automatisch aus den Geschoss-Daten generiert.
|
||||
|
||||
## Öffnungs-Masse
|
||||
|
||||
Für selektierte Öffnungen:
|
||||
|
||||
- Brüstungshöhe (UK)
|
||||
- Sturzhöhe (OK)
|
||||
- Breite × Höhe (Lichtmass)
|
||||
- Position in der Wand (Abstand vom nächsten Eck)
|
||||
|
||||
Werden bei Modell-Änderungen aktualisiert.
|
||||
|
||||
## MASSE-SETTINGS
|
||||
|
||||
Eigenes Sub-Panel für globale Bemassungs-Einstellungen:
|
||||
|
||||
- Linien-Farbe und -Stärke
|
||||
- Pfeil-Typ (Tick, Arrow, Dot)
|
||||
- Schrift-Höhe in mm
|
||||
- Massstabs-Faktor (Display vs. Plot)
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Geschosse & Ebenen
|
||||
linkTitle: Geschosse
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Das **EBENEN**-Panel ist die zentrale Projekt-Struktur. Geschosse mit Name, Höhe und OKFF definieren — Dossier baut die komplette Layer-Hierarchie automatisch auf.
|
||||
|
||||
## Geschoss-Definition
|
||||
|
||||
Jedes Geschoss trägt:
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|--------|-------------------------------------------------------|
|
||||
| Name | `UG`, `EG`, `1OG`, `2OG`, `DG`, … (frei wählbar) |
|
||||
| Höhe | Lichte Höhe in mm |
|
||||
| OKFF | Oberkante Fertigfussboden, absolut in mm |
|
||||
| Typ | Vollgeschoss · Untergeschoss · Dachgeschoss · Attika |
|
||||
|
||||
Gespeichert als JSON in `doc.Strings["dossier_ebenen"]` — bleibt in der `.3dm` erhalten.
|
||||
|
||||
## Layer-Hierarchie
|
||||
|
||||
Aus den Geschossen baut Dossier diese Struktur:
|
||||
|
||||
```text
|
||||
10_GRUNDRISSE
|
||||
├── EG
|
||||
│ ├── 20_WAENDE
|
||||
│ ├── 30_DECKEN
|
||||
│ ├── 31_DAECHER
|
||||
│ └── 40_TREPPEN
|
||||
├── 1OG (gleiche Sublayer)
|
||||
└── 2OG
|
||||
20_SCHNITTE
|
||||
30_ANSICHTEN
|
||||
00_RASTER
|
||||
01_VERMESSUNG
|
||||
40_SITUATION
|
||||
90_REFERENZEN
|
||||
99_KONSTRUKTION
|
||||
```
|
||||
|
||||
Wird ein neues Geschoss angelegt, werden die Sublayer automatisch nachgezogen (`layer_builder.py`).
|
||||
|
||||
## Presets
|
||||
|
||||
Häufige Konstellationen sind als Presets verfügbar:
|
||||
|
||||
- **Einfamilienhaus** — UG / EG / OG / DG
|
||||
- **Mehrfamilienhaus** — UG / EG / 1OG / 2OG / 3OG / DG
|
||||
- **Gewerbe** — TG / EG / 1OG / 2OG
|
||||
|
||||
Eigene Presets können gespeichert und projektübergreifend wiederverwendet werden.
|
||||
|
||||
## Aktives Geschoss
|
||||
|
||||
Das aktive Geschoss bestimmt, auf welchem Layer neue Smart-Elemente landen. Wechsel über das Drop-Down im EBENEN-Panel oder über die OBERLEISTE.
|
||||
|
||||
## Multi-Geschoss-Clipping
|
||||
|
||||
Beim Editieren eines Geschosses werden alle darüberliegenden Geschosse automatisch geclippt — sichtbar ist nur das aktive plus optional die unmittelbar darunterliegenden Etagen als Referenz. Die Clipping-Planes sitzen exakt auf OKFF + Geschoss-Höhe und folgen jeder Höhen-Änderung sofort.
|
||||
|
||||
Modi:
|
||||
|
||||
- **Aktiv only** — nur das gewählte Geschoss
|
||||
- **Aktiv + Darunter** — mit der nächst tieferen Etage als Kontext (Standard)
|
||||
- **Alle** — Multi-Geschoss-Stack ohne Clipping
|
||||
|
||||
Toggle über die OBERLEISTE oder per Shortcut. Clipping ist viewport-spezifisch — Schnittansichten bleiben unberührt.
|
||||
|
||||
## Top-View Z-Guard
|
||||
|
||||
In der Top-View werden Smart-Elemente versehentlich gerne mit Z-Offset verschoben. Der **Z-Guard** fängt das ab: bei Move/Drag-Operationen aus der Top-View wird die Z-Komponente auf 0 (relativ zum aktiven Geschoss-OKFF) gezwungen, ausser der User hält explizit eine Modifier-Taste. Das verhindert die typischen "warum schwebt jetzt meine Wand"-Fehler.
|
||||
|
||||
## Snap-Bar pro Geschoss
|
||||
|
||||
Eigene Snap-Targets pro Geschoss werden im EBENEN-Panel über die **Snap-Bar** verwaltet:
|
||||
|
||||
- **OKFF-Linien** — pro Geschoss als horizontale Endlos-Snaps
|
||||
- **Achs-Lines** — Tragwerk-Achsen als geteilte Referenz
|
||||
- **Polyline-Knoten** — automatisch aus den Wand-Polylines abgeleitet
|
||||
|
||||
Snaps werden viewport-bezogen registriert und beim Geschoss-Wechsel automatisch ein-/ausgeblendet. Spart Rhinos OSnap-Slot für andere Workflows.
|
||||
|
||||
{{< callout type="info" >}}
|
||||
Code-Identifier verwenden **ASCII-Schreibweise** (`UG`, `OG`, `DG`) — UI darf Umlaute, Layer-Codes nicht. Das ist eine bewusste Konvention seit der CPython-3-Migration.
|
||||
{{< /callout >}}
|
||||
|
||||
## Cross-Module-Verhalten
|
||||
|
||||
Wenn Geschoss-Höhen oder OKFF im EBENEN-Panel geändert werden:
|
||||
|
||||
1. `rhinopanel.py` schreibt die neuen Werte in `doc.Strings`
|
||||
2. `elemente_bridge._regenerate_all()` wird über Sticky-Reference getriggert
|
||||
3. Alle Wand- und Decken-Volumen regenerieren automatisch
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Gestaltung & Overrides
|
||||
linkTitle: Gestaltung
|
||||
weight: 10
|
||||
---
|
||||
|
||||
Das **GESTALTUNG**-Panel verwaltet Selektions-Attribute (Farbe, Lineweight, Linetype, Hatch) und integriert sich mit der **Overrides-Engine** für regelbasierte Wiederverwendung.
|
||||
|
||||
## GESTALTUNG-Panel
|
||||
|
||||
Direkte Editierung der Selektions-Eigenschaften:
|
||||
|
||||
- **Farbe** — Hex-Picker, Layer-Default oder Override
|
||||
- **Lineweight** — in mm oder PlotWeight-Stufe
|
||||
- **Linetype** — Continuous, Dashed, Dotted, Custom-Pattern
|
||||
- **Hatch** — Pattern, Scale, Rotation, Color
|
||||
- **Plot-Sync** — Display-Farbe und Plot-Farbe identisch oder unabhängig
|
||||
|
||||
### Hatch-Curve-Link
|
||||
|
||||
Hatches werden über eine **UUID** an die Boundary-Curve gebunden (in `sc.sticky` gespeichert). Grund: Rhinos UserStrings werden bei Move oder Replace teilweise weggewischt — die Sticky-Bindung ist robuster.
|
||||
|
||||
**Pending-Hatch TTL** — 3-Sekunden-Fenster nach Drag/Move, in dem Hatch-Metadaten wiederherstellbar sind. Verhindert verlorene Hatches bei schnellen Edit-Operationen.
|
||||
|
||||
## Overrides-Engine
|
||||
|
||||
Regelbasierte Overrides folgen dem Schema **Bedingung → Aktion**:
|
||||
|
||||
### Beispiel: Tragwände hervorheben
|
||||
|
||||
```yaml
|
||||
- name: Tragwände rot
|
||||
condition:
|
||||
element_type: wand
|
||||
variant: tragwand
|
||||
action:
|
||||
color: "#c44b4b"
|
||||
lineweight: 0.50
|
||||
hatch:
|
||||
pattern: Solid
|
||||
color: "#c44b4b40"
|
||||
```
|
||||
|
||||
### Beispiel: Geschoss 1OG ausblenden
|
||||
|
||||
```yaml
|
||||
- name: 1OG-Fade
|
||||
condition:
|
||||
layer: "10_GRUNDRISSE::1OG::*"
|
||||
action:
|
||||
color: "#88888840"
|
||||
print: false
|
||||
```
|
||||
|
||||
## Presets
|
||||
|
||||
Presets sind **cross-doc** — einmal definiert, in jedem Projekt verfügbar:
|
||||
|
||||
- **Wettbewerb** — Reduktion auf Linie, kein Hatch
|
||||
- **Ausführungsplan** — Volle Hatching, Section-Styles aktiv
|
||||
- **Detail 1:20** — Hohe Strichstärken, Materialhinweise
|
||||
- **Präsentation** — Farben + Schatten, kein Bemassungs-Layer
|
||||
|
||||
Preset-Auswahl in der OBERLEISTE triggert `overrides_bridge._send_state()` — Panel-Sync ohne Reload.
|
||||
|
||||
## Plot-Sync
|
||||
|
||||
Drei Modi für Display vs. Plot:
|
||||
|
||||
| Modus | Beschreibung |
|
||||
|-----------------|---------------------------------------------------------|
|
||||
| **Sync** | Display-Farbe = Plot-Farbe (Standard) |
|
||||
| **Print-only** | Display bunt zum Editieren, Plot in Schwarz/Weiss |
|
||||
| **Display-only**| Plot folgt Layer-Default, Display ist Override |
|
||||
|
||||
## Hatch-Pattern Matching
|
||||
|
||||
`gestaltung.py` und `rhinopanel.py` koppeln über die **Pattern + Scale + Rotation-Signature**. Wenn ein Hatch in der Selektion einer bekannten Layer-Konvention entspricht, wird automatisch der passende Layer vorgeschlagen.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Layouts & PDF-Export
|
||||
linkTitle: Layouts
|
||||
weight: 8
|
||||
---
|
||||
|
||||
Das **LAYOUTS**-Panel baut auf Rhinos Layout-System auf und ergänzt die projektbezogene Verwaltung: Plan-Sets, zentrale Titelblock-Vorlagen und Stapelexport.
|
||||
|
||||
## Plan-Set Workflow
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Vorlage wählen
|
||||
|
||||
Titelblock-Vorlagen liegen als zentrale Master pro Büro / Projekt. Bürologo, Phasenbezeichnung, Plannummer und Adresse werden aus den Projektdaten (`dossier.project.json`) gespeist und bleiben über alle Blätter konsistent.
|
||||
|
||||
### Views auf das Blatt ziehen
|
||||
|
||||
Named Views aus dem Modell (`Grundriss EG`, `Schnitt A-A`, `Axonometrie`, …) werden auf das Layout-Blatt gezogen. Der **Massstab** wird mit der View übernommen und automatisch im Titelblock eingetragen.
|
||||
|
||||
### Plannummer vergeben
|
||||
|
||||
Plannummern nach Konvention `A.01`, `A.02`, `D.01`, … Reihenfolge bestimmt das spätere Planverzeichnis.
|
||||
|
||||
### Beschriftung
|
||||
|
||||
Bemassungen, Höhenkoten und Detailverweise direkt im Modellraum platzieren — durch die View-Verknüpfung erscheinen sie korrekt im Layout.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## Titelblock
|
||||
|
||||
Felder werden aus Projekt-Daten gespeist:
|
||||
|
||||
| Feld | Quelle |
|
||||
|----------------|------------------------------------------------|
|
||||
| Bürologo | `dossier.project.json` → `buero.logo` |
|
||||
| Bauherr | `dossier.project.json` → `bauherr` |
|
||||
| Projektnummer | `dossier.project.json` → `projektnummer` |
|
||||
| Phase | `dossier.project.json` → `phase` |
|
||||
| Plannummer | Pro Layout-Blatt |
|
||||
| Plantitel | Pro Layout-Blatt |
|
||||
| Massstab | Aus Named View übernommen |
|
||||
| Datum | Automatisch (oder manuell überschrieben) |
|
||||
|
||||
## Stapelexport
|
||||
|
||||
Alle Pläne in einem Durchgang:
|
||||
|
||||
- **Vektor-PDF** — eingebettete Schichten (Layer), text-suchbar
|
||||
- **PNG** — hochauflösend für Präsentationen und Wettbewerbe
|
||||
- **JPG** — kompakter, mit Qualitätsstufe einstellbar
|
||||
|
||||
Dateinamen folgen dem Schema `{Projektnummer}_{Plannummer}_{Titel}.pdf` — konfigurierbar.
|
||||
|
||||
## Planverzeichnis
|
||||
|
||||
Automatisches Inhaltsverzeichnis aller Pläne mit:
|
||||
|
||||
- Nummer
|
||||
- Titel
|
||||
- Massstab
|
||||
- Datum
|
||||
- Phase
|
||||
|
||||
Exportierbar als eigenes Übersichts-Blatt im Plan-Set oder als CSV.
|
||||
|
||||
## LAYOUT-Dialog
|
||||
|
||||
Das **LAYOUT-DIALOG**-Panel zeigt für jedes Layout-Blatt:
|
||||
|
||||
- Verwendete Views
|
||||
- Massstab-Verifikation (Soll vs. Ist)
|
||||
- Fehlende Beschriftungen
|
||||
- Status (Entwurf / Eingabe / Ausführung)
|
||||
|
||||
Ein Plan-Audit vor dem Export verhindert, dass Skalen-Fehler in die Ausgabe wandern.
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Massstab & Display-Modes
|
||||
linkTitle: Massstab
|
||||
weight: 9
|
||||
---
|
||||
|
||||
Das **MASSSTAB**-Panel verwaltet den Viewport-Massstab und die Display-Modes. Es kommuniziert bi-direktional mit AUSSCHNITTE — Skala lesen und setzen.
|
||||
|
||||
## Massstab 1:N
|
||||
|
||||
Standard-Massstäbe für architektonische Pläne:
|
||||
|
||||
| Code | Verwendung |
|
||||
|--------|------------------------------------------|
|
||||
| 1:1 | Detail |
|
||||
| 1:5 | Detail |
|
||||
| 1:10 | Detail |
|
||||
| 1:20 | Detail / Konstruktion |
|
||||
| 1:50 | Grundriss Detail / Konstruktion |
|
||||
| 1:100 | Grundriss / Schnitt / Ansicht (Standard) |
|
||||
| 1:200 | Grundriss (gross) |
|
||||
| 1:500 | Situation |
|
||||
| 1:1000 | Situation / Übersicht |
|
||||
|
||||
Eigene Skalen können hinzugefügt werden.
|
||||
|
||||
## Auto-DPI
|
||||
|
||||
Auf Mac wird die DPI über **CoreGraphics** automatisch ermittelt — Retina-Display vs. externer Monitor wird korrekt erkannt. Robuster als die meisten alternativen Ansätze (z.B. NSScreen-Polling) und ohne Display-Profile-Hacks.
|
||||
|
||||
Auf Windows wird `GetDeviceCaps(LOGPIXELSX)` verwendet.
|
||||
|
||||
## PlotWeight-Synchronisation
|
||||
|
||||
Plot-Strichstärken sind massstabsabhängig:
|
||||
|
||||
```text
|
||||
1:100 → 0.18 mm = 0.5 PlotWeight
|
||||
1:50 → 0.18 mm = 1.0 PlotWeight (skalierungsbedingt)
|
||||
1:20 → 0.18 mm = 2.5 PlotWeight
|
||||
```
|
||||
|
||||
Das MASSSTAB-Panel rechnet die effektive PlotWeight für die aktuelle Skala automatisch und schreibt sie in die Layer-Eigenschaften.
|
||||
|
||||
## Display-Modes
|
||||
|
||||
Eingebaute Display-Modes plus DOSSIER-spezifische:
|
||||
|
||||
| Mode | Verwendung |
|
||||
|----------------------|--------------------------------------------------|
|
||||
| Wireframe | Editieren, Geometrie-Check |
|
||||
| Shaded | Modellansicht |
|
||||
| Rendered | Präsentation |
|
||||
| **Dossier-Plan** | Plot-optimiert (in aktiver Entwicklung) |
|
||||
| **Dossier-Flaechen** | SIA-416 farbliche Überlagerung |
|
||||
| **Dossier-Detail** | Hatching aktiv, hohe Strichstärken |
|
||||
|
||||
Display-Modes werden einmal gelesen und im Sticky gecacht (`oberleiste.py`).
|
||||
|
||||
### Dossier-Plan — Plan-Qualität direkt aus 3D
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
**In Arbeit.** Der Dossier-Plan-Mode ist der nächste grosse Schritt für Plan-Qualität direkt aus dem 3D-Modell — ohne Umweg über 2D-Plandateien.
|
||||
{{< /callout >}}
|
||||
|
||||
Drei kombinierte Effekte machen den Mode aus:
|
||||
|
||||
- **Hidden-Line-Removal** — verdeckte Kanten werden ausgeblendet, Sichtkanten als saubere Strichgrafik gerendert. Funktioniert in Top-View und in der Schnitt-Perspektive.
|
||||
- **Weisser Hintergrund** — Modell-Background auf reinweiss gezwungen, Layer-Display-Farben werden zu pure Schwarz remappt. Sieht direkt aus wie ein gedruckter Plan, nicht wie ein 3D-Viewport.
|
||||
- **Section-Hatch** — Schnittflächen, die über die [Schnitte & Ansichten](../schnitte-ansichten)-Section-Style-API erzeugt wurden, werden mit Pattern und Lineweight aus dem Layer-Style gehatched.
|
||||
|
||||
Das Ziel: Wettbewerbs- und Konkurrenz-Pläne direkt aus dem Modell exportieren, ohne Plot-Konvertierung über 2D-CAD.
|
||||
|
||||
## Section-Styles
|
||||
|
||||
Mit der CPython-3-Migration ist `Rhino.DocObjects.SectionStyle()` direkt instanziierbar — `layer.SetCustomSectionStyle()` verfügbar. Die volle Section-Style-API steht zur Verfügung:
|
||||
|
||||
- Schnittlinien-Stil pro Layer
|
||||
- Hatch-Pattern für Schnittflächen
|
||||
- Hidden-Line-Removal für Ansichten
|
||||
|
||||
War der **Anlass** für die Migration von IronPython 2.7 zu CPython 3.
|
||||
|
||||
## Bi-direktional mit AUSSCHNITTE
|
||||
|
||||
```text
|
||||
MASSSTAB ──set scale──▶ AUSSCHNITTE
|
||||
▲ │
|
||||
└──read scale on load───┘
|
||||
```
|
||||
|
||||
Beim Wechsel eines Ausschnitts (Named View) wird die gespeicherte Skala übernommen. Manuelle Massstabs-Änderung wird beim nächsten Save in den Ausschnitt zurückgeschrieben.
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Material-Library
|
||||
linkTitle: Materialien
|
||||
weight: 5
|
||||
---
|
||||
|
||||
Die Material-Library lebt im **PROJECT-SETTINGS**-Tab "Materialien" und folgt einem **ArchiCAD-style List/Detail-Layout**: links die Material-Liste, rechts der Detail-Editor für die aktive Auswahl.
|
||||
|
||||
## List/Detail-Workflow
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Material in der Liste anlegen
|
||||
|
||||
Plus-Button → neues Material mit Default-Werten. Liste wird sofort aktualisiert, Thumbnail erscheint.
|
||||
|
||||
### Detail editieren
|
||||
|
||||
Rechts werden alle Eigenschaften der aktiven Auswahl gezeigt: Name, Farbe, Rauheit, Metallic, Texturen, Linetype-Binding. Änderungen werden live geschrieben — kein Apply-Button.
|
||||
|
||||
### Verwendung im Modell
|
||||
|
||||
Material wird im ELEMENTE-Panel pro Smart-Element ausgewählt. Auto-Regen aktualisiert die Rhino-Rendermaterials und triggert eine View-Aktualisierung.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## PBR-Materialien
|
||||
|
||||
Jedes Material trägt:
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---------------|-------------------------------------------------------|
|
||||
| Name | Frei vergeben — `Sichtbeton fein`, `Eiche geölt` |
|
||||
| Albedo | Basis-Farbe (Hex) oder Albedo-Textur (PNG/JPG) |
|
||||
| Roughness | Glanz-Schärfe, 0 (Spiegel) → 1 (matt) |
|
||||
| Metallic | Dielektrikum vs. Metall, 0 → 1 |
|
||||
| Normal-Map | Optional — PNG für Surface-Detail |
|
||||
| Linetype | Gebundener Linetype für Schnittlinien (Bezug zu .lin) |
|
||||
|
||||
Rhino-Renderer (Cycles) liest die PBR-Daten direkt — kein separater Material-Editor nötig.
|
||||
|
||||
## Material/Ebene-Separation
|
||||
|
||||
Eine bewusste DOSSIER-Konvention:
|
||||
|
||||
```text
|
||||
Material ──────▶ Wie sieht's aus? (Albedo, Roughness, …)
|
||||
Layer ──────▶ Wozu gehört's? (10_GRUNDRISSE::EG::20_WAENDE)
|
||||
```
|
||||
|
||||
Die beiden bleiben **getrennt** — ein Material kann auf vielen Layern verwendet werden, ein Layer kann viele Materialien tragen. Das vermeidet die typische Rhino-Falle, in der Layer-Farbe und Material-Farbe konkurrieren.
|
||||
|
||||
## Auto-Regen
|
||||
|
||||
Wird ein Material editiert (Farbe geändert, Textur ersetzt), regenerieren sich alle Smart-Elemente, die es verwenden, automatisch:
|
||||
|
||||
1. `materialien_bridge` schreibt das neue PBR-Set in Rhinos `RenderMaterial`-Tabelle
|
||||
2. `elemente_bridge._regenerate_all_with_material(material_id)` läuft über alle Wände, Decken, … mit diesem Material
|
||||
3. Joint-Cache wird invalidiert (Material kann die Lineweight bei Anschnitten beeinflussen)
|
||||
|
||||
Spürbare Latenz < 200 ms für Projekte unter 500 Smart-Elementen.
|
||||
|
||||
## Linetypes (.lin-Import)
|
||||
|
||||
Der **Linientypen**-Tab in PROJECT-SETTINGS importiert AutoCAD-kompatible `.lin`-Files:
|
||||
|
||||
```text
|
||||
*BORDER,Border __ __ . __ __ . __ __ . __ __ . __ __ .
|
||||
A,.5,-.25,.5,-.25,0,-.25
|
||||
```
|
||||
|
||||
Der Parser zerlegt die Sequenz und schreibt eine native `Rhino.DocObjects.Linetype` ins Dokument. Skalierung wird projektweit gesetzt und folgt dem aktiven Massstab (siehe [Massstab](../massstab)).
|
||||
|
||||
Pattern-Editor visualisiert die Sequenz mit Drag-Handles für Strich/Lücke-Längen — `.lin`-Import und Editor schreiben in denselben Datentyp, sind also austauschbar.
|
||||
|
||||
## Hatches (.pat-Import)
|
||||
|
||||
Der **Schraffuren**-Tab importiert AutoCAD-Hatches:
|
||||
|
||||
```text
|
||||
*ANSI31,ANSI Iron, Brick, Stone masonry
|
||||
45,0,0,0,.125
|
||||
```
|
||||
|
||||
Pattern werden als `Rhino.DocObjects.HatchPattern` registriert und stehen Section-Styles, Layer-Properties und Gestaltung-Panel direkt zur Verfügung.
|
||||
|
||||
**Pattern + Scale + Rotation-Signatur** wird im GESTALTUNG-Panel zur automatischen Layer-Erkennung verwendet (siehe [Gestaltung](../gestaltung)).
|
||||
|
||||
## Material-Cache
|
||||
|
||||
Beim Lesen wird über eine **Hex → MaterialIndex-Map** gecacht. Stale-Check bei jedem Read:
|
||||
|
||||
- Existiert der Material-Index noch im Rhino-Doc?
|
||||
- Hat sich der Hex-Wert verändert?
|
||||
- Wurde das Material extern via Rhinos Material-Editor gelöscht?
|
||||
|
||||
Bei Stale wird die Map neu aufgebaut. Manuell triggerbar via [Werkzeuge → Material-Cache leeren](../werkzeuge).
|
||||
|
||||
{{< callout type="info" >}}
|
||||
Texturen werden **relativ zur `.3dm`** referenziert. Wenn das Projekt verschoben wird, müssen Texturen mitwandern — Dossier zeigt fehlende Texturen mit einem warnenden Placeholder-Thumbnail in der Library-Liste an.
|
||||
{{< /callout >}}
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Project-Settings
|
||||
linkTitle: Project-Settings
|
||||
weight: 4
|
||||
---
|
||||
|
||||
Das **PROJECT-SETTINGS**-Panel ist der zentrale Dialog für alles, was projektweit konsistent sein muss. Fünf Tabs, alle Daten im `.3dm` — keine externen Config-Files.
|
||||
|
||||
## Übersicht
|
||||
|
||||
| Tab | Inhalt |
|
||||
|-------------------|-----------------------------------------------------------------------|
|
||||
| Voreinstellungen | Bürodaten, Projektdaten, Phase, Plankopf-Defaults |
|
||||
| Materialien | PBR-Materialien mit Texturen, Farbe, Roughness, Metallic |
|
||||
| Linientypen | Custom-Linetypes per Skalierung und Pattern-Editor (.lin-Import) |
|
||||
| Schraffuren | Hatch-Pattern-Library mit Scale, Rotation und .pat-Import |
|
||||
| Symbole | Symbol-Library mit 2D+3D Pair-Files (siehe [Symbol-Library](../symbol-library)) |
|
||||
|
||||
Alle Tabs schreiben direkt in das `.3dm` (User-Strings am Document-Level) und in eine begleitende `dossier.project.json` neben der Datei.
|
||||
|
||||
## Tab 1 — Voreinstellungen + Projektdaten
|
||||
|
||||
Bürodaten und Projektdaten gehören zusammen ins Plankopf-System. Felder:
|
||||
|
||||
### Bürodaten
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|----------------|---------------------------------------------|
|
||||
| Bürologo | PNG/SVG-Pfad, wird im Titelblock eingesetzt |
|
||||
| Büroname | Volle Firmenbezeichnung |
|
||||
| Adresse | Strasse, PLZ, Ort |
|
||||
| Kontakt | Telefon, Email, Website |
|
||||
|
||||
### Projektdaten
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|-------------------|-----------------------------------------------------------|
|
||||
| Projektnummer | Frei vergeben — `2026-001`, `WBB-123` |
|
||||
| Projektname | Kurz-Titel |
|
||||
| Bauadresse | Strasse, PLZ, Ort der Baustelle |
|
||||
| Bauherr | Name + Adresse |
|
||||
| Phase | Vorprojekt · Bauprojekt · Ausführung · Wettbewerb |
|
||||
| Höhe ü. Meer | Auto-Prefill aus [Swisstopo](../swisstopo-osm) AlMo-DOM |
|
||||
|
||||
Diese Werte stehen allen Layout-Blättern als Titelblock-Variablen zur Verfügung — pro Plan ändert sich nur die Plannummer, der Rest ist projektweit konsistent.
|
||||
|
||||
## Tab 2 — Materialien
|
||||
|
||||
Siehe [Material-Library](../materialien) für die volle Doku.
|
||||
|
||||
Kurz: PBR-Materialien mit Albedo-Textur, Roughness, Metallic, Normal-Map und Linetype-Bindung. Auto-Regen bei Material-Change in der ganzen Geschoss-Hierarchie.
|
||||
|
||||
## Tab 3 — Linientypen
|
||||
|
||||
Custom-Linetypes pro Projekt:
|
||||
|
||||
- **Pattern-Editor** — Strich/Lücke-Sequenz visuell editieren
|
||||
- **Skalierung** — pro Linetype und global (mit Massstab-Bezug)
|
||||
- **.lin-Import** — AutoCAD-kompatible Linetype-Files direkt einlesen
|
||||
|
||||
Linetypes werden in das `.3dm` geschrieben (Rhino-native `Linetype`-Tabelle) und stehen sofort allen Layer-Eigenschaften zur Verfügung.
|
||||
|
||||
## Tab 4 — Schraffuren
|
||||
|
||||
Hatch-Pattern-Verwaltung:
|
||||
|
||||
- **Pattern-Library** mit Standardbestand (Mauerwerk, Stahlbeton, Holz, Dämmung, …)
|
||||
- **.pat-Import** — AutoCAD-Hatches einlesen
|
||||
- **Scale + Rotation** pro Pattern projektweit setzen, lokal überschreibbar
|
||||
|
||||
Hatches werden mit Section-Styles verknüpft (siehe [Schnitte & Ansichten](../schnitte-ansichten)) — Anschnitte beziehen das Pattern automatisch aus den Layer-Properties.
|
||||
|
||||
## Tab 5 — Symbole
|
||||
|
||||
Siehe [Symbol-Library](../symbol-library) für die volle Doku.
|
||||
|
||||
Kurz: 2D+3D Pair-Files mit Satellite-Picker, Multi-Format-Import, Auto-Thumbnails als Base64-PNG.
|
||||
|
||||
## Datenhaltung
|
||||
|
||||
```text
|
||||
dossier.project.json (neben der .3dm)
|
||||
├── buero — Logo, Name, Adresse
|
||||
├── projekt — Nummer, Name, Phase, Bauadresse, Höhe ü. M
|
||||
├── bauherr — Name, Adresse
|
||||
└── settings — Plankopf-Defaults, Massstab, …
|
||||
|
||||
.3dm (Document-UserStrings + Rhino-native Tabellen)
|
||||
├── dossier_materialien — JSON-Liste der Material-IDs + PBR-Daten
|
||||
├── dossier_linetypes — Linetype-Tabelle (Rhino-native)
|
||||
├── dossier_hatches — Hatch-Pattern-Tabelle (Rhino-native)
|
||||
└── dossier_symbols — JSON-Liste der Symbol-IDs + Thumbnail-Cache
|
||||
```
|
||||
|
||||
Eine `.3dm` bleibt eine Datei — Backup heisst `.3dm` + `dossier.project.json` kopieren.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Räume nach SIA 416
|
||||
linkTitle: Räume (SIA 416)
|
||||
weight: 7
|
||||
---
|
||||
|
||||
Räume sind ein eigener Smart-Element-Typ mit **SIA-416-Kategorisierung**. Die Klassifikation lebt als Metadatum am Raum, nicht als sichtbare Farbe — die Plandarstellung bleibt sauber.
|
||||
|
||||
## Kategorien
|
||||
|
||||
| Code | Kategorie | Beispiele |
|
||||
|------|----------------------------|------------------------------------------|
|
||||
| HNF | Hauptnutzfläche | Wohnen, Schlafen, Büro, Verkauf, … |
|
||||
| NNF | Nebennutzfläche | Keller, Estrich, Garage, Technik |
|
||||
| FF | Funktionsfläche | Lift, Schacht, Haustechnik-Raum |
|
||||
| VF | Verkehrsfläche | Korridor, Treppenhaus, Eingangsbereich |
|
||||
| KF | Konstruktionsfläche | Wandquerschnitte, Schächte |
|
||||
|
||||
Konstanten in `elemente.py`:
|
||||
|
||||
```python
|
||||
SIA_KATEGORIEN = {
|
||||
"HNF": ("#a8d4c5", "Hauptnutzflaeche"),
|
||||
"NNF": ("#d4c8a8", "Nebennutzflaeche"),
|
||||
"FF": ("#c8a8d4", "Funktionsflaeche"),
|
||||
"VF": ("#a8b8d4", "Verkehrsflaeche"),
|
||||
}
|
||||
```
|
||||
|
||||
## Standard-Modus vs. Flächen-Modus
|
||||
|
||||
**Standard** — Räume sind im Plan **transparent**. Nur der Raum-Stempel ist sichtbar (Bezeichnung + Fläche).
|
||||
|
||||
**Flächen-Modus** — Räume erhalten eine farbliche Überlagerung nach SIA-Kategorie. Umschaltung in der OBERLEISTE über Display-Mode-Switch.
|
||||
|
||||
## Raum-Stempel
|
||||
|
||||
Jeder Raum trägt einen Stempel mit:
|
||||
|
||||
- Raum-Bezeichnung (frei vergeben — `Wohnen`, `Schlafen 1`, `WC Damen`)
|
||||
- Fläche in m² (automatisch aus Outline berechnet)
|
||||
- Optional: Raum-Nummer und SIA-Kategorie
|
||||
|
||||
Stempel-Position automatisch im Centroid, manuell verschiebbar.
|
||||
|
||||
## Auswertung
|
||||
|
||||
### Raumliste
|
||||
|
||||
Export aller Räume als CSV mit:
|
||||
|
||||
| Geschoss | Nr | Bezeichnung | SIA | Fläche m² | Höhe m |
|
||||
|---|---|---|---|---|---|
|
||||
| EG | 1.01 | Wohnen | HNF | 28.4 | 2.50 |
|
||||
| EG | 1.02 | Küche | HNF | 12.1 | 2.50 |
|
||||
| EG | 1.03 | Korridor | VF | 6.8 | 2.50 |
|
||||
| … | … | … | … | … | … |
|
||||
|
||||
### Flächen-Schema
|
||||
|
||||
Aggregierte Tabelle pro Geschoss:
|
||||
|
||||
| Geschoss | HNF m² | NNF m² | FF m² | VF m² | Summe |
|
||||
|---|---|---|---|---|---|
|
||||
| UG | — | 84.2 | 4.5 | 12.0 | 100.7 |
|
||||
| EG | 102.4 | — | — | 18.6 | 121.0 |
|
||||
| 1OG | 98.1 | — | — | 16.8 | 114.9 |
|
||||
| **Total** | **200.5** | **84.2** | **4.5** | **47.4** | **336.6** |
|
||||
|
||||
Beide direkt aus den Raum-Metadaten generiert — keine separate Erfassung nötig.
|
||||
|
||||
## Integration ins Plan-Set
|
||||
|
||||
Raumliste und Flächen-Schema können als eigenes **Übersichts-Blatt** ins Plan-Set integriert werden (über LAYOUTS-Panel) oder als CSV exportiert werden.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: Schnitte & Ansichten
|
||||
linkTitle: Schnitte & Ansichten
|
||||
weight: 3
|
||||
---
|
||||
|
||||
Schnitte und Ansichten sind in DOSSIER **eigenständige Smart-Elemente** auf dem 3D-Modell — keine projizierten 2D-Pläne. Die vollständige Section-Style-API aus Rhino 8 wird direkt angesteuert.
|
||||
|
||||
## Schnitt-Perspektive
|
||||
|
||||
Schnitte werden über eine **Section-Plane** mit Position, Normal und Tiefe definiert. Die Section-Plane lebt im Modellraum, ist verschiebbar und folgt jeder Geometrie-Änderung automatisch.
|
||||
|
||||
Pro Schnitt einstellbar:
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|-------------------|-------------------------------------------------------------|
|
||||
| Schnitt-Tiefe | Wie viel hinter der Plane sichtbar bleibt |
|
||||
| Hidden-Line | Verdeckte Kanten ausblenden |
|
||||
| Anschnitt-Hatch | Pattern + Farbe + Lineweight pro Layer |
|
||||
| Beschriftung | Schnitt-Name (`Schnitt A-A`), Massstab im Plankopf-Bezug |
|
||||
|
||||
Schnittlinie wird automatisch in Grundriss-Layouts projiziert.
|
||||
|
||||
## Section-Style-API
|
||||
|
||||
Mit der CPython-3-Migration wurde `Rhino.DocObjects.SectionStyle()` direkt instanziierbar — `layer.SetCustomSectionStyle()` ist verfügbar. Das war der **konkrete Anlass** für den Sprung von IronPython 2.7 auf CPython 3.
|
||||
|
||||
Pro Layer wird gespeichert:
|
||||
|
||||
- **Schnittlinien-Stil** — Farbe, Lineweight, Linetype für gerade angeschnittene Kanten
|
||||
- **Hatch-Pattern** — Pattern, Scale, Rotation, Farbe für die Schnittfläche
|
||||
- **Hidden-Line-Stil** — separate Eigenschaften für verdeckte Geometrie
|
||||
- **Anschnitt-Tiefe** — wie tief in den Layer "hineingeschnitten" wird
|
||||
|
||||
Die Section-Style-Daten leben als `Rhino.DocObjects.SectionStyle` direkt am Layer im `.3dm` — keine externen Konfigs.
|
||||
|
||||
## Ansichten (Elevations)
|
||||
|
||||
Ansichten sind Spezialfall einer Section-Plane:
|
||||
|
||||
- **Parallel-Projektion** statt Perspektive
|
||||
- **Section-Plane** liegt vor der Geometrie (nicht durch sie hindurch)
|
||||
- **Auto-Direction** aus der Kompass-Richtung (Nord, Ost, Süd, West) oder benutzerdefiniert
|
||||
|
||||
Werden über AUSSCHNITTE als Named View gespeichert und auf Layout-Blätter gezogen.
|
||||
|
||||
## Cross-Module-Verhalten
|
||||
|
||||
```text
|
||||
SCHNITTE ──Section-Plane──▶ Display-Mode "Dossier-Plan"
|
||||
│ │
|
||||
│ ▼
|
||||
│ Hidden-Line + Section-Hatch
|
||||
▼
|
||||
AUSSCHNITTE ──save with section──▶ LAYOUTS
|
||||
│
|
||||
▼
|
||||
Plankopf-Feld "Massstab + Schnitt-Name"
|
||||
```
|
||||
|
||||
## Display-Mode-Kopplung
|
||||
|
||||
Der [Massstab-Modus](../massstab) **Dossier-Plan** wertet aktive Section-Planes automatisch aus:
|
||||
|
||||
- Anschnitt-Geometrie wird mit dem Layer-Section-Hatch belegt
|
||||
- Hidden-Line wird über die Plane berechnet (Rhino-natives Verfahren)
|
||||
- Background weisst, Linien schwarz — direkt plottbar
|
||||
|
||||
## Convention
|
||||
|
||||
Schnittlinien-Namen folgen der Bauplan-Konvention:
|
||||
|
||||
| Beschriftung | Verwendung |
|
||||
|--------------|---------------------------------------------|
|
||||
| `A-A`, `B-B` | Längs- und Querschnitte |
|
||||
| `C-C`, `D-D` | Treppenhaus, Schächte, Sonderschnitte |
|
||||
| `D1`, `D2` | Detailschnitte (1:5 oder 1:20) |
|
||||
|
||||
Detailschnitt-Bereich kann als Rechteck im Grundriss markiert werden — Layout-Blatt zieht automatisch den Section-Cut + Detail-Beschriftung.
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Smart-Elemente
|
||||
linkTitle: Smart-Elemente
|
||||
weight: 2
|
||||
---
|
||||
|
||||
Smart-Elemente sind parametrische Bauteile mit dem **Source ↔ Volume Pattern**: eine editierbare Source-Geometrie und ein automatisch generiertes Volumen.
|
||||
|
||||
## Pattern
|
||||
|
||||
```text
|
||||
Source-Geometrie (vom User editiert)
|
||||
│
|
||||
▼ Regeneration bei Change
|
||||
Volume (Brep, vom Plugin verwaltet)
|
||||
```
|
||||
|
||||
Wird die Source verschoben oder verformt, regeneriert das Volumen automatisch — Wand-Joints, Decken-Aussparungen und Öffnungs-Cutouts werden mitgeführt.
|
||||
|
||||
## Bauteil-Typen
|
||||
|
||||
### Wände (Wand-System v1)
|
||||
|
||||
| Source | Volume |
|
||||
|-------------------------|-----------------------------------------------------|
|
||||
| Polyline (mit Bögen) | Brep mit Dicke × Höhe, Geschoss-OKFF als Basis |
|
||||
|
||||
**Polyline-Achse statt Einzel-Segmente:** Eine Wand ist ein zusammenhängender Polyline-Strang — gerade Teilstrecken, Bogen-Segmente und Knicke gehören zur selben Source. Joints zwischen Polylines werden weiterhin als Miter / T-Junction gerechnet, aber Knicke innerhalb einer Wand brauchen kein eigenes Joint-Setup mehr.
|
||||
|
||||
**Chain-Anchor:** Die Polyline trägt einen stabilen Anker auf einem festgelegten Knoten. Bei Modifikationen am Strang (verschieben, verlängern, Knoten einfügen) bleibt die Wand-Identität an diesem Anker — Material, UserStrings und Joint-Verknüpfungen wandern mit.
|
||||
|
||||
**Native Rhino-Grips:** Knoten der Polyline-Achse erscheinen direkt als Rhino-Grips. Verschieben eines Knotens triggert Volume-Regeneration plus Joint-Re-Calc in einem Idle-Tick — keine separate Edit-Mode-UI nötig.
|
||||
|
||||
**Cmd+Z stabil:** Undo/Redo über alle Joint-Operationen ist garantiert konsistent. Der Joint-Cache wird über die Polyline-Achse rekonstruiert, nicht aus dem stale Pre-Undo-State.
|
||||
|
||||
**Joint-Cache** pro Geschoss (`_JOINTS_CACHE_KEY`), invalidiert bei Add/Delete und nach Polyline-Edits über den Grip-Handler.
|
||||
|
||||
**Wand-Typen:** Tragwand · Trennwand · Brandwand · Aufschüttung
|
||||
|
||||
**Material-Cache:** Hex → MaterialIndex, stale-Check beim Lesen — der Cache wird beim Material-Delete in Rhino automatisch invalidiert (siehe [Materialien](../materialien)).
|
||||
|
||||
### Decken & Dächer
|
||||
|
||||
| Source | Volume |
|
||||
|------------|---------------------------------------------------------|
|
||||
| Outline | Brep-Extrusion mit konstanter Stärke, OKFF + Geschoss-Höhe |
|
||||
|
||||
- Aussparungen für Schächte als Sekundär-Curves
|
||||
- Dächer mit Neigung (Pultdach, Satteldach, Walmdach)
|
||||
|
||||
### Öffnungen (Fenster / Türen)
|
||||
|
||||
| Source | Volume |
|
||||
|---------|-----------------------------------------------------|
|
||||
| Punkt | Rahmen + Sims + Flügel, schneidet Wand-Volumen aus |
|
||||
|
||||
- Bibliothek mit Standard-Typen (Holz / Aluminium / Kunststoff)
|
||||
- Brüstungshöhe und Sturzhöhe pro Element parametrisch
|
||||
- Cutout im Wand-Volumen wird automatisch nachgeführt
|
||||
|
||||
### Treppen
|
||||
|
||||
Geometrie-Typen:
|
||||
|
||||
- **Gerade Treppe** — Anfangs-/Endpunkt, Steigung
|
||||
- **L-Treppe** — Dreipunkt, Zwischenpodest automatisch
|
||||
- **Wendeltreppe** — Zentrum, Radius, Steigungswinkel
|
||||
|
||||
### Tragwerk
|
||||
|
||||
- **Stützen** — Punkt, Profil (Rechteck / Rund / I)
|
||||
- **Träger** — Linie, I-Profil mit Schenkel-Höhe und -Breite
|
||||
- **I-Profile** — Standardisierte Querschnitte (HEA / HEB / IPE)
|
||||
|
||||
## UI-Workflow
|
||||
|
||||
Im **ELEMENTE**-Panel:
|
||||
|
||||
1. Bauteil-Typ wählen (Wand, Decke, Öffnung, …)
|
||||
2. Variante wählen (Tragwand, Pultdach, Holzfenster, …)
|
||||
3. Source in Rhino zeichnen
|
||||
4. Volumen erscheint sofort
|
||||
|
||||
## Properties
|
||||
|
||||
Das **ELEMENTE-PROPERTIES**-Panel zeigt für die Selektion:
|
||||
|
||||
- Element-Typ und -Variante
|
||||
- Dicke / Höhe / Position
|
||||
- Geschoss-Zuordnung
|
||||
- UserStrings (`dossier_element_id`, `dossier_element_type`)
|
||||
|
||||
Änderungen werden direkt geschrieben — keine Apply-Button-Logik.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das **ELEMENTE-ÜBERSICHT**-Panel listet alle Smart-Elemente im Dokument tabellarisch — gefiltert nach Geschoss, Typ und Material. Ideal für Mengen-Audits und Konsistenz-Checks.
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
`elemente.py` ist **7'244 LOC** und enthält BIM-Logik aus echten Projekten. Nicht ohne expliziten Auftrag und Test-Plan refaktorisieren.
|
||||
{{< /callout >}}
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Swisstopo & OpenStreetMap
|
||||
linkTitle: Swisstopo & OSM
|
||||
weight: 13
|
||||
---
|
||||
|
||||
DOSSIER bringt **georeferenzierten Hintergrund** direkt in Rhino — über Schweizer Landeskarten-Tiles (Swisstopo) oder OpenStreetMap-Daten.
|
||||
|
||||
## SWISSTOPO-Panel
|
||||
|
||||
Direkter Import von Tile-Layern aus [api3.geo.admin.ch](https://api3.geo.admin.ch/):
|
||||
|
||||
| Layer | Verwendung |
|
||||
|-------------------------------|-------------------------------------|
|
||||
| Landeskarte 1:25'000 | Übersicht, Situation |
|
||||
| Landeskarte 1:10'000 | Quartier-Plan |
|
||||
| Orthofoto | Luftbild als Hintergrund |
|
||||
| AV-Daten (Kataster) | Grundstücksgrenzen |
|
||||
| AlMo-DOM (Höhenmodell) | Geländedaten als Höhenkurven |
|
||||
|
||||
### Workflow
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Adresse oder Koordinate eingeben
|
||||
|
||||
Adresse oder LV95-Koordinaten ins SWISSTOPO-Panel eingeben. Karte zoomt auf die Stelle. Bei Adressen wird über die Swisstopo-Geocoder-API aufgelöst und das Ergebnis in LV95 zurückgegeben.
|
||||
|
||||
### Adress-Prefill aus Projektdaten
|
||||
|
||||
Liegt eine Adresse in der `dossier.project.json` (Bauherr-Adresse oder Bauadresse), wird sie direkt als Default ins SWISSTOPO-Panel übernommen. Ein Klick auf **Lokalisieren** zoomt sofort auf den Standort, ohne nochmal tippen zu müssen.
|
||||
|
||||
### Layer wählen
|
||||
|
||||
Tile-Layer aus der Liste auswählen, gewünschte Zoomstufe einstellen.
|
||||
|
||||
### Importieren
|
||||
|
||||
Tiles werden als PictureFrame in Rhino platziert — auf Layer `40_SITUATION`, georeferenziert in LV95 (alternativ WGS84).
|
||||
|
||||
### Terrain & m.ü.M
|
||||
|
||||
Wird **AlMo-DOM (Höhenmodell)** als Layer gewählt, importiert Dossier nicht nur die Höhenkurven, sondern ermittelt für den Projekt-Zentrum-Punkt die exakte **Höhe über Meer** (m.ü.M). Dieser Wert wird in der `dossier.project.json` als `projekt.hoehe_ueber_meer` abgelegt und vom Titelblock automatisch übernommen.
|
||||
|
||||
Optional kann aus den DOM-Daten ein **Terrain-Mesh** rekonstruiert werden — als Hintergrundgeometrie für Schnitte und Visualisierungen.
|
||||
|
||||
### Auf 0 verschieben
|
||||
|
||||
Optional: Projekt-Zentrum auf Welt-Nullpunkt verschieben, Tiles werden mit verschoben — kleine Koordinaten für die Geometrie, aber Real-World-Bezug ist als LV95-Offset und m.ü.M-Wert in der `dossier.project.json` gespeichert.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## OSM-Panel
|
||||
|
||||
Import von OpenStreetMap-Vektordaten:
|
||||
|
||||
- **Strassen** — als Curves auf Layer `40_SITUATION::STRASSEN`
|
||||
- **Gebäude** — als Outlines mit Höhe (aus `building:levels`) auf `40_SITUATION::GEBAEUDE`
|
||||
- **Grünflächen** — als Hatches auf `40_SITUATION::GRUEN`
|
||||
- **Höhenkurven** — wenn SRTM-Daten vorhanden
|
||||
|
||||
Quelle: [Overpass API](https://overpass-api.de/). Cache lokal in `~/Library/Application Support/ch.gabrielevarano.Dossier/osm_cache/`.
|
||||
|
||||
### Bounding-Box
|
||||
|
||||
Im OSM-Panel werden Min/Max-Koordinaten der Bounding-Box gesetzt oder per Rechteck in Rhino gepickt. Daten werden für die Box geladen.
|
||||
|
||||
## Kombination
|
||||
|
||||
Typischer Workflow für die **Situations-Planung**:
|
||||
|
||||
1. Swisstopo Landeskarte 1:5'000 als Hintergrund-Bitmap
|
||||
2. OSM-Strassen als editable Curves drüber
|
||||
3. AV-Daten als Grundstücksgrenze
|
||||
4. Eigenes Projekt mit Smart-Elementen plazieren
|
||||
|
||||
Alles auf `40_SITUATION`-Sublayern getrennt — über die OBERLEISTE schnell sichtbar/unsichtbar schaltbar.
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Symbol-Library
|
||||
linkTitle: Symbol-Library
|
||||
weight: 6
|
||||
---
|
||||
|
||||
Die Symbol-Library lebt im **PROJECT-SETTINGS**-Tab "Symbole" und macht ein klassisches Architekturbüro-Problem zentral verwaltbar: Möbel, Sanitär-Apparate, Bäume, Autos, technische Symbole — überall im Modell, aber konsistent in der Darstellung.
|
||||
|
||||
## 2D+3D Pair-Files
|
||||
|
||||
Jedes Symbol besteht aus zwei verknüpften Files:
|
||||
|
||||
```text
|
||||
sofa-3sitz.3dm — 3D-Modell (Block-Definition)
|
||||
sofa-3sitz.2d.3dm — 2D-Plandarstellung (Block-Definition)
|
||||
```
|
||||
|
||||
Beim Plazieren entscheidet der View-Type:
|
||||
|
||||
- **3D-View / Perspektive** → 3D-File wird verwendet
|
||||
- **Top-View im Grundriss** → 2D-File wird verwendet
|
||||
- **Section-Plane aktiv** → Section-Cut wird aus dem 3D-File abgeleitet
|
||||
|
||||
So bleibt ein Sofa im Schnitt eine sauber gezeichnete 2D-Plandarstellung, in der Visualisierung aber ein vollständiges 3D-Möbel.
|
||||
|
||||
## Satellite-Picker
|
||||
|
||||
Statt Symbole in einem schwergewichtigen Browser zu suchen, öffnet DOSSIER einen **Satellite-Picker** — ein eigenständiges, kleines Fenster mit Live-Thumbnails:
|
||||
|
||||
- Floating-Window neben dem Modellraum
|
||||
- Filter nach Kategorie (Möbel · Sanitär · Vegetation · Verkehr · …)
|
||||
- Klick auf Thumbnail → Symbol wird in Rhino plaziert (Drag-Place)
|
||||
- Mehrfach-Plazierung in Serie ohne Picker zu schliessen
|
||||
|
||||
Bleibt offen während aktivem Modell-Edit — typischer Workflow für Wohnungs-Möblierung.
|
||||
|
||||
## Multi-Format-Import
|
||||
|
||||
Symbole müssen nicht in `.3dm` vorliegen. CRUD im PROJECT-SETTINGS-Symbole-Tab unterstützt:
|
||||
|
||||
| Format | Verwendung |
|
||||
|--------------|-----------------------------------------------------|
|
||||
| `.3dm` | Rhino-native (Pair-Files werden so behandelt) |
|
||||
| `.dwg` | AutoCAD-Möbelbibliotheken |
|
||||
| `.obj` | Generische 3D-Modelle (z.B. von Polantis, Free3D) |
|
||||
| `.fbx` | 3D-Modelle aus DCC-Tools |
|
||||
| `.dae` | Collada — SketchUp-Export |
|
||||
| `.stl` | Mesh-only — Grobgeometrie |
|
||||
|
||||
Beim Import wird das File in den projektbezogenen `symbols/`-Ordner kopiert, ein Block in der Rhino-Block-Tabelle registriert und das Thumbnail generiert.
|
||||
|
||||
## Auto-Thumbnails (Base64-PNG)
|
||||
|
||||
Thumbnails werden **automatisch** beim Import erzeugt:
|
||||
|
||||
1. Symbol wird headless in einer 256×256-Viewport gerendert
|
||||
2. Iso-Ansicht für 3D-Files, Top-Ansicht für 2D-Files
|
||||
3. PNG wird als **Base64-String** in die `dossier.project.json` geschrieben
|
||||
|
||||
Vorteil von Base64: Thumbnails sind teil der Projekt-Konfiguration — der Satellite-Picker kann sie ohne File-IO im Hintergrund anzeigen, und Library-Sharing wird trivial (Config kopieren = Thumbnails mitkopiert).
|
||||
|
||||
Cache-Größe pro Symbol typisch 4–12 KB.
|
||||
|
||||
## CRUD-Operationen
|
||||
|
||||
Volle Verwaltung im PROJECT-SETTINGS-Symbole-Tab:
|
||||
|
||||
- **Create** — Import-Button öffnet File-Picker, Format wird auto-detected
|
||||
- **Read** — Liste mit Thumbnails, Filter nach Kategorie, Suche im Namen
|
||||
- **Update** — Symbol-Eigenschaften (Name, Kategorie, 2D-Pair zuordnen) editieren
|
||||
- **Delete** — Block-Definition aus Rhino entfernen, File aus `symbols/`-Ordner löschen, JSON-Eintrag entfernen
|
||||
|
||||
Lösch-Operation prüft, ob das Symbol im Modell verwendet wird, und bietet Replace-Optionen statt blockierender Hard-Delete.
|
||||
|
||||
## Datenhaltung
|
||||
|
||||
```text
|
||||
projekt-ordner/
|
||||
├── projekt.3dm
|
||||
├── dossier.project.json
|
||||
│ └── symbols: [
|
||||
│ { id: "sofa-3sitz", name: "Sofa 3-Sitz",
|
||||
│ file_3d: "symbols/sofa-3sitz.3dm",
|
||||
│ file_2d: "symbols/sofa-3sitz.2d.3dm",
|
||||
│ thumbnail: "data:image/png;base64,iVBORw0KGgo...",
|
||||
│ category: "moebel" }
|
||||
│ ]
|
||||
└── symbols/
|
||||
├── sofa-3sitz.3dm
|
||||
├── sofa-3sitz.2d.3dm
|
||||
├── tisch-esstisch.3dm
|
||||
└── …
|
||||
```
|
||||
|
||||
Symbol-Files relativ referenziert — Projekt verschieben = Symbole ziehen mit.
|
||||
|
||||
## Roadmap
|
||||
|
||||
**Library Phase C** (geplant): Cloud-Sync via GitHub-Releases — Team-Sharing einer projektübergreifenden Symbol-Library mit semantischer Versionierung. Siehe [Roadmap](../../docs/roadmap).
|
||||
|
||||
**Satellite-Windows-Restyle** (geplant): alle Satellite-Windows (Symbol-Picker, Material-Picker, …) im einheitlichen Pill-Style. Siehe [Roadmap](../../docs/roadmap).
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: DOSSIER-Text Editor
|
||||
linkTitle: Text-Editor
|
||||
weight: 14
|
||||
---
|
||||
|
||||
WYSIWYG Rich-Text Editor für Rhino-TextEntities mit RTF-Export. Im **TEXT-EDITOR**-Panel: Frame picken, formatieren, schreiben.
|
||||
|
||||
## Workflow
|
||||
|
||||
{{% steps %}}
|
||||
|
||||
### Text-Frame in Rhino picken
|
||||
|
||||
Im Modell ein Text-Frame (Rechteck oder TextEntity) selektieren — der Editor öffnet sich mit dem Frame als Container.
|
||||
|
||||
### Formatieren
|
||||
|
||||
Im React-WYSIWYG:
|
||||
|
||||
- **Fett** · *Kursiv* · <u>Underline</u> · ~~Strikethrough~~
|
||||
- Schrift wählen aus den installierten System-Fonts
|
||||
- Tab-Stops setzen
|
||||
- Zeilenumbrüche
|
||||
|
||||
### Speichern
|
||||
|
||||
Der Editor schreibt RTF zurück in die TextEntity. Rhinos eingebauter Parser rendert das Ergebnis im Modellraum.
|
||||
|
||||
{{% /steps %}}
|
||||
|
||||
## RTF-Support
|
||||
|
||||
Rhinos TextEntity-RTF-Parser unterstützt:
|
||||
|
||||
| Code | Bedeutung |
|
||||
|-----------|---------------------------------------|
|
||||
| `\b` | Bold |
|
||||
| `\i` | Italic |
|
||||
| `\ul` | Underline |
|
||||
| `\strike` | Strikethrough |
|
||||
| `\fN` | Font (N = Font-Index in Font-Table) |
|
||||
| `\tab` | Tab-Stop |
|
||||
| `{}` | Gruppe |
|
||||
| `\par` | Absatz (zwischen Gruppen für Newline) |
|
||||
|
||||
{{< callout type="warning" >}}
|
||||
**Kein `\fs`** — eine TextEntity hat global eine Schriftgröße, keine per-Segment-Sizes. Wenn unterschiedliche Größen nötig sind, brauchst du separate TextEntities.
|
||||
{{< /callout >}}
|
||||
|
||||
Quirks bei Newlines und Zeichen-Replace sind in `_runs_to_rtf` (`rhino/text_editor.py`) dokumentiert.
|
||||
|
||||
## TEXT-CREATE Sub-Panel
|
||||
|
||||
Für neue Text-Elemente:
|
||||
|
||||
- **Text-Styles** als Presets (`Plankopf`, `Notiz`, `Legende`, `Bemassung`)
|
||||
- **Font-Apply** — Schrift global oder pro Selektion ändern
|
||||
- **Selection-Settings** — Größe, Farbe, Justification für Multi-Selektion
|
||||
|
||||
Konsistenz über das ganze Projekt — Stile lassen sich aus einem Projekt in ein anderes übertragen.
|
||||
|
||||
## Plan-Beschriftung
|
||||
|
||||
Typische Anwendungen:
|
||||
|
||||
- Plankopf-Text (Adresse, Bauherr)
|
||||
- Legenden im Modellraum
|
||||
- Raum-Stempel-Override (statt Standard-Stempel)
|
||||
- Schnitt-Beschriftungen ("Schnitt A-A", "M 1:50")
|
||||
- Detail-Verweise mit Pfeil und Bezugsnummer
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Werkzeuge
|
||||
linkTitle: Werkzeuge
|
||||
weight: 15
|
||||
---
|
||||
|
||||
Das **WERKZEUGE**-Panel ist eine Sammlung von Batch- und Wartungs-Tools. Schmal gehalten (58 LOC) — keine eigene Logik, nur Knöpfe für häufige Operationen.
|
||||
|
||||
## Verfügbare Tools
|
||||
|
||||
### Cache-Verwaltung
|
||||
|
||||
| Tool | Effekt |
|
||||
|---------------------|------------------------------------------------------------|
|
||||
| **Joint-Cache leeren** | `_dossier_joints_cache` invalidieren — bei verlorenen Joints |
|
||||
| **Material-Cache leeren** | Hex→MaterialIndex-Cache resetten — nach Material-Delete in Rhino |
|
||||
| **Hatch-Link leeren** | UUID→Hatch-Bindung verwerfen — nur als letzter Resort |
|
||||
|
||||
### Layer-Bereinigung
|
||||
|
||||
- **Leere Layer entfernen** — Sublayer ohne Objekte werden gelöscht
|
||||
- **Ungenutzte Geschosse aufräumen** — Sublayer für gelöschte Geschosse entfernen
|
||||
- **Default-Hierarchie wiederherstellen** — fehlende Standard-Layer (`10_GRUNDRISSE`, …) ergänzen
|
||||
|
||||
Siehe `clean.py` und `clean_layers.py`.
|
||||
|
||||
### Section-Style-Reset
|
||||
|
||||
Setzt alle Layer-spezifischen Section-Styles auf Default zurück — nützlich nach Plot-Style-Experimenten.
|
||||
|
||||
### Panel-Reset
|
||||
|
||||
`_reset_panels.py` — alle Bridges und Listener neu registrieren, ohne Rhino-Restart. Bei Python-Code-Änderungen nötig:
|
||||
|
||||
```text
|
||||
_RunPythonScript <DOSSIER_PFAD>/rhino/_reset_panels.py
|
||||
```
|
||||
|
||||
### Section-Inspection
|
||||
|
||||
`inspect_section.py` — Debug-Tool: zeigt für die aktive Section-Plane alle gefundenen Schnittkanten mit ihren Layer-Zuordnungen.
|
||||
|
||||
## ABOUT-Panel
|
||||
|
||||
Eigenes Sub-Panel mit:
|
||||
|
||||
- Plugin-Version
|
||||
- Python-Runtime-Info (`sys.version`)
|
||||
- Lizenz-Info
|
||||
- Build-Datum
|
||||
- Update-Check (über Launcher)
|
||||
|
||||
## Workflow-Position
|
||||
|
||||
WERKZEUGE läuft als eigenständiger Panel-Eintrag in der OBERLEISTE — wird selten direkt benötigt, aber praktisch wenn Caches stale werden oder ein Layer-Layout aus Versehen kaputt geht.
|
||||
|
||||
{{< callout type="info" >}}
|
||||
Die meisten Tools sind **idempotent** — mehrfach aufrufen schadet nicht. Cache-Operationen invalidieren immer atomar, kein Halbzustand möglich.
|
||||
{{< /callout >}}
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Dossier-Launcher
|
||||
linkTitle: Launcher
|
||||
weight: 3
|
||||
toc: true
|
||||
---
|
||||
|
||||
Der **Dossier-Launcher** ist eine separate **Tauri 2** Standalone-App. Sie verwaltet Projekte, hält Settings, liefert Auto-Updates und lebt im System-Tray. Aktuell produktiv im Einsatz für Projekt-Switching, Window-Layout-Push und Plugin-Settings.
|
||||
|
||||
## Was er macht
|
||||
|
||||
{{< cards >}}
|
||||
{{< card title="Projekt-Verwaltung" icon="collection" subtitle="Liste aller DOSSIER-Projekte mit Pfad, letztem Zugriff und Phase." >}}
|
||||
{{< card title="Settings-Sync" icon="cog" subtitle="Schreibt Plugin-Settings in dossier_settings.json — Rhino liest live." >}}
|
||||
{{< card title="Auto-Updates" icon="cloud-download" subtitle="Über tauri-plugin-updater, signiert mit Code-Signing-Key." >}}
|
||||
{{< card title="System-Tray" icon="desktop-computer" subtitle="Quick-Open der letzten 5 Projekte, ohne Hauptfenster zu öffnen." >}}
|
||||
{{< card title="Window-Layouts" icon="template" subtitle="Live-Push von Window-Layouts an laufende Rhino-Session." >}}
|
||||
{{< card title="Recent-Cache" icon="clock" subtitle="recent.json — letzte 50 geöffnete Projekte für Quick-Access." >}}
|
||||
{{< /cards >}}
|
||||
|
||||
## Technologie
|
||||
|
||||
| Komponente | Stack |
|
||||
|--------------|------------------------------------|
|
||||
| Frontend | React + Vite (`launcher/src/`) |
|
||||
| Backend | Rust + Tauri 2 (`launcher/src-tauri/`) |
|
||||
| Update-Plugin| `tauri-plugin-updater` |
|
||||
| Tray-Plugin | `tauri-plugin-tray` |
|
||||
| IPC zu Rhino | Dateibasiert (kein Socket) |
|
||||
|
||||
## Settings-Files
|
||||
|
||||
Pfad-Hierarchie:
|
||||
|
||||
1. **Primär** (Launcher schreibt):
|
||||
`~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json`
|
||||
2. **Legacy-Fallback** (read-only):
|
||||
`~/Library/Application Support/RhinoPanel/dossier_settings.json`
|
||||
|
||||
Recent-Cache:
|
||||
`~/Library/Application Support/ch.gabrielevarano.Dossier/recent.json`
|
||||
|
||||
## Bekannte Settings-Keys
|
||||
|
||||
| Key | Beschreibung |
|
||||
|----------------------|-------------------------------------------------------|
|
||||
| `windowLayout` | Aktives Window-Layout (XML-Display-Name) |
|
||||
| `autoApplyLayout` | Bool — Layout beim Projekt-Wechsel anwenden? |
|
||||
| `pendingApplyLayout` | Pending Layout-Name — wird von `oberleiste.tick_idle()` gepollt und gelöscht |
|
||||
| `rhinoApp` | Pfad zum Rhino-App-Bundle |
|
||||
| `templatePath` | Default-Template für neue Projekte |
|
||||
|
||||
## Window-Layouts auf Mac
|
||||
|
||||
Mac Rhino 8 speichert Window-Layouts als **XML** unter:
|
||||
|
||||
```text
|
||||
~/Library/Application Support/McNeel/Rhinoceros/8.0/settings/Scheme__Default/workspaces/<GUID>.xml
|
||||
```
|
||||
|
||||
Display-Name aus dem `<RhinoUI name="…">` Attribut. Der Launcher liest diese Datei direkt — kein `.rwl` wie auf Windows.
|
||||
|
||||
**Apply** via Reflection über `Rhino.UI.WindowLayout.*`, Fallback `_-SetActiveLayout "Name" _Enter`.
|
||||
|
||||
## Live-Push Workflow
|
||||
|
||||
```text
|
||||
Launcher ─writes─▶ pendingApplyLayout key ──▶ dossier_settings.json
|
||||
│
|
||||
▼
|
||||
Rhino-Plugin: oberleiste.tick_idle() ─polls─▶ liest + clearet
|
||||
│
|
||||
▼
|
||||
Window-Layout aktiv
|
||||
```
|
||||
|
||||
Polling-Intervall: 500 ms im Rhino-Idle-Handler. Latenz spürbar < 1 s.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
cd launcher
|
||||
npm install
|
||||
npm run tauri dev # Development-Mode
|
||||
```
|
||||
|
||||
Release-Build mit Code-Signing:
|
||||
|
||||
```bash
|
||||
cd launcher
|
||||
./scripts/release.sh # → schreibt latest.json für Updater
|
||||
```
|
||||
|
||||
Voraussetzungen siehe [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/).
|
||||
|
||||
## Standalone
|
||||
|
||||
**Rhino läuft ohne Launcher** — Settings haben sinnvolle Defaults, Plugin funktioniert direkt aus dem PackageManager-Install.
|
||||
|
||||
**Launcher läuft ohne Rhino** — Projekt-Verwaltung, Settings-Editor und Updates funktionieren auch ohne installiertes Rhino. IPC ist bewusst dateibasiert.
|
||||
@@ -0,0 +1,99 @@
|
||||
# DOSSIER — Hugo site configuration
|
||||
baseURL: "https://dossier.gabrielevarano.ch/"
|
||||
title: "DOSSIER"
|
||||
theme: "hextra"
|
||||
|
||||
enableRobotsTXT: true
|
||||
hasCJKLanguage: false
|
||||
|
||||
outputs:
|
||||
home: [html]
|
||||
page: [html]
|
||||
section: [html, rss]
|
||||
|
||||
defaultContentLanguage: de
|
||||
languages:
|
||||
de:
|
||||
label: Deutsch
|
||||
weight: 1
|
||||
title: DOSSIER
|
||||
|
||||
module:
|
||||
hugoVersion:
|
||||
extended: true
|
||||
min: "0.146.0"
|
||||
|
||||
markup:
|
||||
highlight:
|
||||
noClasses: false
|
||||
goldmark:
|
||||
renderer:
|
||||
unsafe: true
|
||||
|
||||
enableInlineShortcodes: true
|
||||
|
||||
menu:
|
||||
main:
|
||||
- identifier: documentation
|
||||
name: Dokumentation
|
||||
pageRef: /docs
|
||||
weight: 1
|
||||
- identifier: features
|
||||
name: Features
|
||||
pageRef: /features
|
||||
weight: 2
|
||||
- identifier: launcher
|
||||
name: Launcher
|
||||
pageRef: /launcher
|
||||
weight: 3
|
||||
- identifier: faq
|
||||
name: FAQ
|
||||
pageRef: /faq
|
||||
weight: 4
|
||||
- name: Search
|
||||
weight: 5
|
||||
params:
|
||||
type: search
|
||||
- name: Gitea
|
||||
weight: 6
|
||||
url: "https://git.kgva.ch/karim/DOSSIER"
|
||||
params:
|
||||
icon: github
|
||||
|
||||
params:
|
||||
description: "Rhino 8 Plugin für architektonisches Entwerfen — Smart-Elemente, SIA-416 Flächen, Plan-Layouts. Teil von OpenBureau."
|
||||
|
||||
externalLinkDecoration: true
|
||||
|
||||
navbar:
|
||||
displayTitle: true
|
||||
displayLogo: false
|
||||
width: normal
|
||||
|
||||
theme:
|
||||
default: dark
|
||||
displayToggle: true
|
||||
|
||||
footer:
|
||||
enable: true
|
||||
displayCopyright: true
|
||||
displayPoweredBy: false
|
||||
width: normal
|
||||
|
||||
page:
|
||||
width: normal
|
||||
|
||||
displayUpdatedDate: false
|
||||
|
||||
search:
|
||||
enable: true
|
||||
type: flexsearch
|
||||
flexsearch:
|
||||
index: content
|
||||
tokenize: forward
|
||||
|
||||
editURL:
|
||||
enable: false
|
||||
|
||||
toc:
|
||||
displayTags: false
|
||||
@@ -0,0 +1 @@
|
||||
copyright: '© 2026 [DOSSIER](https://git.kgva.ch/karim/DOSSIER) · AGPL-3.0 · Teil von OpenBureau'
|
||||
@@ -0,0 +1,10 @@
|
||||
{{ define "main" }}
|
||||
<div class='hx:mx-auto hx:flex hextra-max-page-width'>
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true) }}
|
||||
<main id="content" class="hx:w-full hx:break-words hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:pb-8 hx:pt-8 hx:md:pt-12 hx:pl-[max(env(safe-area-inset-left),1.5rem)] hx:pr-[max(env(safe-area-inset-left),1.5rem)]">
|
||||
<div class="hx:flex hx:flex-col hx:items-center hx:text-center hextra-home">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{ end }}
|
||||
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/hugo:1": {
|
||||
"extended": true,
|
||||
"version": "0.161.1"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/node:1": {}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["mhutchie.git-graph", "esbenp.prettier-vscode", "tamasfe.even-better-toml", "budparr.language-hugo-vscode"]
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "npm install",
|
||||
"forwardPorts": [1313]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Mark generated files so they are collapsed by default in GitHub diffs
|
||||
assets/css/compiled/main.css linguist-generated=true
|
||||
docs/hugo_stats.json linguist-generated=true
|
||||
@@ -0,0 +1,9 @@
|
||||
node_modules/
|
||||
public/
|
||||
resources/
|
||||
|
||||
.hugo_build.lock
|
||||
|
||||
# Playwright
|
||||
playwright-report/
|
||||
test-results/
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"plugins": [
|
||||
"prettier-plugin-go-template"
|
||||
],
|
||||
"goTemplateBracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"printWidth": 200,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"options": {
|
||||
"parser": "go-template"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Hextra is a modern, responsive Hugo theme designed for creating documentation websites, technical blogs, and static sites. Built with Tailwind CSS, it offers features like full-text search, dark mode, multi-language support, and extensive customization options.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Initial Setup
|
||||
|
||||
When working in a new worktree or fresh clone without `node_modules`, run `npm install` first to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Development Server
|
||||
|
||||
```bash
|
||||
# Start development server with theme reloading (recommended for theme development)
|
||||
npm run dev:theme
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build the example site
|
||||
npm run build
|
||||
|
||||
# Build CSS assets only
|
||||
npm run build:css
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Hugo Theme Structure
|
||||
|
||||
- **Base Layout**: `layouts/baseof.html` wraps all pages
|
||||
- **Specialized Layouts**: `layouts/docs/`, `layouts/blog/`, `layouts/hextra-home.html`
|
||||
- **Partials**: Reusable components in `layouts/_partials/`
|
||||
- Core UI: `navbar.html`, `sidebar.html`, `footer.html`, `breadcrumb.html`, `toc.html`
|
||||
- Utilities: `layouts/_partials/utils/` for helper functions
|
||||
- Custom overrides: `layouts/_partials/custom/` for user customizations
|
||||
- **Shortcodes**: Custom Markdown extensions in `layouts/_shortcodes/`
|
||||
- **Render Hooks**: Custom Markdown rendering in `layouts/_markup/` for codeblocks, headings, images, and links
|
||||
|
||||
### Asset Organization
|
||||
|
||||
```
|
||||
assets/
|
||||
├── css/
|
||||
│ ├── styles.css # Main stylesheet (Tailwind entry point)
|
||||
│ ├── compiled/main.css # Built CSS output (generated)
|
||||
│ ├── components/ # Component-specific styles
|
||||
│ ├── chroma/ # Syntax highlighting themes
|
||||
│ └── custom.css # User customization entry point
|
||||
└── js/
|
||||
├── core/ # Core JS components
|
||||
└── flexsearch.js # Search functionality
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
- **Search**: FlexSearch-powered full-text search (`assets/js/flexsearch.js`)
|
||||
- **Navigation**: Responsive navbar and auto-generated sidebar
|
||||
- **Theme Toggle**: Dark/light mode switching
|
||||
- **Internationalization**: 20+ language support in `i18n/`
|
||||
|
||||
### Content Features
|
||||
|
||||
- **Shortcodes**: `callout`, `card`, `cards`, `tabs`, `tab`, `details`, `steps`, `filetree`, `jupyter`, `badge`, `icon`, `pdf`, `include`, `asciinema`, `term`
|
||||
- **Code Features**: Syntax highlighting (Chroma), copy buttons, line numbers via render hooks
|
||||
- **SEO**: Open Graph, Twitter Cards, structured data
|
||||
- **Performance**: Minimal JavaScript, optimized CSS with Tailwind
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Example Site Development
|
||||
|
||||
The `docs/` directory serves as both documentation and testing ground:
|
||||
|
||||
- Test new features here before releasing
|
||||
- Configuration examples in `docs/hugo.yaml` showing multi-language setup
|
||||
- Content examples demonstrate all theme capabilities
|
||||
- Run from docs with: `hugo server --themesDir=../..`
|
||||
|
||||
### CSS Development Workflow
|
||||
|
||||
- Source: `assets/css/styles.css` (main stylesheet)
|
||||
- Build process: Tailwind CSS → PostCSS → `assets/css/compiled/main.css`
|
||||
- Component styles organized in `assets/css/components/`
|
||||
- Chroma syntax highlighting themes in `assets/css/chroma/`
|
||||
- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+)
|
||||
|
||||
#### Rebuilding CSS after template changes
|
||||
|
||||
Tailwind CSS relies on `docs/hugo_stats.json` to know which HTML tags, classes, and IDs are actually used in the built site, so it can tree-shake unused styles. When you modify layouts, partials, or shortcodes you must **regenerate `hugo_stats.json` first**, then rebuild the CSS:
|
||||
|
||||
1. **Generate `docs/hugo_stats.json`** — Run Hugo with the `dev.toml` config (which sets `build.buildStats.enable = true`):
|
||||
|
||||
```bash
|
||||
# Using npm (starts a dev server that writes hugo_stats.json on every rebuild):
|
||||
npm run dev:theme
|
||||
|
||||
# Or a one-shot build using the raw Hugo command:
|
||||
hugo --config=hugo.yaml,../dev.toml --themesDir=../.. --source=docs
|
||||
```
|
||||
|
||||
2. **Build the CSS** — With an up-to-date `hugo_stats.json` in place, compile the stylesheet:
|
||||
|
||||
```bash
|
||||
npm run build:css
|
||||
```
|
||||
|
||||
> **Why two steps?** `dev.toml` mounts `docs/hugo_stats.json` into the Hugo asset pipeline (`assets/notwatching/hugo_stats.json`) and configures a cache-buster so that changes to the stats file trigger a CSS recompile during `dev:theme`. When running outside the dev server you need to perform these steps manually in order.
|
||||
|
||||
### Customization Points
|
||||
|
||||
- Custom partials: `layouts/_partials/custom/`
|
||||
- Custom CSS: `assets/css/custom.css`
|
||||
- Site-specific overrides: Copy any layout to your site's `layouts/` directory
|
||||
|
||||
## Configuration & Requirements
|
||||
|
||||
### Theme Requirements
|
||||
|
||||
- Hugo minimum version: 0.146.0 (extended version required - see `theme.toml`)
|
||||
- Go 1.20+ (as specified in `go.mod`)
|
||||
- Node.js for CSS compilation (PostCSS, Tailwind CSS v4+)
|
||||
|
||||
### Key Configuration Files
|
||||
|
||||
- `docs/hugo.yaml` - Example Hugo configuration with multi-language setup
|
||||
- `postcss.config.mjs` - PostCSS configuration for CSS processing
|
||||
- `package.json` - Node.js dependencies and build scripts
|
||||
|
||||
### Development Environment
|
||||
|
||||
- Default Hugo development server: Port 1313
|
||||
- Development server runs with `--disableFastRender -D` for better development experience
|
||||
- Theme development uses `--logLevel=debug` for detailed logging
|
||||
|
||||
### Multi-language Support
|
||||
|
||||
- Configure languages in `hugo.yaml` (supports 20+ languages including RTL)
|
||||
- Translation files in `i18n/` directory (e.g., `en.yaml`, `fa.yaml`, `ja.yaml`, `zh-cn.yaml`)
|
||||
- Example supports English, Persian (RTL), Japanese, and Simplified Chinese
|
||||
|
||||
## Theme Development Guidelines
|
||||
|
||||
### Hugo Theme Conventions
|
||||
|
||||
- Theme files in this repository override Hugo defaults
|
||||
- Follow Hugo's theme development guidelines for compatibility
|
||||
- Maintain backward compatibility with existing configurations
|
||||
|
||||
### JavaScript & Performance
|
||||
|
||||
- All JavaScript components are designed to have minimal footprint
|
||||
- Core JS components in `assets/js/core/`: `theme.js`, `nav-menu.js`, `code-copy.js`, `sidebar.js`, `tabs.js`, etc.
|
||||
- FlexSearch powers offline full-text search (`assets/js/flexsearch.js`)
|
||||
|
||||
### CSS Architecture
|
||||
|
||||
- Uses Tailwind CSS v4+ with PostCSS processing
|
||||
- Component-based CSS organization in `assets/css/components/`
|
||||
- Compiled output goes to `assets/css/compiled/main.css`
|
||||
- Prettier formatting for Go templates and code consistency
|
||||
|
||||
### Accessibility (WCAG Compliance)
|
||||
|
||||
All new features and UI changes must follow the [Web Content Accessibility Guidelines (WCAG) 2.2](https://www.w3.org/TR/WCAG22/) at the **AA** conformance level. Key requirements:
|
||||
|
||||
- **Semantic HTML**: Use appropriate elements (`<nav>`, `<main>`, `<aside>`, `<button>`, `<ul>`, etc.) instead of generic `<div>`/`<span>` where applicable.
|
||||
- **ARIA attributes**: Add `aria-label`, `aria-expanded`, `aria-controls`, `aria-current`, `role`, and other ARIA attributes to interactive components (menus, toggles, dropdowns, modals) so screen readers can interpret them.
|
||||
- **Keyboard navigation**: All interactive elements must be reachable and operable via keyboard (`Tab`, `Enter`, `Escape`, arrow keys). Manage focus appropriately when opening/closing menus, modals, and drawers.
|
||||
- **Focus indicators**: Never remove visible focus outlines. Use the existing `hextra-focus` utility or equivalent visible focus ring styles.
|
||||
- **Color contrast**: Text and interactive elements must meet WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text). Verify in both light and dark modes.
|
||||
- **Images and icons**: Decorative SVGs/icons should have `aria-hidden="true"`. Meaningful images need descriptive `alt` text.
|
||||
- **Skip links and landmarks**: Preserve existing skip-navigation links and ARIA landmark roles (`role="navigation"`, `role="search"`, etc.).
|
||||
- **Live regions**: Use `aria-live` for dynamic content updates (e.g., search results, status messages) so assistive technology announces changes.
|
||||
- **Form controls**: Associate `<label>` elements with inputs. Provide accessible names for buttons that contain only icons.
|
||||
|
||||
When introducing a new component or modifying an existing one, verify it works with keyboard-only navigation and review the rendered HTML for proper semantics and ARIA usage.
|
||||
|
||||
### Testing & Quality Assurance
|
||||
|
||||
- Test all changes in `docs/` before releasing
|
||||
- Use `npm run dev:theme` for theme development with hot reloading
|
||||
- Format code with `npx prettier --write .` before committing
|
||||
- Verify multi-language functionality across supported languages
|
||||
@@ -0,0 +1,193 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Hextra is a modern, responsive Hugo theme designed for creating documentation websites, technical blogs, and static sites. Built with Tailwind CSS, it offers features like full-text search, dark mode, multi-language support, and extensive customization options.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Initial Setup
|
||||
|
||||
When working in a new worktree or fresh clone without `node_modules`, run `npm install` first to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Development Server
|
||||
|
||||
```bash
|
||||
# Start development server with theme reloading (recommended for theme development)
|
||||
npm run dev:theme
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
# Build the example site
|
||||
npm run build
|
||||
|
||||
# Build CSS assets only
|
||||
npm run build:css
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Hugo Theme Structure
|
||||
|
||||
- **Base Layout**: `layouts/baseof.html` wraps all pages
|
||||
- **Specialized Layouts**: `layouts/docs/`, `layouts/blog/`, `layouts/hextra-home.html`
|
||||
- **Partials**: Reusable components in `layouts/_partials/`
|
||||
- Core UI: `navbar.html`, `sidebar.html`, `footer.html`, `breadcrumb.html`, `toc.html`
|
||||
- Utilities: `layouts/_partials/utils/` for helper functions
|
||||
- Custom overrides: `layouts/_partials/custom/` for user customizations
|
||||
- **Shortcodes**: Custom Markdown extensions in `layouts/_shortcodes/`
|
||||
- **Render Hooks**: Custom Markdown rendering in `layouts/_markup/` for codeblocks, headings, images, and links
|
||||
|
||||
### Asset Organization
|
||||
|
||||
```
|
||||
assets/
|
||||
├── css/
|
||||
│ ├── styles.css # Main stylesheet (Tailwind entry point)
|
||||
│ ├── compiled/main.css # Built CSS output (generated)
|
||||
│ ├── components/ # Component-specific styles
|
||||
│ ├── chroma/ # Syntax highlighting themes
|
||||
│ └── custom.css # User customization entry point
|
||||
└── js/
|
||||
├── core/ # Core JS components
|
||||
└── flexsearch.js # Search functionality
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
- **Search**: FlexSearch-powered full-text search (`assets/js/flexsearch.js`)
|
||||
- **Navigation**: Responsive navbar and auto-generated sidebar
|
||||
- **Theme Toggle**: Dark/light mode switching
|
||||
- **Internationalization**: 20+ language support in `i18n/`
|
||||
|
||||
### Content Features
|
||||
|
||||
- **Shortcodes**: `callout`, `card`, `cards`, `tabs`, `tab`, `details`, `steps`, `filetree`, `jupyter`, `badge`, `icon`, `pdf`, `include`, `asciinema`, `term`
|
||||
- **Code Features**: Syntax highlighting (Chroma), copy buttons, line numbers via render hooks
|
||||
- **SEO**: Open Graph, Twitter Cards, structured data
|
||||
- **Performance**: Minimal JavaScript, optimized CSS with Tailwind
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Example Site Development
|
||||
|
||||
The `docs/` directory serves as both documentation and testing ground:
|
||||
|
||||
- Test new features here before releasing
|
||||
- Configuration examples in `docs/hugo.yaml` showing multi-language setup
|
||||
- Content examples demonstrate all theme capabilities
|
||||
- Run from docs with: `hugo server --themesDir=../..`
|
||||
|
||||
### CSS Development Workflow
|
||||
|
||||
- Source: `assets/css/styles.css` (main stylesheet)
|
||||
- Build process: Tailwind CSS → PostCSS → `assets/css/compiled/main.css`
|
||||
- Component styles organized in `assets/css/components/`
|
||||
- Chroma syntax highlighting themes in `assets/css/chroma/`
|
||||
- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+)
|
||||
|
||||
#### Rebuilding CSS after template changes
|
||||
|
||||
Tailwind CSS relies on `docs/hugo_stats.json` to know which HTML tags, classes, and IDs are actually used in the built site, so it can tree-shake unused styles. When you modify layouts, partials, or shortcodes you must **regenerate `hugo_stats.json` first**, then rebuild the CSS:
|
||||
|
||||
1. **Generate `docs/hugo_stats.json`** — Run Hugo with the `dev.toml` config (which sets `build.buildStats.enable = true`):
|
||||
|
||||
```bash
|
||||
# Using npm (starts a dev server that writes hugo_stats.json on every rebuild):
|
||||
npm run dev:theme
|
||||
|
||||
# Or a one-shot build using the raw Hugo command:
|
||||
hugo --config=hugo.yaml,../dev.toml --themesDir=../.. --source=docs
|
||||
```
|
||||
|
||||
2. **Build the CSS** — With an up-to-date `hugo_stats.json` in place, compile the stylesheet:
|
||||
|
||||
```bash
|
||||
npm run build:css
|
||||
```
|
||||
|
||||
> **Why two steps?** `dev.toml` mounts `docs/hugo_stats.json` into the Hugo asset pipeline (`assets/notwatching/hugo_stats.json`) and configures a cache-buster so that changes to the stats file trigger a CSS recompile during `dev:theme`. When running outside the dev server you need to perform these steps manually in order.
|
||||
|
||||
### Customization Points
|
||||
|
||||
- Custom partials: `layouts/_partials/custom/`
|
||||
- Custom CSS: `assets/css/custom.css`
|
||||
- Site-specific overrides: Copy any layout to your site's `layouts/` directory
|
||||
|
||||
## Configuration & Requirements
|
||||
|
||||
### Theme Requirements
|
||||
|
||||
- Hugo minimum version: 0.146.0 (extended version required - see `theme.toml`)
|
||||
- Go 1.20+ (as specified in `go.mod`)
|
||||
- Node.js for CSS compilation (PostCSS, Tailwind CSS v4+)
|
||||
|
||||
### Key Configuration Files
|
||||
|
||||
- `docs/hugo.yaml` - Example Hugo configuration with multi-language setup
|
||||
- `postcss.config.mjs` - PostCSS configuration for CSS processing
|
||||
- `package.json` - Node.js dependencies and build scripts
|
||||
|
||||
### Development Environment
|
||||
|
||||
- Default Hugo development server: Port 1313
|
||||
- Development server runs with `--disableFastRender -D` for better development experience
|
||||
- Theme development uses `--logLevel=debug` for detailed logging
|
||||
|
||||
### Multi-language Support
|
||||
|
||||
- Configure languages in `hugo.yaml` (supports 20+ languages including RTL)
|
||||
- Translation files in `i18n/` directory (e.g., `en.yaml`, `fa.yaml`, `ja.yaml`, `zh-cn.yaml`)
|
||||
- Example supports English, Persian (RTL), Japanese, and Simplified Chinese
|
||||
|
||||
## Theme Development Guidelines
|
||||
|
||||
### Hugo Theme Conventions
|
||||
|
||||
- Theme files in this repository override Hugo defaults
|
||||
- Follow Hugo's theme development guidelines for compatibility
|
||||
- Maintain backward compatibility with existing configurations
|
||||
|
||||
### JavaScript & Performance
|
||||
|
||||
- All JavaScript components are designed to have minimal footprint
|
||||
- Core JS components in `assets/js/core/`: `theme.js`, `nav-menu.js`, `code-copy.js`, `sidebar.js`, `tabs.js`, etc.
|
||||
- FlexSearch powers offline full-text search (`assets/js/flexsearch.js`)
|
||||
|
||||
### CSS Architecture
|
||||
|
||||
- Uses Tailwind CSS v4+ with PostCSS processing
|
||||
- Component-based CSS organization in `assets/css/components/`
|
||||
- Compiled output goes to `assets/css/compiled/main.css`
|
||||
- Prettier formatting for Go templates and code consistency
|
||||
|
||||
### Accessibility (WCAG Compliance)
|
||||
|
||||
All new features and UI changes must follow the [Web Content Accessibility Guidelines (WCAG) 2.2](https://www.w3.org/TR/WCAG22/) at the **AA** conformance level. Key requirements:
|
||||
|
||||
- **Semantic HTML**: Use appropriate elements (`<nav>`, `<main>`, `<aside>`, `<button>`, `<ul>`, etc.) instead of generic `<div>`/`<span>` where applicable.
|
||||
- **ARIA attributes**: Add `aria-label`, `aria-expanded`, `aria-controls`, `aria-current`, `role`, and other ARIA attributes to interactive components (menus, toggles, dropdowns, modals) so screen readers can interpret them.
|
||||
- **Keyboard navigation**: All interactive elements must be reachable and operable via keyboard (`Tab`, `Enter`, `Escape`, arrow keys). Manage focus appropriately when opening/closing menus, modals, and drawers.
|
||||
- **Focus indicators**: Never remove visible focus outlines. Use the existing `hextra-focus` utility or equivalent visible focus ring styles.
|
||||
- **Color contrast**: Text and interactive elements must meet WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text). Verify in both light and dark modes.
|
||||
- **Images and icons**: Decorative SVGs/icons should have `aria-hidden="true"`. Meaningful images need descriptive `alt` text.
|
||||
- **Skip links and landmarks**: Preserve existing skip-navigation links and ARIA landmark roles (`role="navigation"`, `role="search"`, etc.).
|
||||
- **Live regions**: Use `aria-live` for dynamic content updates (e.g., search results, status messages) so assistive technology announces changes.
|
||||
- **Form controls**: Associate `<label>` elements with inputs. Provide accessible names for buttons that contain only icons.
|
||||
|
||||
When introducing a new component or modifying an existing one, verify it works with keyboard-only navigation and review the rendered HTML for proper semantics and ARIA usage.
|
||||
|
||||
### Testing & Quality Assurance
|
||||
|
||||
- Test all changes in `docs/` before releasing
|
||||
- Use `npm run dev:theme` for theme development with hot reloading
|
||||
- Format code with `npx prettier --write .` before committing
|
||||
- Verify multi-language functionality across supported languages
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Xin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,49 @@
|
||||
<div align="center">
|
||||
<h1 align="center">هگزترا</h1>
|
||||
<sup align="center"><a href="README.md">English</a> | <a href="README.zh-cn.md">简体中文</a> | <a href="README.fa.md">فارسی</a></sup>
|
||||
<p align="center">تم هیوگو مدرن، پاسخگو و دارای امکانات کامل برای ایجاد وبسایتهای استاتیک زیبا.</p>
|
||||
|
||||
نسخهی نمایشی → [imfing.github.io/hextra](https://imfing.github.io/hextra/fa)
|
||||
</div>
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/5097752/263550533-c18343ca-3848-4230-b5c0-ee989d7916da.png">
|
||||
<img alt="Hextra" src="https://user-images.githubusercontent.com/5097752/263550528-663599f9-17a1-4686-b5c4-3da233b5034d.png">
|
||||
</picture>
|
||||
|
||||
<div align="right">
|
||||
<a href="https://github.com/imfing/hextra/actions/workflows/pages.yml"><img alt="GitHub Actions Status" src="https://github.com/imfing/hextra/actions/workflows/pages.yml/badge.svg"></a> <a href="https://app.netlify.com/sites/hugo-hextra/deploys"><img alt="Netlify Status" src="https://api.netlify.com/api/v1/badges/61d6e55a-2447-487e-b59f-c9537e5df175/deploy-status"></a>
|
||||
</div>
|
||||
|
||||
## ویژگیها
|
||||
|
||||
- **طراحی زیبا** - با الهام از Nextra، هگزترا از Tailwind CSS برای ارائه یک طراحی مدرن که سایت شما را برجسته میکند، استفاده میکند.
|
||||
- **طراحی واکنشگرا و حالت تیره** - در تمام دستگاهها، از تلفن همراه، تبلت تا دسکتاپ، عالی به نظر میرسد. حالت تیره نیز برای انطباق با شرایط مختلف روشنایی پشتیبانی میشود.
|
||||
- **سریع و سبک** - طراحی شده توسط Hugo، یک ایجادکننده سایت استاتیک سریع مثل رعد و برق که در یک فایل باینری قرار گرفته است، هگزترا ردپای خود را به حداقل میرساند. برای استفاده از آن به جاوااسکریپت یا Node.js نیازی ندارید.
|
||||
- **جستجوی متن کامل** - جستجوی متن کاملا آفلاین داخلی طراحی شده توسط FlexSearch، بدون نیاز به پیکربندی اضافی.
|
||||
- **امکانات کامل** - برای بهتر کردن محتوای شما مارکداون، برجستهکردن سینتکس، فرمولهای ریاضی LaTeX، نمودارها و عناصر Shortcodeها را شامل میشه. فهرست مطالب، بردکرامب، صفحهبندی، پیمایش نوار کناری و موارد دیگر همه به صورت خودکار تولید میشوند.
|
||||
- **چند زبانه و سئو آماده** - سایتهای چند زبانه با حالت چند زبانه Hugo راحت ساخته میشوند. پشتیبانی خارج از جعبه برای برچسبهای سئو، Open Graph و کارتهای توییتر گنجانده شده است.
|
||||
- **پشتیبانی از دسترسپذیری** - اجزای تعاملی از نشانهگذاری معنایی، رفتار سازگار با صفحهکلید و بررسیهای خودکار دسترسپذیری استفاده میکنند تا رابط کاربری در گردشکارهای رایج فناوریهای کمکی قابل استفاده بماند.
|
||||
|
||||
## شروع کنید
|
||||
|
||||
### شروع سریع از طریق Template
|
||||
|
||||
استفاده از [Hextra Starter Template](https://github.com/imfing/hextra-starter-template) سادهترین روش برای راهاندازی سریع یک وبسایت جدید با تم هگزترا است. با کلیک بر روی دکمه "Use this template" در بالای صفحه مخزن شروع کنید.
|
||||
|
||||
مخزن تم همچنین شامل یک [گردش کار گیتهاب Actions](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) رای بهکاراندازی وبسایت شما در گیتهاب Pages است.
|
||||
|
||||
<img alt="Hextra Starter Template" src="https://user-images.githubusercontent.com/5097752/263551418-c403b9a9-a76c-47a6-8466-513d772ef0b7.jpg" width=600/>
|
||||
|
||||
### استفاده
|
||||
|
||||
برای اطلاعات بیشتر به بخش [مستندات](https://imfing.github.io/hextra/fa/docs) مراجعه کنید.
|
||||
|
||||
## مشارکت کردن
|
||||
|
||||
از مشارکت افراد جدید استقبال میکنیم.
|
||||
برای شروع، [راهنمای مشارکت](.github/CONTRIBUTING.md) را بررسی کنید.
|
||||
|
||||
## مجوز
|
||||
|
||||
[مجوز MIT](./LICENSE)
|
||||
@@ -0,0 +1,49 @@
|
||||
<div align="center">
|
||||
<h1 align="center">Hextra</h1>
|
||||
<sup align="center"><a href="README.md">English</a> | <a href="README.zh-cn.md">简体中文</a> | <a href="README.fa.md">فارسی</a></sup>
|
||||
<p align="center">Modern, responsive, batteries-included Hugo theme for creating beautiful static websites.</p>
|
||||
|
||||
Demo → [imfing.github.io/hextra](https://imfing.github.io/hextra/)
|
||||
</div>
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/5097752/263550533-c18343ca-3848-4230-b5c0-ee989d7916da.png">
|
||||
<img alt="Hextra" src="https://user-images.githubusercontent.com/5097752/263550528-663599f9-17a1-4686-b5c4-3da233b5034d.png">
|
||||
</picture>
|
||||
|
||||
<div align="right">
|
||||
<a href="https://github.com/imfing/hextra/actions/workflows/pages.yml"><img alt="GitHub Actions Status" src="https://github.com/imfing/hextra/actions/workflows/pages.yml/badge.svg"></a> <a href="https://app.netlify.com/sites/hugo-hextra/deploys"><img alt="Netlify Status" src="https://api.netlify.com/api/v1/badges/61d6e55a-2447-487e-b59f-c9537e5df175/deploy-status"></a>
|
||||
</div>
|
||||
|
||||
## Features
|
||||
|
||||
- **Beautiful Design** - Inspired by Nextra, Hextra utilizes Tailwind CSS to offer a modern design that makes your site look outstanding.
|
||||
- **Responsive Layout and Dark Mode** - It looks great on all devices, from mobile to desktop. Dark mode is also supported to accommodate various lighting conditions.
|
||||
- **Fast and Lightweight** - Powered by Hugo, a lightning-fast static-site generator housed in a single binary file, Hextra keeps its footprint minimal. No JavaScript or Node.js are needed to use it.
|
||||
- **Full-text Search** - Built-in offline full-text search powered by FlexSearch, no extra configuration required.
|
||||
- **Battery-included** - Markdown, syntax highlighting, LaTeX math formulae, diagrams and Shortcodes elements to enhance your content. Table of contents, breadcrumbs, pagination, sidebar navigation and more are all automatically generated.
|
||||
- **Multi-language and SEO Ready** - Multi-language sites made easy with Hugo's multilingual mode. Out-of-the-box support is included for SEO tags, Open Graph, and Twitter Cards.
|
||||
- **Accessibility Support** - Interactive components use semantic markup, keyboard-friendly behavior, and automated accessibility checks to keep the UI usable across common assistive workflows.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Use the template
|
||||
|
||||
Using the [Hextra Starter Template](https://github.com/imfing/hextra-starter-template) is the simplest method to bootstrap a new website with Hextra theme. Get started by clicking the "Use this template" button on the template repository page.
|
||||
|
||||
The template repository also includes a [GitHub Actions workflow](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) for deploying your website to GitHub Pages.
|
||||
|
||||
<img alt="Hextra Starter Template" src="https://user-images.githubusercontent.com/5097752/263551418-c403b9a9-a76c-47a6-8466-513d772ef0b7.jpg" width=600/>
|
||||
|
||||
### Usage
|
||||
|
||||
Refer to the [documentation](https://imfing.github.io/hextra/docs) for more information.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome.
|
||||
Check out the [contributing guide](.github/CONTRIBUTING.md) to get started.
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](./LICENSE)
|
||||
@@ -0,0 +1,46 @@
|
||||
<div align="center">
|
||||
<h1 align="center">Hextra</h1>
|
||||
<sup align="center"><a href="README.md">English</a> | <a href="README.zh-cn.md">简体中文</a> | <a href="README.fa.md">فارسی</a></sup>
|
||||
<p align="center">用于创建美观的静态站点的现代化, 响应式, 功能强大的 Hugo 主题.</p>
|
||||
|
||||
演示 → [imfing.github.io/hextra](https://imfing.github.io/hextra/)
|
||||
</div>
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/5097752/263550533-c18343ca-3848-4230-b5c0-ee989d7916da.png">
|
||||
<img alt="Hextra" src="https://user-images.githubusercontent.com/5097752/263550528-663599f9-17a1-4686-b5c4-3da233b5034d.png">
|
||||
</picture>
|
||||
|
||||
<div align="right">
|
||||
<a href="https://github.com/imfing/hextra/actions/workflows/pages.yml"><img alt="GitHub Actions Status" src="https://github.com/imfing/hextra/actions/workflows/pages.yml/badge.svg"></a> <a href="https://app.netlify.com/sites/hugo-hextra/deploys"><img alt="Netlify Status" src="https://api.netlify.com/api/v1/badges/61d6e55a-2447-487e-b59f-c9537e5df175/deploy-status"></a>
|
||||
</div>
|
||||
|
||||
## 特性
|
||||
|
||||
- **美观的设计** - 受 Nextra 的启发,Hextra 利用 Tailwind CSS 提供现代化的设计,使您的网站看起来美观有加.
|
||||
- **响应式布局和深色模式支持** - 在任何设备上看起来都足够美观, 无论是手机, 平板电脑或者电脑. 深色模式的支持使 Hextra 可以应对各种照明环境.
|
||||
- **快速且轻量** - 由 Hugo 强力支持, Hugo 是一个快如闪电的静态站点生成器, 这一切都只需一个可执行文件, Hextra 始终保持最小化, 无需 Javascript 或者 Node.js.
|
||||
- **全文搜索** - 集成了 Flexsearch 的全文搜索, 无需额外的配置.
|
||||
- **功能齐全** - Markdown, 代码高亮, LaTex 数学公式, diagrams 图表和 Shortcodes 都可以用于丰富你的内容. 目录, 面包屑导航, 分页, 侧边栏等均由 Hextra 自动生成。
|
||||
- **多语言和 SEO Ready** - Hugo 的多语言模式使得构建多语言网站更简单. 具有 SEO tags, Open Graph, 和 Twitter Cards 等诸多开箱即用的功能.
|
||||
- **无障碍支持** - 交互组件使用语义化标记、友好的键盘交互以及自动化无障碍检查,以便在常见辅助技术工作流中保持良好的可用性。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 使用模板
|
||||
|
||||
使用 [Hextra stater template](https://github.com/imfing/hextra-starter-template) 是使用 Hextra 主题的最简单方法. 点击仓库页面上的 `Use this template` 按钮开始使用.
|
||||
|
||||
此仓库中包含一个 [GitHub Actions workflow](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) 来帮助你免费在 GitHub Pages 上自动构建和部署网站.
|
||||
|
||||
### 使用
|
||||
|
||||
转至[文档](https://imfing.github.io/hextra/zh-cn/docs)
|
||||
|
||||
## 贡献
|
||||
|
||||
该项目正在积极开发中. 欢迎贡献!
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT License](./LICENSE)
|
||||
@@ -0,0 +1,89 @@
|
||||
.dark .highlight {
|
||||
/* Background .bg { color: #c9d1d9; background-color: #0d1117; }
|
||||
/* PreWrapper .chroma { color: #c9d1d9; background-color: #0d1117; } */
|
||||
/* Other */ .chroma .x { }
|
||||
/* Error */ .chroma .err { color: #f85149 }
|
||||
/* CodeLine */ .chroma .cl { }
|
||||
/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
|
||||
/* LineTableTD .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } */
|
||||
/* LineTable .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } */
|
||||
/* LineHighlight .chroma .hl { background-color: #ffffcc } */
|
||||
/* LineNumbersTable .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #64686c } */
|
||||
/* LineNumbers .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #6e7681 } */
|
||||
/* Line */ .chroma .line { display: flex; }
|
||||
/* Keyword */ .chroma .k { color: #ff7b72 }
|
||||
/* KeywordConstant */ .chroma .kc { color: #79c0ff }
|
||||
/* KeywordDeclaration */ .chroma .kd { color: #ff7b72 }
|
||||
/* KeywordNamespace */ .chroma .kn { color: #ff7b72 }
|
||||
/* KeywordPseudo */ .chroma .kp { color: #79c0ff }
|
||||
/* KeywordReserved */ .chroma .kr { color: #ff7b72 }
|
||||
/* KeywordType */ .chroma .kt { color: #ff7b72 }
|
||||
/* Name */ .chroma .n { }
|
||||
/* NameAttribute */ .chroma .na { }
|
||||
/* NameBuiltin */ .chroma .nb { }
|
||||
/* NameBuiltinPseudo */ .chroma .bp { }
|
||||
/* NameClass */ .chroma .nc { color: #f0883e; font-weight: bold }
|
||||
/* NameConstant */ .chroma .no { color: #79c0ff; font-weight: bold }
|
||||
/* NameDecorator */ .chroma .nd { color: #d2a8ff; font-weight: bold }
|
||||
/* NameEntity */ .chroma .ni { color: #ffa657 }
|
||||
/* NameException */ .chroma .ne { color: #f0883e; font-weight: bold }
|
||||
/* NameFunction */ .chroma .nf { color: #d2a8ff; font-weight: bold }
|
||||
/* NameFunctionMagic */ .chroma .fm { }
|
||||
/* NameLabel */ .chroma .nl { color: #79c0ff; font-weight: bold }
|
||||
/* NameNamespace */ .chroma .nn { color: #ff7b72 }
|
||||
/* NameOther */ .chroma .nx { }
|
||||
/* NameProperty */ .chroma .py { color: #79c0ff }
|
||||
/* NameTag */ .chroma .nt { color: #7ee787 }
|
||||
/* NameVariable */ .chroma .nv { color: #79c0ff }
|
||||
/* NameVariableClass */ .chroma .vc { }
|
||||
/* NameVariableGlobal */ .chroma .vg { }
|
||||
/* NameVariableInstance */ .chroma .vi { }
|
||||
/* NameVariableMagic */ .chroma .vm { }
|
||||
/* Literal */ .chroma .l { color: #a5d6ff }
|
||||
/* LiteralDate */ .chroma .ld { color: #79c0ff }
|
||||
/* LiteralString */ .chroma .s { color: #a5d6ff }
|
||||
/* LiteralStringAffix */ .chroma .sa { color: #79c0ff }
|
||||
/* LiteralStringBacktick */ .chroma .sb { color: #a5d6ff }
|
||||
/* LiteralStringChar */ .chroma .sc { color: #a5d6ff }
|
||||
/* LiteralStringDelimiter */ .chroma .dl { color: #79c0ff }
|
||||
/* LiteralStringDoc */ .chroma .sd { color: #a5d6ff }
|
||||
/* LiteralStringDouble */ .chroma .s2 { color: #a5d6ff }
|
||||
/* LiteralStringEscape */ .chroma .se { color: #79c0ff }
|
||||
/* LiteralStringHeredoc */ .chroma .sh { color: #79c0ff }
|
||||
/* LiteralStringInterpol */ .chroma .si { color: #a5d6ff }
|
||||
/* LiteralStringOther */ .chroma .sx { color: #a5d6ff }
|
||||
/* LiteralStringRegex */ .chroma .sr { color: #79c0ff }
|
||||
/* LiteralStringSingle */ .chroma .s1 { color: #a5d6ff }
|
||||
/* LiteralStringSymbol */ .chroma .ss { color: #a5d6ff }
|
||||
/* LiteralNumber */ .chroma .m { color: #a5d6ff }
|
||||
/* LiteralNumberBin */ .chroma .mb { color: #a5d6ff }
|
||||
/* LiteralNumberFloat */ .chroma .mf { color: #a5d6ff }
|
||||
/* LiteralNumberHex */ .chroma .mh { color: #a5d6ff }
|
||||
/* LiteralNumberInteger */ .chroma .mi { color: #a5d6ff }
|
||||
/* LiteralNumberIntegerLong */ .chroma .il { color: #a5d6ff }
|
||||
/* LiteralNumberOct */ .chroma .mo { color: #a5d6ff }
|
||||
/* Operator */ .chroma .o { color: #ff7b72; font-weight: bold }
|
||||
/* OperatorWord */ .chroma .ow { color: #ff7b72; font-weight: bold }
|
||||
/* Punctuation */ .chroma .p { }
|
||||
/* Comment */ .chroma .c { color: #8b949e; font-style: italic }
|
||||
/* CommentHashbang */ .chroma .ch { color: #8b949e; font-style: italic }
|
||||
/* CommentMultiline */ .chroma .cm { color: #8b949e; font-style: italic }
|
||||
/* CommentSingle */ .chroma .c1 { color: #8b949e; font-style: italic }
|
||||
/* CommentSpecial */ .chroma .cs { color: #8b949e; font-weight: bold; font-style: italic }
|
||||
/* CommentPreproc */ .chroma .cp { color: #8b949e; font-weight: bold; font-style: italic }
|
||||
/* CommentPreprocFile */ .chroma .cpf { color: #8b949e; font-weight: bold; font-style: italic }
|
||||
/* Generic */ .chroma .g { }
|
||||
/* GenericDeleted */ .chroma .gd { color: #ffa198; background-color: #490202 }
|
||||
/* GenericEmph */ .chroma .ge { color: inherit; font-style: italic }
|
||||
/* GenericError */ .chroma .gr { color: #ffa198 }
|
||||
/* GenericHeading */ .chroma .gh { color: #79c0ff; font-weight: bold }
|
||||
/* GenericInserted */ .chroma .gi { color: #56d364; background-color: #0f5323 }
|
||||
/* GenericOutput */ .chroma .go { color: #8b949e }
|
||||
/* GenericPrompt */ .chroma .gp { color: #8b949e }
|
||||
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||
/* GenericSubheading */ .chroma .gu { color: #79c0ff }
|
||||
/* GenericTraceback */ .chroma .gt { color: #ff7b72 }
|
||||
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
|
||||
/* TextWhitespace */ .chroma .w { color: #6e7681 }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* Light theme for syntax highlight */
|
||||
/* Generated using `hugo gen chromastyles --style=github` */
|
||||
.highlight {
|
||||
/* Background .bg { background-color: #ffffff; } */
|
||||
/* PreWrapper .chroma { background-color: #ffffff; } */
|
||||
/* Other .chroma .x { } */
|
||||
/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 }
|
||||
/* CodeLine .chroma .cl { } */
|
||||
/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
|
||||
/* LineTableTD .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } */
|
||||
/* LineTable .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } */
|
||||
/* LineHighlight .chroma .hl { background-color: #ffffcc } */
|
||||
/* LineNumbersTable .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } */
|
||||
/* LineNumbers .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } */
|
||||
/* Line */ .chroma .line { display: flex; }
|
||||
/* Keyword */ .chroma .k { color: #000000; font-weight: bold }
|
||||
/* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold }
|
||||
/* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold }
|
||||
/* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold }
|
||||
/* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold }
|
||||
/* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold }
|
||||
/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold }
|
||||
/* Name .chroma .n { } */
|
||||
/* NameAttribute */ .chroma .na { color: #008080 }
|
||||
/* NameBuiltin */ .chroma .nb { color: #0086b3 }
|
||||
/* NameBuiltinPseudo */ .chroma .bp { color: #999999 }
|
||||
/* NameClass */ .chroma .nc { color: #445588; font-weight: bold }
|
||||
/* NameConstant */ .chroma .no { color: #008080 }
|
||||
/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold }
|
||||
/* NameEntity */ .chroma .ni { color: #800080 }
|
||||
/* NameException */ .chroma .ne { color: #990000; font-weight: bold }
|
||||
/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold }
|
||||
/* NameFunctionMagic .chroma .fm { } */
|
||||
/* NameLabel */ .chroma .nl { color: #990000; font-weight: bold }
|
||||
/* NameNamespace */ .chroma .nn { color: #555555 }
|
||||
/* NameOther .chroma .nx { } */
|
||||
/* NameProperty .chroma .py { } */
|
||||
/* NameTag */ .chroma .nt { color: #000080 }
|
||||
/* NameVariable */ .chroma .nv { color: #008080 }
|
||||
/* NameVariableClass */ .chroma .vc { color: #008080 }
|
||||
/* NameVariableGlobal */ .chroma .vg { color: #008080 }
|
||||
/* NameVariableInstance */ .chroma .vi { color: #008080 }
|
||||
/* NameVariableMagic .chroma .vm { } */
|
||||
/* Literal .chroma .l { } */
|
||||
/* LiteralDate .chroma .ld { } */
|
||||
/* LiteralString */ .chroma .s { color: #dd1144 }
|
||||
/* LiteralStringAffix */ .chroma .sa { color: #dd1144 }
|
||||
/* LiteralStringBacktick */ .chroma .sb { color: #dd1144 }
|
||||
/* LiteralStringChar */ .chroma .sc { color: #dd1144 }
|
||||
/* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 }
|
||||
/* LiteralStringDoc */ .chroma .sd { color: #dd1144 }
|
||||
/* LiteralStringDouble */ .chroma .s2 { color: #dd1144 }
|
||||
/* LiteralStringEscape */ .chroma .se { color: #dd1144 }
|
||||
/* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 }
|
||||
/* LiteralStringInterpol */ .chroma .si { color: #dd1144 }
|
||||
/* LiteralStringOther */ .chroma .sx { color: #dd1144 }
|
||||
/* LiteralStringRegex */ .chroma .sr { color: #009926 }
|
||||
/* LiteralStringSingle */ .chroma .s1 { color: #dd1144 }
|
||||
/* LiteralStringSymbol */ .chroma .ss { color: #990073 }
|
||||
/* LiteralNumber */ .chroma .m { color: #009999 }
|
||||
/* LiteralNumberBin */ .chroma .mb { color: #009999 }
|
||||
/* LiteralNumberFloat */ .chroma .mf { color: #009999 }
|
||||
/* LiteralNumberHex */ .chroma .mh { color: #009999 }
|
||||
/* LiteralNumberInteger */ .chroma .mi { color: #009999 }
|
||||
/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 }
|
||||
/* LiteralNumberOct */ .chroma .mo { color: #009999 }
|
||||
/* Operator */ .chroma .o { color: #000000; font-weight: bold }
|
||||
/* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold }
|
||||
/* Punctuation .chroma .p { } */
|
||||
/* Comment */ .chroma .c { color: #999988; font-style: italic }
|
||||
/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic }
|
||||
/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic }
|
||||
/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic }
|
||||
/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic }
|
||||
/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic }
|
||||
/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic }
|
||||
/* Generic .chroma .g { } */
|
||||
/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd }
|
||||
/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic }
|
||||
/* GenericError */ .chroma .gr { color: #aa0000 }
|
||||
/* GenericHeading */ .chroma .gh { color: #999999 }
|
||||
/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd }
|
||||
/* GenericOutput */ .chroma .go { color: #888888 }
|
||||
/* GenericPrompt */ .chroma .gp { color: #555555 }
|
||||
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||
/* GenericSubheading */ .chroma .gu { color: #aaaaaa }
|
||||
/* GenericTraceback */ .chroma .gt { color: #aa0000 }
|
||||
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
|
||||
/* TextWhitespace */ .chroma .w { color: #bbbbbb }
|
||||
}
|
||||
+2
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
.hextra-archive-timeline {
|
||||
@apply hx:border-l-2 hx:border-black/15 hx:dark:border-white/15;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.hextra-badge {
|
||||
@apply hx:inline-flex hx:items-center;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.hextra-banner-hidden .hextra-banner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hextra-banner {
|
||||
:where(a):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:underline hx:decoration-from-font;
|
||||
}
|
||||
:where(p):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:leading-7 hx:first:mt-0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.hextra-cards {
|
||||
grid-template-columns: repeat(auto-fill, minmax(max(250px, calc((100% - 1rem * 2) / var(--hextra-cards-grid-cols))), 1fr));
|
||||
}
|
||||
|
||||
.hextra-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hextra-card img {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.hextra-card:hover .hextra-card-icon svg {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.hextra-card .hextra-card-icon svg {
|
||||
width: 1.5rem;
|
||||
color: #00000033;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.hextra-card p {
|
||||
margin-top: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dark .hextra-card .hextra-card-icon svg {
|
||||
color: #ffffff66;
|
||||
}
|
||||
|
||||
.dark .hextra-card:hover .hextra-card-icon svg {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.hextra-card-tag {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 5px;
|
||||
&:where(:dir(ltr)) {
|
||||
right: 5px;
|
||||
}
|
||||
&:where(:dir(rtl)) {
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@supports (
|
||||
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
|
||||
) {
|
||||
.hextra-code-copy-btn {
|
||||
@apply hx:backdrop-blur-md hx:opacity-85 hx:dark:opacity-80;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.hextra-feature-grid {
|
||||
@media (min-width: 1024px) {
|
||||
grid-template-columns: repeat(var(--hextra-feature-grid-cols), minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.hextra-jupyter-code-cell {
|
||||
scrollbar-gutter: auto;
|
||||
|
||||
@apply hx:mt-6;
|
||||
|
||||
.hextra-jupyter-code-cell-outputs-container {
|
||||
@apply hx:text-xs hx:overflow-hidden;
|
||||
|
||||
.hextra-jupyter-code-cell-outputs {
|
||||
@apply hx:overflow-auto hx:max-h-[50vh];
|
||||
|
||||
pre {
|
||||
@apply hx:text-xs hx:overflow-auto hx:max-w-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
nav {
|
||||
.hextra-search-wrapper {
|
||||
@apply hx:hidden hx:md:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (
|
||||
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
|
||||
) {
|
||||
.hextra-nav-container-blur {
|
||||
@apply hx:backdrop-blur-md hx:bg-white/[.85] hx:dark:bg-dark/80!;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hamburger Menu - Flattened Structure */
|
||||
.hextra-hamburger-menu svg g {
|
||||
@apply hx:origin-center hx:transition-all hx:duration-100 hx:ease-out;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg path {
|
||||
@apply hx:opacity-100 hx:transition-all hx:duration-100 hx:ease-out hx:delay-100;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open path {
|
||||
@apply hx:transition-transform hx:duration-100 hx:ease-out hx:delay-0;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open g {
|
||||
@apply hx:transition-transform hx:duration-100 hx:ease-out hx:delay-100;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open > path {
|
||||
@apply hx:opacity-0;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open > g:nth-of-type(1) {
|
||||
@apply hx:rotate-45;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open > g:nth-of-type(1) path {
|
||||
@apply hx:translate-y-1;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open > g:nth-of-type(2) {
|
||||
@apply hx:-rotate-45;
|
||||
}
|
||||
|
||||
.hextra-hamburger-menu svg.open > g:nth-of-type(2) path {
|
||||
@apply hx:-translate-y-1;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
.hextra-scrollbar, .hextra-scrollbar * {
|
||||
scrollbar-width: thin; /* Firefox */
|
||||
scrollbar-color: oklch(55.55% 0 0 / 40%) transparent; /* Firefox */
|
||||
|
||||
scrollbar-gutter: stable;
|
||||
&::-webkit-scrollbar {
|
||||
@apply hx:w-3 hx:h-3;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply hx:bg-transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@apply hx:rounded-[10px];
|
||||
}
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
border: 3px solid transparent;
|
||||
background-color: var(--tw-shadow-color);
|
||||
background-clip: content-box;
|
||||
@apply hx:shadow-neutral-500/20 hx:hover:shadow-neutral-500/40;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
.hextra-search-wrapper {
|
||||
li {
|
||||
@apply hx:mx-2.5 hx:wrap-break-word hx:rounded-md hx:contrast-more:border hx:text-gray-800 hx:contrast-more:border-transparent hx:dark:text-gray-300;
|
||||
a {
|
||||
@apply hx:focus-visible:outline-none hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
|
||||
}
|
||||
|
||||
.hextra-search-title {
|
||||
@apply hx:text-base hx:font-semibold hx:leading-5;
|
||||
}
|
||||
|
||||
.hextra-search-active {
|
||||
@apply hx:rounded-md hx:bg-primary-500/10 hx:contrast-more:border-primary-500;
|
||||
}
|
||||
}
|
||||
|
||||
.hextra-search-no-result {
|
||||
@apply hx:block hx:select-none hx:p-8 hx:text-center hx:text-sm hx:text-gray-400;
|
||||
}
|
||||
|
||||
.hextra-search-prefix {
|
||||
@apply hx:mx-2.5 hx:mb-2 hx:mt-6 hx:select-none hx:border-b hx:border-black/10 hx:px-2.5 hx:pb-1.5 hx:text-xs hx:font-semibold
|
||||
hx:uppercase hx:text-gray-500 hx:first:mt-0 hx:dark:border-white/20 hx:dark:text-gray-300 hx:contrast-more:border-gray-600
|
||||
hx:contrast-more:text-gray-900 hx:contrast-more:dark:border-gray-50 hx:contrast-more:dark:text-gray-50;
|
||||
}
|
||||
|
||||
.hextra-search-excerpt {
|
||||
@apply hx:overflow-hidden hx:text-ellipsis hx:mt-1 hx:text-sm hx:leading-[1.35rem] hx:text-gray-600 hx:dark:text-gray-400 hx:contrast-more:dark:text-gray-50;
|
||||
display: -webkit-box;
|
||||
line-clamp: 1;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.hextra-search-match {
|
||||
@apply hx:text-primary-600;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@media (max-width: 48rem) {
|
||||
.hextra-sidebar-container {
|
||||
@apply hx:fixed hx:pt-[calc(var(--navbar-height)+var(--hextra-banner-height))] hx:top-0 hx:w-full hx:bottom-0 hx:z-15 hx:overscroll-contain hx:bg-white hx:dark:bg-dark;
|
||||
transition: transform 0.4s cubic-bezier(0.52, 0.16, 0.04, 1);
|
||||
will-change: transform, opacity;
|
||||
contain: layout style;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.hextra-sidebar-container {
|
||||
li > .hextra-sidebar-children {
|
||||
@apply hx:h-0;
|
||||
}
|
||||
li.open > .hextra-sidebar-children {
|
||||
@apply hx:h-auto hx:pt-1;
|
||||
}
|
||||
li.open > .hextra-sidebar-item > .hextra-sidebar-collapsible-button > svg > path {
|
||||
@apply hx:rotate-90;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
.hextra-steps {
|
||||
:where(h2, h3, h4, h5, h6):not(.no-step-marker) {
|
||||
counter-increment: step;
|
||||
@apply hx:ltr:before:ml-[-41px] hx:rtl:before:mr-[-44px];
|
||||
/* https://github.com/tailwindlabs/tailwindcss/issues/15597#issuecomment-2582673546 */
|
||||
@apply hx:before:bg-gray-100 hx:dark:before:bg-neutral-800;
|
||||
@apply hx:before:border-4 hx:before:border-white hx:dark:before:border-dark;
|
||||
&:before {
|
||||
content: counter(step);
|
||||
@apply hx:absolute hx:size-[33px];
|
||||
@apply hx:rounded-full hx:text-neutral-400 hx:text-base hx:font-normal hx:text-center hx:-indent-px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:lang(fa) .hextra-steps {
|
||||
:where(h2, h3, h4, h5, h6):not(.no-step-marker) {
|
||||
&:before {
|
||||
content: counter(step, persian);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Table of Contents Scroll Spy Styles */
|
||||
.hextra-toc a.hextra-toc-active {
|
||||
@apply hx:text-gray-900! hx:dark:text-gray-50! hx:transition-all hx:duration-200;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Code syntax highlight */
|
||||
@import "./chroma/light.css";
|
||||
@import "./chroma/dark.css";
|
||||
|
||||
.hextra-code-block {
|
||||
@apply hx:text-[.9em] hx:leading-5;
|
||||
|
||||
pre {
|
||||
@apply hx:text-[.9em] hx:bg-primary-700/5 hx:overflow-x-auto hx:font-medium hx:subpixel-antialiased hx:dark:bg-primary-300/10 hx:contrast-more:border hx:contrast-more:border-primary-900/20 hx:contrast-more:contrast-150 hx:contrast-more:dark:border-primary-100/40;
|
||||
}
|
||||
|
||||
.hextra-code-filename {
|
||||
@apply hx:absolute hx:top-0 hx:z-1 hx:w-full hx:truncate hx:rounded-t-xl hx:bg-primary-700/5 hx:py-2 hx:px-4 hx:text-xs hx:text-gray-700 hx:dark:bg-primary-300/10 hx:dark:text-gray-200;
|
||||
}
|
||||
|
||||
.hextra-code-filename + pre:not(.lntable pre) {
|
||||
/* Override padding for code blocks with filename but no highlight */
|
||||
@apply hx:pt-12;
|
||||
}
|
||||
}
|
||||
|
||||
.hextra-code-block pre:not(.lntable pre) {
|
||||
@apply hx:px-4 hx:mb-4 hx:py-4 hx:rounded-xl;
|
||||
}
|
||||
|
||||
.hextra-code-block div:nth-of-type(2) pre {
|
||||
@apply hx:pt-12 hx:pb-4;
|
||||
}
|
||||
|
||||
.chroma {
|
||||
.lntable {
|
||||
@apply hx:m-0 hx:block hx:w-auto hx:overflow-auto hx:rounded-xl;
|
||||
|
||||
pre {
|
||||
@apply hx:pt-4 hx:pb-4;
|
||||
}
|
||||
}
|
||||
.ln,
|
||||
.lnt:not(.hl > .lnt),
|
||||
.hl:not(.line) {
|
||||
@apply hx:pl-4 hx:pr-4 hx:min-w-[2.6rem] hx:text-neutral-600 hx:dark:text-neutral-300;
|
||||
}
|
||||
.lntd {
|
||||
@apply hx:p-0 hx:align-top;
|
||||
}
|
||||
.lntd:last-of-type {
|
||||
@apply hx:w-full;
|
||||
}
|
||||
/* LineHighlight */
|
||||
.hl {
|
||||
@apply hx:block hx:w-full hx:bg-primary-800/10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
hx:max-w-full
|
||||
@@ -0,0 +1,91 @@
|
||||
@import "tailwindcss" prefix(hx);
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--color-primary-50: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 47));
|
||||
--color-primary-100: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 44));
|
||||
--color-primary-200: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 36));
|
||||
--color-primary-300: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27));
|
||||
--color-primary-400: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 16));
|
||||
--color-primary-500: hsl(var(--primary-hue) var(--primary-saturation) var(--primary-lightness));
|
||||
--color-primary-600: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 45));
|
||||
--color-primary-700: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 39));
|
||||
--color-primary-800: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 32));
|
||||
--color-primary-900: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 24));
|
||||
--color-dark: #111;
|
||||
}
|
||||
|
||||
html {
|
||||
@apply hx:text-base hx:antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply hx:w-full hx:bg-white hx:dark:bg-dark hx:dark:text-gray-100;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-hue: 212deg;
|
||||
--primary-saturation: 100%;
|
||||
--primary-lightness: 50%;
|
||||
--navbar-height: 4rem;
|
||||
--hextra-banner-height: 2rem;
|
||||
--menu-height: 3.75rem; /* 60px */
|
||||
}
|
||||
|
||||
.dark {
|
||||
--primary-hue: 204deg;
|
||||
--primary-saturation: 100%;
|
||||
--primary-lightness: 50%;
|
||||
}
|
||||
|
||||
@utility hextra-focus {
|
||||
@apply hx:outline-none hx:ring-2 hx:ring-primary-200 hx:ring-offset-1 hx:ring-offset-primary-300 hx:dark:ring-primary-800 hx:dark:ring-offset-primary-700;
|
||||
}
|
||||
|
||||
@utility hextra-focus-visible {
|
||||
@apply hx:focus-visible:outline-none hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:focus-visible:ring-offset-1 hx:focus-visible:ring-offset-primary-300 hx:dark:focus-visible:ring-primary-800 hx:dark:focus-visible:ring-offset-primary-700;
|
||||
}
|
||||
|
||||
@utility hextra-focus-visible-inset {
|
||||
@apply hx:focus-visible:outline-none hx:focus-visible:ring-inset hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:dark:focus-visible:ring-primary-800 hx:focus-visible:ring-offset-0;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
abbr:where([title]) {
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:where(a, button, [role="tab"], [role="menuitem"], [role="menuitemradio"], input, select, textarea, [tabindex="0"]):not(
|
||||
[class*="hextra-focus-visible"]
|
||||
):focus-visible {
|
||||
@apply hx:hextra-focus;
|
||||
}
|
||||
}
|
||||
|
||||
@import "./typography.css";
|
||||
@import "./highlight.css";
|
||||
@import "./components/cards.css";
|
||||
@import "./components/steps.css";
|
||||
@import "./components/search.css";
|
||||
@import "./components/sidebar.css";
|
||||
@import "./components/banner.css";
|
||||
@import "./components/navbar.css";
|
||||
@import "./components/scrollbar.css";
|
||||
@import "./components/code-copy.css";
|
||||
@import "./components/hextra/feature-grid.css";
|
||||
@import "./components/jupyter.css";
|
||||
@import "./components/badge.css";
|
||||
@import "./components/toc.css";
|
||||
@import "./components/archives.css";
|
||||
@@ -0,0 +1,147 @@
|
||||
.content {
|
||||
:where(h1):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100;
|
||||
}
|
||||
:where(h2):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-10 hx:border-b hx:pb-1 hx:text-3xl hx:border-neutral-200/70 hx:contrast-more:border-neutral-400 hx:dark:border-primary-100/10 hx:contrast-more:dark:border-neutral-400;
|
||||
}
|
||||
:where(h3):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-2xl;
|
||||
}
|
||||
:where(h4):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-xl;
|
||||
}
|
||||
:where(h5):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-lg;
|
||||
}
|
||||
:where(h6):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:font-semibold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100 hx:mt-8 hx:text-base;
|
||||
}
|
||||
:where(p):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-6 hx:leading-7 hx:first:mt-0;
|
||||
}
|
||||
:where(a):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:text-primary-600 hx:underline hx:decoration-from-font;
|
||||
}
|
||||
:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-6 hx:border-gray-300 hx:italic hx:text-gray-700 hx:dark:border-gray-700 hx:dark:text-gray-400 hx:first:mt-0 hx:ltr:border-l-2 hx:ltr:pl-6 hx:rtl:border-r-2 hx:rtl:pr-6;
|
||||
}
|
||||
:where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:bg-primary-700/5 hx:mb-4 hx:overflow-x-auto hx:rounded-xl hx:font-medium hx:subpixel-antialiased hx:dark:bg-primary-300/10 hx:text-[.9em] hx:contrast-more:border hx:contrast-more:border-primary-900/20 hx:contrast-more:contrast-150 hx:contrast-more:dark:border-primary-100/40 hx:py-4;
|
||||
}
|
||||
:where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:border-black/4 hx:bg-black/3 hx:wrap-break-word hx:rounded-md hx:border hx:py-0.5 hx:px-[.25em] hx:text-[.9em] hx:dark:border-white/10 hx:dark:bg-white/10;
|
||||
}
|
||||
:where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:block hx:overflow-x-auto hx:my-6 hx:p-0 hx:first:mt-0 hx:w-full hx:text-sm hx:leading-5 hx:border-collapse;
|
||||
|
||||
thead {
|
||||
@apply hx:bg-gray-50 hx:dark:bg-gray-600/20;
|
||||
}
|
||||
tr {
|
||||
@apply hx:m-0 hx:border-t hx:border-gray-300 hx:p-0 hx:dark:border-gray-600;
|
||||
}
|
||||
th {
|
||||
@apply hx:m-0 hx:border hx:border-gray-300 hx:p-2 hx:font-semibold hx:dark:border-gray-600;
|
||||
}
|
||||
td {
|
||||
@apply hx:m-0 hx:border hx:border-gray-300 hx:p-2 hx:dark:border-gray-600;
|
||||
}
|
||||
}
|
||||
:where(ol):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-6 hx:list-decimal hx:first:mt-0 hx:ltr:ml-6 hx:rtl:mr-6;
|
||||
li {
|
||||
@apply hx:my-2;
|
||||
}
|
||||
}
|
||||
:where(ul):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-6 hx:list-disc hx:first:mt-0 hx:ltr:ml-6 hx:rtl:mr-6;
|
||||
li {
|
||||
@apply hx:my-2;
|
||||
}
|
||||
}
|
||||
/* Task lists - hide list markers for lists containing checkboxes */
|
||||
:where(ul):not(:where([class~=not-prose],[class~=not-prose] *)):has(li input[type="checkbox"]) {
|
||||
@apply hx:list-none;
|
||||
}
|
||||
/* This CSS rule targets the first nested unordered (ul) or ordered (ol) list
|
||||
inside the list item (li) of any parent ul or ol.
|
||||
The rule sets the top margin of the selected list to zero. */
|
||||
:where(ul, ol) > li > :where(ul, ol):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mt-0;
|
||||
}
|
||||
:where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:border-black/4 hx:bg-black/3 hx:wrap-break-word hx:rounded-md hx:border hx:py-0.5 hx:px-[.25em] hx:text-[.9em] hx:dark:border-white/10 hx:dark:bg-white/10;
|
||||
}
|
||||
:where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:bg-transparent hx:rounded-none hx:dark:bg-transparent;
|
||||
}
|
||||
:where(img):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:mx-auto hx:my-4 hx:rounded-md;
|
||||
}
|
||||
:where(figure):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
figcaption {
|
||||
@apply hx:text-sm hx:text-gray-500 hx:dark:text-gray-400 hx:mt-2 hx:block hx:text-center;
|
||||
}
|
||||
}
|
||||
/* Definition list */
|
||||
:where(dl):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
dt {
|
||||
@apply hx:mt-6 hx:font-semibold;
|
||||
}
|
||||
dd {
|
||||
@apply hx:my-2 hx:ps-6;
|
||||
}
|
||||
}
|
||||
/* Horizontal line */
|
||||
:where(hr):not(:where([class~=not-prose],[class~=not-prose] *)) {
|
||||
@apply hx:my-10 hx:first:mt-0 hx:last:mb-0 hx:border-gray-200 hx:dark:border-neutral-800;
|
||||
}
|
||||
.footnotes {
|
||||
@apply hx:mt-12 hx:text-sm;
|
||||
|
||||
hr {
|
||||
@apply hx:border-gray-200 hx:dark:border-neutral-800;
|
||||
}
|
||||
}
|
||||
.subheading-anchor {
|
||||
@apply hx:opacity-0 hx:transition-opacity hx:ltr:ml-1 hx:rtl:mr-1;
|
||||
|
||||
span:target + &,
|
||||
:hover > &,
|
||||
&:focus-visible {
|
||||
@apply hx:opacity-100;
|
||||
}
|
||||
|
||||
span + &,
|
||||
:hover > & {
|
||||
@apply hx:no-underline!;
|
||||
}
|
||||
|
||||
@apply hx:after:text-gray-300 hx:dark:after:text-neutral-700;
|
||||
&:after {
|
||||
@apply hx:content-['#'] hx:px-1;
|
||||
span:target + & {
|
||||
@apply hx:text-gray-400;
|
||||
@apply hx:dark:text-neutral-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article details > summary {
|
||||
&::-webkit-details-marker {
|
||||
@apply hx:hidden;
|
||||
}
|
||||
&::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='hx:h-5 hx:w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd' /%3E%3C/svg%3E");
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
vertical-align: -4px;
|
||||
padding: 0 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
:lang(fa) ol {
|
||||
list-style-type: persian;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Hugo template to derive CSS variables from site and page parameters */
|
||||
|
||||
/* Do not remove the following comment. It is used by Hugo to render CSS variables.
|
||||
{{- $layoutWidthValues := dict "normal" "80rem" "wide" "90rem" "full" "100%" -}}
|
||||
{{- $layoutWidthDefault := index $layoutWidthValues "normal" -}}
|
||||
|
||||
{{- $maxPageWidth := (index $layoutWidthValues (site.Params.page.width | default "normal")) | default $layoutWidthDefault -}}
|
||||
{{- $maxNavbarWidth := (index $layoutWidthValues (site.Params.navbar.width | default "normal")) | default $layoutWidthDefault -}}
|
||||
{{- $maxFooterWidth := (index $layoutWidthValues (site.Params.footer.width | default "normal")) | default $layoutWidthDefault -}}
|
||||
|
||||
{{- $maxContentWidth := "72rem" -}}
|
||||
*/
|
||||
|
||||
:root {
|
||||
--hextra-max-page-width: {{ $maxPageWidth }};
|
||||
--hextra-max-content-width: {{ $maxContentWidth }};
|
||||
--hextra-max-navbar-width: {{ $maxNavbarWidth }};
|
||||
--hextra-max-footer-width: {{ $maxFooterWidth }};
|
||||
}
|
||||
|
||||
.hextra-max-page-width {
|
||||
max-width: var(--hextra-max-page-width);
|
||||
}
|
||||
|
||||
.hextra-max-content-width {
|
||||
max-width: var(--hextra-max-content-width);
|
||||
}
|
||||
|
||||
.hextra-max-navbar-width {
|
||||
max-width: var(--hextra-max-navbar-width);
|
||||
}
|
||||
|
||||
.hextra-max-footer-width {
|
||||
max-width: var(--hextra-max-footer-width);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Back to top button
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const backToTop = document.querySelector("#backToTop");
|
||||
if (backToTop) {
|
||||
backToTop.addEventListener("click", scrollUp);
|
||||
document.addEventListener("scroll", (e) => {
|
||||
if (window.scrollY > 300) {
|
||||
backToTop.classList.remove("hx:opacity-0");
|
||||
backToTop.removeAttribute("tabindex");
|
||||
} else {
|
||||
backToTop.classList.add("hx:opacity-0");
|
||||
backToTop.setAttribute("tabindex", "-1");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function scrollUp() {
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
window.scroll({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: prefersReducedMotion ? 'auto' : 'smooth',
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// {{- if site.Params.banner }}
|
||||
(function () {
|
||||
const banner = document.querySelector(".hextra-banner")
|
||||
document.documentElement.style.setProperty("--hextra-banner-height", banner.clientHeight+"px");
|
||||
|
||||
const closeBtn = banner.querySelector(".hextra-banner-close-button");
|
||||
|
||||
closeBtn.addEventListener("click", () => {
|
||||
document.documentElement.classList.add("hextra-banner-hidden");
|
||||
document.documentElement.style.setProperty("--hextra-banner-height", "0px");
|
||||
|
||||
localStorage.setItem('{{ site.Params.banner.key | default `banner-closed` }}', "0");
|
||||
});
|
||||
})();
|
||||
// {{- end -}}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copy button for code blocks
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const getCopyIcon = () => {
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.innerHTML = `
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
`;
|
||||
svg.setAttribute('fill', 'none');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
svg.setAttribute('stroke', 'currentColor');
|
||||
svg.setAttribute('stroke-width', '2');
|
||||
return svg;
|
||||
}
|
||||
|
||||
const getSuccessIcon = () => {
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.innerHTML = `
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
||||
`;
|
||||
svg.setAttribute('fill', 'none');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
svg.setAttribute('stroke', 'currentColor');
|
||||
svg.setAttribute('stroke-width', '2');
|
||||
return svg;
|
||||
}
|
||||
|
||||
// Make scrollable code blocks focusable for keyboard users.
|
||||
const updateScrollableCodeBlocks = () => {
|
||||
document.querySelectorAll('.hextra-code-block pre, .highlight pre').forEach(function (pre) {
|
||||
if (pre.scrollWidth > pre.clientWidth) {
|
||||
pre.setAttribute('tabindex', '0');
|
||||
} else {
|
||||
pre.removeAttribute('tabindex');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateScrollableCodeBlocks();
|
||||
|
||||
let resizeRaf;
|
||||
window.addEventListener('resize', () => {
|
||||
if (resizeRaf) {
|
||||
cancelAnimationFrame(resizeRaf);
|
||||
}
|
||||
resizeRaf = requestAnimationFrame(updateScrollableCodeBlocks);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) {
|
||||
// Add copy and success icons
|
||||
button.querySelector('.hextra-copy-icon')?.appendChild(getCopyIcon());
|
||||
button.querySelector('.hextra-success-icon')?.appendChild(getSuccessIcon());
|
||||
|
||||
// Add click event listener for copy button
|
||||
button.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
// Get the code target
|
||||
const target = button.parentElement.previousElementSibling;
|
||||
let codeElement;
|
||||
if (target.tagName === 'CODE') {
|
||||
codeElement = target;
|
||||
} else {
|
||||
// Select the last code element in case line numbers are present
|
||||
const codeElements = target.querySelectorAll('code');
|
||||
codeElement = codeElements[codeElements.length - 1];
|
||||
}
|
||||
if (codeElement) {
|
||||
let code = codeElement.innerText;
|
||||
// Replace double newlines with single newlines in the innerText
|
||||
// as each line inside <span> has trailing newline '\n'
|
||||
if ("lang" in codeElement.dataset) {
|
||||
code = code.replace(/\n\n/g, '\n');
|
||||
}
|
||||
navigator.clipboard.writeText(code).then(function () {
|
||||
button.classList.add('copied');
|
||||
var originalLabel = button.getAttribute('aria-label');
|
||||
var copiedLabel = button.dataset.copiedLabel || 'Copied!';
|
||||
button.setAttribute('aria-label', copiedLabel);
|
||||
setTimeout(function () {
|
||||
button.classList.remove('copied');
|
||||
button.setAttribute('aria-label', originalLabel);
|
||||
}, 1000);
|
||||
}).catch(function (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
} else {
|
||||
console.error('Target element not found');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// {{ $faviconDarkExists := fileExists (path.Join "static" "favicon-dark.svg") }}
|
||||
(function () {
|
||||
const faviconEl = document.getElementById("favicon-svg");
|
||||
const faviconDarkExists = "{{ $faviconDarkExists }}" === "true";
|
||||
|
||||
if (faviconEl && faviconDarkExists) {
|
||||
const lightFavicon = '{{ "favicon.svg" | relURL }}';
|
||||
const darkFavicon = '{{ "favicon-dark.svg" | relURL }}';
|
||||
|
||||
const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
function updateFavicon(e) {
|
||||
faviconEl.href = e.matches ? darkFavicon : lightFavicon;
|
||||
}
|
||||
|
||||
// Set favicon on load
|
||||
updateFavicon(darkModeQuery);
|
||||
|
||||
// Listen for system preference changes
|
||||
darkModeQuery.addEventListener("change", updateFavicon);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,15 @@
|
||||
// Script for filetree shortcode collapsing/expanding folders used in the theme
|
||||
// ======================================================================
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const folders = document.querySelectorAll(".hextra-filetree-folder");
|
||||
folders.forEach(function (folder) {
|
||||
folder.addEventListener("click", function () {
|
||||
Array.from(folder.children).forEach(function (el) {
|
||||
el.dataset.state = el.dataset.state === "open" ? "closed" : "open";
|
||||
});
|
||||
var newState = folder.nextElementSibling.dataset.state === "open" ? "closed" : "open";
|
||||
folder.nextElementSibling.dataset.state = newState;
|
||||
folder.setAttribute('aria-expanded', newState === 'open' ? 'true' : 'false');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
(function () {
|
||||
const languageSwitchers = document.querySelectorAll('.hextra-language-switcher');
|
||||
const closeSwitcher = (switcher, focusSwitcher = false) => {
|
||||
switcher.dataset.state = 'closed';
|
||||
switcher.setAttribute('aria-expanded', 'false');
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
optionsElement.classList.add('hx:hidden');
|
||||
if (focusSwitcher) {
|
||||
switcher.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const openSwitcher = (switcher, focusTarget = "none") => {
|
||||
switcher.dataset.state = 'open';
|
||||
switcher.setAttribute('aria-expanded', 'true');
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
if (optionsElement.classList.contains('hx:hidden')) {
|
||||
toggleMenu(switcher);
|
||||
} else {
|
||||
resizeMenu(switcher);
|
||||
}
|
||||
|
||||
if (focusTarget !== "none") {
|
||||
const items = Array.from(optionsElement.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length > 0) {
|
||||
const target = focusTarget === "last" ? items[items.length - 1] : items[0];
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
languageSwitchers.forEach((switcher) => {
|
||||
switcher.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (switcher.dataset.state === 'open') {
|
||||
closeSwitcher(switcher);
|
||||
} else {
|
||||
openSwitcher(switcher);
|
||||
}
|
||||
});
|
||||
|
||||
switcher.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
openSwitcher(switcher, 'first');
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
openSwitcher(switcher, 'last');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.hextra-language-options[role=menu]').forEach((menu) => {
|
||||
menu.addEventListener('keydown', (e) => {
|
||||
const items = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length === 0) return;
|
||||
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case 'Escape': {
|
||||
e.preventDefault();
|
||||
const switcher = menu.previousElementSibling;
|
||||
if (switcher) {
|
||||
closeSwitcher(switcher, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => languageSwitchers.forEach(resizeMenu));
|
||||
|
||||
// Dismiss language switcher when clicking outside.
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.hextra-language-switcher') && !e.target.closest('.hextra-language-options')) {
|
||||
languageSwitchers.forEach((switcher) => {
|
||||
closeSwitcher(switcher);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,84 @@
|
||||
// Hamburger menu for mobile navigation
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const menu = document.querySelector('.hextra-hamburger-menu');
|
||||
const sidebarContainer = document.querySelector('.hextra-sidebar-container');
|
||||
const mobileQuery = window.matchMedia('(max-width: 767px)');
|
||||
|
||||
function isMenuOpen() {
|
||||
return menu.querySelector('svg').classList.contains('open');
|
||||
}
|
||||
|
||||
// On mobile, the sidebar is off-screen so hide it from assistive tech
|
||||
function syncAriaHidden() {
|
||||
if (mobileQuery.matches) {
|
||||
sidebarContainer.setAttribute('aria-hidden', isMenuOpen() ? 'false' : 'true');
|
||||
} else {
|
||||
sidebarContainer.removeAttribute('aria-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial state
|
||||
syncAriaHidden();
|
||||
mobileQuery.addEventListener('change', syncAriaHidden);
|
||||
|
||||
function toggleMenu(options = {}) {
|
||||
const { focusOnOpen = true } = options;
|
||||
|
||||
// Toggle the hamburger menu
|
||||
menu.querySelector('svg').classList.toggle('open');
|
||||
|
||||
// When the menu is open, we want to show the navigation sidebar
|
||||
sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,-100%,0)]');
|
||||
sidebarContainer.classList.toggle('hx:max-md:[transform:translate3d(0,0,0)]');
|
||||
|
||||
// When the menu is open, we want to prevent the body from scrolling
|
||||
document.body.classList.toggle('hx:overflow-hidden');
|
||||
document.body.classList.toggle('hx:md:overflow-auto');
|
||||
|
||||
// Sync aria-expanded and aria-hidden
|
||||
const isOpen = isMenuOpen();
|
||||
menu.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
syncAriaHidden();
|
||||
|
||||
// Move focus into sidebar when opening, restore when closing
|
||||
if (isOpen) {
|
||||
if (focusOnOpen) {
|
||||
const firstFocusable = sidebarContainer.querySelector('a, button, input, [tabindex="0"]');
|
||||
if (firstFocusable) firstFocusable.focus();
|
||||
}
|
||||
} else {
|
||||
menu.focus();
|
||||
}
|
||||
}
|
||||
|
||||
menu.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
// Pointer-initiated clicks on mobile should not force focus into the search input,
|
||||
// which opens the software keyboard immediately.
|
||||
toggleMenu({ focusOnOpen: e.detail === 0 });
|
||||
});
|
||||
|
||||
// Close menu on Escape key (mobile only)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && mobileQuery.matches && isMenuOpen()) {
|
||||
toggleMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// Select all anchor tags in the sidebar container
|
||||
const sidebarLinks = sidebarContainer.querySelectorAll('a');
|
||||
|
||||
// Add click event listener to each anchor tag
|
||||
sidebarLinks.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
// Check if the href attribute contains a hash symbol (links to a heading)
|
||||
if (link.getAttribute('href') && link.getAttribute('href').startsWith('#')) {
|
||||
// Only dismiss overlay on mobile view
|
||||
if (window.innerWidth < 768) {
|
||||
toggleMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
(function () {
|
||||
const hiddenClass = "hx:hidden";
|
||||
const dropdownToggles = document.querySelectorAll(".hextra-nav-menu-toggle");
|
||||
const closeDropdown = (toggle, focusToggle = false) => {
|
||||
toggle.dataset.state = "closed";
|
||||
toggle.setAttribute("aria-expanded", "false");
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
menuItemsElement.classList.add(hiddenClass);
|
||||
if (focusToggle) {
|
||||
toggle.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const openDropdown = (toggle, focusTarget = "none") => {
|
||||
// Close all other dropdowns first.
|
||||
dropdownToggles.forEach((otherToggle) => {
|
||||
if (otherToggle !== toggle) {
|
||||
closeDropdown(otherToggle);
|
||||
}
|
||||
});
|
||||
|
||||
toggle.dataset.state = "open";
|
||||
toggle.setAttribute("aria-expanded", "true");
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
|
||||
// Position dropdown centered with toggle.
|
||||
menuItemsElement.style.position = "absolute";
|
||||
menuItemsElement.style.top = "100%";
|
||||
menuItemsElement.style.left = "50%";
|
||||
menuItemsElement.style.transform = "translateX(-50%)";
|
||||
menuItemsElement.style.zIndex = "1000";
|
||||
menuItemsElement.classList.remove(hiddenClass);
|
||||
|
||||
if (focusTarget !== "none") {
|
||||
const items = Array.from(menuItemsElement.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length > 0) {
|
||||
const target = focusTarget === "last" ? items[items.length - 1] : items[0];
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
toggle.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Toggle current dropdown.
|
||||
const isOpen = toggle.dataset.state === "open";
|
||||
if (isOpen) {
|
||||
closeDropdown(toggle);
|
||||
} else {
|
||||
openDropdown(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
toggle.addEventListener("keydown", (e) => {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
openDropdown(toggle, "first");
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
openDropdown(toggle, "last");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".hextra-nav-menu-items[role=menu]").forEach((menu) => {
|
||||
menu.addEventListener("keydown", (e) => {
|
||||
const items = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length === 0) return;
|
||||
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowDown":
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case "ArrowUp":
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case "Home":
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case "End":
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case "Escape": {
|
||||
e.preventDefault();
|
||||
const toggle = menu.previousElementSibling;
|
||||
if (toggle) {
|
||||
closeDropdown(toggle, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Dismiss dropdown when clicking outside.
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.closest(".hextra-nav-menu-toggle") && !e.target.closest(".hextra-nav-menu-items")) {
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
closeDropdown(toggle);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Close dropdowns on escape key.
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
if (toggle.dataset.state === "open") {
|
||||
closeDropdown(toggle, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,172 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Pre-fetch markdown content for all copy buttons to avoid Safari NotAllowedError
|
||||
// Safari requires clipboard writes to happen synchronously within user gesture
|
||||
const copyButtons = document.querySelectorAll('.hextra-page-context-menu-copy');
|
||||
const contentCache = new Map();
|
||||
|
||||
// Pre-fetch content for each button on page load
|
||||
copyButtons.forEach(button => {
|
||||
const url = button.dataset.url;
|
||||
if (url) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) return response.text();
|
||||
throw new Error('Failed to fetch');
|
||||
})
|
||||
.then(markdown => contentCache.set(url, markdown))
|
||||
.catch(error => console.error('Failed to pre-fetch markdown:', error));
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize copy buttons with synchronous clipboard access
|
||||
copyButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const url = button.dataset.url;
|
||||
const markdown = contentCache.get(url);
|
||||
|
||||
if (markdown) {
|
||||
// Synchronous clipboard write initiation - works in Safari
|
||||
navigator.clipboard.writeText(markdown)
|
||||
.then(() => {
|
||||
button.classList.add('copied');
|
||||
setTimeout(() => button.classList.remove('copied'), 1000);
|
||||
})
|
||||
.catch(error => console.error('Failed to copy markdown:', error));
|
||||
} else {
|
||||
// Fallback: fetch and copy (may fail in Safari if content not pre-fetched)
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Failed to fetch');
|
||||
return response.text();
|
||||
})
|
||||
.then(text => {
|
||||
contentCache.set(url, text);
|
||||
return navigator.clipboard.writeText(text);
|
||||
})
|
||||
.then(() => {
|
||||
button.classList.add('copied');
|
||||
setTimeout(() => button.classList.remove('copied'), 1000);
|
||||
})
|
||||
.catch(error => console.error('Failed to copy markdown:', error));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize dropdown toggles
|
||||
const dropdownToggles = document.querySelectorAll('.hextra-page-context-menu-toggle');
|
||||
dropdownToggles.forEach(toggle => {
|
||||
const container = toggle.closest('.hextra-page-context-menu');
|
||||
const menu = container.querySelector('.hextra-page-context-menu-dropdown');
|
||||
const chevron = toggle.querySelector('[data-chevron]');
|
||||
|
||||
toggle.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const isOpen = toggle.dataset.state === 'open';
|
||||
|
||||
// Close all other dropdowns first
|
||||
dropdownToggles.forEach(t => {
|
||||
if (t !== toggle) {
|
||||
t.dataset.state = 'closed';
|
||||
t.setAttribute('aria-expanded', 'false');
|
||||
const otherContainer = t.closest('.hextra-page-context-menu');
|
||||
const otherMenu = otherContainer.querySelector('.hextra-page-context-menu-dropdown');
|
||||
const otherChevron = t.querySelector('[data-chevron]');
|
||||
otherMenu.classList.add('hx:hidden');
|
||||
if (otherChevron) {
|
||||
otherChevron.style.transform = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle current
|
||||
toggle.dataset.state = isOpen ? 'closed' : 'open';
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
||||
menu.classList.toggle('hx:hidden', isOpen);
|
||||
|
||||
// Rotate chevron icon
|
||||
if (chevron) {
|
||||
chevron.style.transform = isOpen ? '' : 'rotate(180deg)';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
// Check if click is outside any dropdown container
|
||||
const isOutside = !e.target.closest('.hextra-page-context-menu');
|
||||
if (isOutside) {
|
||||
dropdownToggles.forEach(toggle => {
|
||||
toggle.dataset.state = 'closed';
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
const container = toggle.closest('.hextra-page-context-menu');
|
||||
const menu = container.querySelector('.hextra-page-context-menu-dropdown');
|
||||
const chevron = toggle.querySelector('[data-chevron]');
|
||||
menu.classList.add('hx:hidden');
|
||||
if (chevron) {
|
||||
chevron.style.transform = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Close dropdown on Escape key and return focus to toggle
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
dropdownToggles.forEach(toggle => {
|
||||
if (toggle.dataset.state === 'open') {
|
||||
const container = toggle.closest('.hextra-page-context-menu');
|
||||
closeDropdown(container);
|
||||
toggle.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Helper to close dropdown
|
||||
const closeDropdown = (container) => {
|
||||
if (!container) return;
|
||||
|
||||
const toggle = container.querySelector('.hextra-page-context-menu-toggle');
|
||||
const menu = container.querySelector('.hextra-page-context-menu-dropdown');
|
||||
|
||||
if (!toggle || !menu) return;
|
||||
|
||||
const chevron = toggle.querySelector('[data-chevron]');
|
||||
toggle.dataset.state = 'closed';
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
menu.classList.add('hx:hidden');
|
||||
if (chevron) {
|
||||
chevron.style.transform = '';
|
||||
}
|
||||
};
|
||||
|
||||
// Handle dropdown menu copy action
|
||||
document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="copy"]').forEach(btn => {
|
||||
btn.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
const container = btn.closest('.hextra-page-context-menu');
|
||||
if (!container) return;
|
||||
|
||||
const copyBtn = container.querySelector('.hextra-page-context-menu-copy');
|
||||
if (!copyBtn) return;
|
||||
|
||||
closeDropdown(container);
|
||||
copyBtn.click();
|
||||
});
|
||||
});
|
||||
|
||||
// Handle dropdown menu view action
|
||||
document.querySelectorAll('.hextra-page-context-menu-dropdown button[data-action="view"]').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const container = btn.closest('.hextra-page-context-menu');
|
||||
if (!container) return;
|
||||
|
||||
const url = btn.dataset.url;
|
||||
if (!url) return;
|
||||
|
||||
closeDropdown(container);
|
||||
window.open(url, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
scrollToActiveItem();
|
||||
enableCollapsibles();
|
||||
});
|
||||
|
||||
function enableCollapsibles() {
|
||||
const buttons = document.querySelectorAll(".hextra-sidebar-collapsible-button");
|
||||
buttons.forEach(function (button) {
|
||||
button.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
const list = button.closest('li');
|
||||
if (list) {
|
||||
list.classList.toggle("open");
|
||||
button.setAttribute('aria-expanded', list.classList.contains('open') ? 'true' : 'false');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function scrollToActiveItem() {
|
||||
const sidebarScrollbar = document.querySelector("aside.hextra-sidebar-container > .hextra-scrollbar");
|
||||
const activeItems = document.querySelectorAll(".hextra-sidebar-active-item");
|
||||
const visibleActiveItem = Array.from(activeItems).find(function (activeItem) {
|
||||
return activeItem.getBoundingClientRect().height > 0;
|
||||
});
|
||||
|
||||
if (!visibleActiveItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const yOffset = visibleActiveItem.clientHeight;
|
||||
const yDistance = visibleActiveItem.getBoundingClientRect().top - sidebarScrollbar.getBoundingClientRect().top;
|
||||
sidebarScrollbar.scrollTo({
|
||||
behavior: "instant",
|
||||
top: yDistance - yOffset
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
function computeMenuTranslation(switcher, optionsElement) {
|
||||
// Calculate the position of a language options element.
|
||||
const switcherRect = switcher.getBoundingClientRect();
|
||||
|
||||
// Must be called before optionsElement.clientWidth.
|
||||
optionsElement.style.minWidth = `${Math.max(switcherRect.width, 50)}px`;
|
||||
|
||||
const isOnTop = switcher.dataset.location === 'top';
|
||||
const isOnBottom = switcher.dataset.location === 'bottom';
|
||||
const isOnBottomRight = switcher.dataset.location === 'bottom-right';
|
||||
const isRTL = document.documentElement.dir === 'rtl'
|
||||
|
||||
// Stuck on the left side of the switcher.
|
||||
let x = switcherRect.left;
|
||||
|
||||
if (isOnTop && !isRTL || isOnBottom && isRTL || isOnBottomRight && !isRTL) {
|
||||
// Stuck on the right side of the switcher.
|
||||
x = switcherRect.right - optionsElement.clientWidth;
|
||||
}
|
||||
|
||||
// Stuck on the top of the switcher.
|
||||
let y = switcherRect.top - window.innerHeight - 10;
|
||||
|
||||
if (isOnTop) {
|
||||
// Stuck on the bottom of the switcher.
|
||||
y = switcherRect.top - window.innerHeight + optionsElement.clientHeight + switcher.clientHeight + 4;
|
||||
}
|
||||
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
||||
function toggleMenu(switcher) {
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
|
||||
optionsElement.classList.toggle('hx:hidden');
|
||||
|
||||
// Calculate the position of a language options element.
|
||||
const translate = computeMenuTranslation(switcher, optionsElement);
|
||||
|
||||
optionsElement.style.transform = `translate3d(${translate.x}px, ${translate.y}px, 0)`;
|
||||
}
|
||||
|
||||
function resizeMenu(switcher) {
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
|
||||
if (optionsElement.classList.contains('hx:hidden')) return;
|
||||
|
||||
// Calculate the position of a language options element.
|
||||
const translate = computeMenuTranslation(switcher, optionsElement);
|
||||
|
||||
optionsElement.style.transform = `translate3d(${translate.x}px, ${translate.y}px, 0)`;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
(function () {
|
||||
function updateGroup(container, index) {
|
||||
const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
|
||||
tabs.forEach((tab, i) => {
|
||||
tab.dataset.state = i === index ? 'selected' : '';
|
||||
if (i === index) {
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
tab.tabIndex = 0;
|
||||
} else {
|
||||
tab.setAttribute('aria-selected', 'false');
|
||||
tab.tabIndex = -1;
|
||||
}
|
||||
});
|
||||
const panelsContainer = container.parentElement.nextElementSibling;
|
||||
if (!panelsContainer) return;
|
||||
Array.from(panelsContainer.children).forEach((panel, i) => {
|
||||
panel.dataset.state = i === index ? 'selected' : '';
|
||||
panel.setAttribute('aria-hidden', i === index ? 'false' : 'true');
|
||||
if (i === index) {
|
||||
panel.tabIndex = 0;
|
||||
} else {
|
||||
panel.removeAttribute('tabindex');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const syncGroups = document.querySelectorAll('[data-tab-group]');
|
||||
|
||||
syncGroups.forEach((group) => {
|
||||
const key = encodeURIComponent(group.dataset.tabGroup);
|
||||
const saved = localStorage.getItem('hextra-tab-' + key);
|
||||
if (saved !== null) {
|
||||
updateGroup(group, parseInt(saved, 10));
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.hextra-tabs-toggle').forEach((button) => {
|
||||
button.addEventListener('click', function (e) {
|
||||
const targetButton = e.currentTarget;
|
||||
const container = targetButton.parentElement;
|
||||
const index = Array.from(container.querySelectorAll('.hextra-tabs-toggle')).indexOf(
|
||||
targetButton
|
||||
);
|
||||
|
||||
if (container.dataset.tabGroup) {
|
||||
// Sync behavior: update all tab groups with the same name
|
||||
const tabGroupValue = container.dataset.tabGroup;
|
||||
const key = encodeURIComponent(tabGroupValue);
|
||||
document
|
||||
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
|
||||
.forEach((grp) => updateGroup(grp, index));
|
||||
localStorage.setItem('hextra-tab-' + key, index.toString());
|
||||
} else {
|
||||
// Non-sync behavior: update only this specific tab group
|
||||
updateGroup(container, index);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation for tabs
|
||||
button.addEventListener('keydown', function (e) {
|
||||
const container = button.parentElement;
|
||||
const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
|
||||
const currentIndex = tabs.indexOf(button);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % tabs.length;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
newIndex = 0;
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
newIndex = tabs.length - 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.dataset.tabGroup) {
|
||||
const tabGroupValue = container.dataset.tabGroup;
|
||||
const key = encodeURIComponent(tabGroupValue);
|
||||
document
|
||||
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
|
||||
.forEach((grp) => updateGroup(grp, newIndex));
|
||||
localStorage.setItem('hextra-tab-' + key, newIndex.toString());
|
||||
} else {
|
||||
updateGroup(container, newIndex);
|
||||
}
|
||||
tabs[newIndex].focus();
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,15 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Hugo task lists render bare checkboxes; provide an accessible name.
|
||||
document.querySelectorAll("main#content li > input[type='checkbox']").forEach(function (checkbox) {
|
||||
if (checkbox.hasAttribute("aria-label") || checkbox.hasAttribute("aria-labelledby")) {
|
||||
return;
|
||||
}
|
||||
var listItem = checkbox.closest("li");
|
||||
if (!listItem) return;
|
||||
|
||||
var labelText = listItem.textContent.replace(/\s+/g, " ").trim();
|
||||
if (labelText) {
|
||||
checkbox.setAttribute("aria-label", labelText);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
// Light / Dark theme toggle
|
||||
(function () {
|
||||
const defaultTheme = '{{ site.Params.theme.default | default `system`}}'
|
||||
const themes = ["light", "dark"];
|
||||
|
||||
const themeToggleButtons = document.querySelectorAll(".hextra-theme-toggle");
|
||||
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options button[role=menuitemradio]");
|
||||
|
||||
function applyTheme(theme) {
|
||||
theme = themes.includes(theme) ? theme : "system";
|
||||
|
||||
themeToggleButtons.forEach((btn) => btn.parentElement.dataset.theme = theme );
|
||||
themeToggleOptions.forEach((option) => {
|
||||
option.setAttribute('aria-checked', option.dataset.item === theme ? 'true' : 'false');
|
||||
});
|
||||
|
||||
localStorage.setItem("color-theme", theme);
|
||||
}
|
||||
|
||||
function switchTheme(theme) {
|
||||
setTheme(theme);
|
||||
applyTheme(theme);
|
||||
}
|
||||
|
||||
const colorTheme = "color-theme" in localStorage ? localStorage.getItem("color-theme") : defaultTheme;
|
||||
switchTheme(colorTheme);
|
||||
|
||||
// Add click event handler to the menu items.
|
||||
themeToggleOptions.forEach((option) => {
|
||||
option.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
switchTheme(option.dataset.item);
|
||||
})
|
||||
})
|
||||
|
||||
// Add click event handler to the buttons
|
||||
themeToggleButtons.forEach((toggler) => {
|
||||
toggler.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
toggler.dataset.state = toggler.dataset.state === 'open' ? 'closed' : 'open';
|
||||
toggleMenu(toggler);
|
||||
const isOpen = toggler.dataset.state === 'open';
|
||||
toggler.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
|
||||
// Focus first menuitem when opening
|
||||
if (isOpen) {
|
||||
const firstItem = toggler.nextElementSibling.querySelector('button[role=menuitemradio]');
|
||||
if (firstItem) firstItem.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => themeToggleButtons.forEach(resizeMenu))
|
||||
|
||||
// Dismiss the menu when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.hextra-theme-toggle') === null) {
|
||||
themeToggleButtons.forEach((toggler) => {
|
||||
toggler.dataset.state = 'closed';
|
||||
toggler.setAttribute('aria-expanded', 'false');
|
||||
toggler.nextElementSibling.classList.add('hx:hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation for the theme menu
|
||||
document.querySelectorAll('.hextra-theme-toggle-options[role=menu]').forEach(function (menu) {
|
||||
menu.addEventListener('keydown', function (e) {
|
||||
const items = Array.from(menu.querySelectorAll('button[role=menuitemradio]'));
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
var toggler = menu.previousElementSibling;
|
||||
toggler.dataset.state = 'closed';
|
||||
toggler.setAttribute('aria-expanded', 'false');
|
||||
menu.classList.add('hx:hidden');
|
||||
toggler.focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||
if (localStorage.getItem("color-theme") === "system") {
|
||||
setTheme("system");
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* TOC Scroll - Highlights active TOC links based on visible headings
|
||||
*
|
||||
* Uses Intersection Observer to track heading visibility and applies
|
||||
* 'hextra-toc-active' class to corresponding TOC links. Selects the
|
||||
* topmost heading when multiple are visible.
|
||||
*
|
||||
* Requires: .hextra-toc element, matching heading IDs, toc.css styles
|
||||
*/
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const toc = document.querySelector(".hextra-toc");
|
||||
if (!toc) return;
|
||||
|
||||
const tocLinks = toc.querySelectorAll('a[href^="#"]');
|
||||
if (tocLinks.length === 0) return;
|
||||
|
||||
const headingIds = Array.from(tocLinks).map((link) => link.getAttribute("href").substring(1));
|
||||
|
||||
const headings = headingIds.map((id) => document.getElementById(decodeURIComponent(id))).filter(Boolean);
|
||||
if (headings.length === 0) return;
|
||||
|
||||
let currentActiveLink = null;
|
||||
let isHashNavigation = false;
|
||||
|
||||
// Create intersection observer
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
// Skip observer updates during hash navigation
|
||||
if (isHashNavigation) return;
|
||||
|
||||
const visibleHeadings = entries.filter((entry) => entry.isIntersecting).map((entry) => entry.target);
|
||||
|
||||
if (visibleHeadings.length === 0) return;
|
||||
|
||||
// Find the heading closest to the top of the viewport
|
||||
const topMostHeading = visibleHeadings.reduce((closest, heading) => {
|
||||
const headingTop = heading.getBoundingClientRect().top;
|
||||
const closestTop = closest.getBoundingClientRect().top;
|
||||
return Math.abs(headingTop) < Math.abs(closestTop) ? heading : closest;
|
||||
});
|
||||
|
||||
// Encode the id and make it lowercase to match the TOC link
|
||||
const targetId = encodeURIComponent(topMostHeading.id).toLowerCase();
|
||||
const targetLink = toc.querySelector(`a[href="#${targetId}"]`);
|
||||
|
||||
if (targetLink && targetLink !== currentActiveLink) {
|
||||
// Remove active class from previous link
|
||||
if (currentActiveLink) {
|
||||
currentActiveLink.classList.remove("hextra-toc-active");
|
||||
currentActiveLink.removeAttribute("aria-current");
|
||||
}
|
||||
|
||||
// Add active class to current link
|
||||
targetLink.classList.add("hextra-toc-active");
|
||||
targetLink.setAttribute("aria-current", "location");
|
||||
currentActiveLink = targetLink;
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: "-20px 0px -60% 0px", // Adjust sensitivity
|
||||
threshold: [0, 0.1, 0.5, 1],
|
||||
}
|
||||
);
|
||||
|
||||
// Observe all headings
|
||||
headings.forEach((heading) => observer.observe(heading));
|
||||
|
||||
// Handle direct navigation to page with hash
|
||||
function handleHashNavigation() {
|
||||
const hash = window.location.hash; // already url encoded
|
||||
if (hash) {
|
||||
const targetLink = toc.querySelector(`a[href="${hash}"]`);
|
||||
if (targetLink) {
|
||||
// Disable observer temporarily during hash navigation
|
||||
isHashNavigation = true;
|
||||
|
||||
if (currentActiveLink) {
|
||||
currentActiveLink.classList.remove("hextra-toc-active");
|
||||
currentActiveLink.removeAttribute("aria-current");
|
||||
}
|
||||
targetLink.classList.add("hextra-toc-active");
|
||||
targetLink.setAttribute("aria-current", "location");
|
||||
currentActiveLink = targetLink;
|
||||
|
||||
// Re-enable observer after scroll settles
|
||||
setTimeout(() => { isHashNavigation = false; }, 500);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle hash changes navigation
|
||||
window.addEventListener("hashchange", handleHashNavigation);
|
||||
|
||||
// Handle initial load
|
||||
setTimeout(handleHashNavigation, 100);
|
||||
});
|
||||
@@ -0,0 +1,492 @@
|
||||
// Search functionality using FlexSearch.
|
||||
|
||||
// Change shortcut key to cmd+k on Mac, iPad or iPhone.
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (/iPad|iPhone|Macintosh/.test(navigator.userAgent)) {
|
||||
// select the kbd element under the .hextra-search-wrapper class
|
||||
const keys = document.querySelectorAll(".hextra-search-wrapper kbd");
|
||||
keys.forEach(key => {
|
||||
key.innerHTML = '<span class="hx:text-xs">⌘</span>K';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Render the search data as JSON.
|
||||
// {{ $searchDataFile := printf "%s.search-data.json" .Language.Lang }}
|
||||
// {{ $searchData := resources.Get "json/search-data.json" | resources.ExecuteAsTemplate $searchDataFile . }}
|
||||
// {{ if hugo.IsProduction }}
|
||||
// {{ $searchData := $searchData | minify | fingerprint }}
|
||||
// {{ end }}
|
||||
// {{ $noResultsFound := (T "noResultsFound") | default "No results found." }}
|
||||
|
||||
(function () {
|
||||
const searchDataURL = '{{ $searchData.RelPermalink }}';
|
||||
const resultsFoundTemplate = '{{ (T "resultsFound") | default "%d results found" }}';
|
||||
|
||||
const inputElements = document.querySelectorAll('.hextra-search-input');
|
||||
for (const el of inputElements) {
|
||||
el.addEventListener('focus', init);
|
||||
el.addEventListener('keyup', search);
|
||||
el.addEventListener('keydown', handleKeyDown);
|
||||
el.addEventListener('input', handleInputChange);
|
||||
}
|
||||
|
||||
const shortcutElements = document.querySelectorAll('.hextra-search-wrapper kbd');
|
||||
|
||||
function setShortcutElementsOpacity(opacity) {
|
||||
shortcutElements.forEach(el => {
|
||||
el.style.opacity = opacity;
|
||||
});
|
||||
}
|
||||
|
||||
function handleInputChange(e) {
|
||||
const opacity = e.target.value.length > 0 ? 0 : 100;
|
||||
setShortcutElementsOpacity(opacity);
|
||||
}
|
||||
|
||||
// Get the search wrapper, input, and results elements.
|
||||
function getActiveSearchElement() {
|
||||
const inputs = Array.from(document.querySelectorAll('.hextra-search-wrapper')).filter(el => el.clientHeight > 0);
|
||||
if (inputs.length === 1) {
|
||||
return {
|
||||
wrapper: inputs[0],
|
||||
inputElement: inputs[0].querySelector('.hextra-search-input'),
|
||||
resultsElement: inputs[0].querySelector('.hextra-search-results')
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const INPUTS = ['input', 'select', 'button', 'textarea']
|
||||
|
||||
// Focus the search input when pressing ctrl+k/cmd+k or /.
|
||||
document.addEventListener('keydown', function (e) {
|
||||
const { inputElement } = getActiveSearchElement();
|
||||
if (!inputElement) return;
|
||||
|
||||
const activeElement = document.activeElement;
|
||||
const tagName = activeElement && activeElement.tagName;
|
||||
if (
|
||||
inputElement === activeElement ||
|
||||
!tagName ||
|
||||
INPUTS.includes(tagName) ||
|
||||
(activeElement && activeElement.isContentEditable))
|
||||
return;
|
||||
|
||||
if (
|
||||
e.key === '/' ||
|
||||
(e.key === 'k' &&
|
||||
(e.metaKey /* for Mac */ || /* for non-Mac */ e.ctrlKey))
|
||||
) {
|
||||
e.preventDefault();
|
||||
inputElement.focus();
|
||||
} else if (e.key === 'Escape' && inputElement.value) {
|
||||
inputElement.blur();
|
||||
}
|
||||
});
|
||||
|
||||
// Dismiss the search results when clicking outside the search box.
|
||||
document.addEventListener('mousedown', function (e) {
|
||||
const { inputElement, resultsElement } = getActiveSearchElement();
|
||||
if (!inputElement || !resultsElement) return;
|
||||
if (
|
||||
e.target !== inputElement &&
|
||||
e.target !== resultsElement &&
|
||||
!resultsElement.contains(e.target)
|
||||
) {
|
||||
setShortcutElementsOpacity(100);
|
||||
hideSearchResults();
|
||||
}
|
||||
});
|
||||
|
||||
// Get the currently active result and its index.
|
||||
function getActiveResult() {
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
if (!resultsElement) return { result: undefined, index: -1 };
|
||||
|
||||
const result = resultsElement.querySelector('.hextra-search-active');
|
||||
if (!result) return { result: undefined, index: -1 };
|
||||
|
||||
const index = parseInt(result.dataset.index, 10);
|
||||
return { result, index };
|
||||
}
|
||||
|
||||
// Set the active result by index.
|
||||
function setActiveResult(index) {
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
if (!resultsElement) return;
|
||||
|
||||
const { result: activeResult } = getActiveResult();
|
||||
activeResult && activeResult.classList.remove('hextra-search-active');
|
||||
const result = resultsElement.querySelector(`[data-index="${index}"]`);
|
||||
if (result) {
|
||||
result.classList.add('hextra-search-active');
|
||||
result.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of search results from the DOM.
|
||||
function getResultsLength() {
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
if (!resultsElement) return 0;
|
||||
return resultsElement.dataset.count;
|
||||
}
|
||||
|
||||
// Finish the search by hiding the results and clearing the input.
|
||||
function finishSearch() {
|
||||
const { inputElement } = getActiveSearchElement();
|
||||
if (!inputElement) return;
|
||||
hideSearchResults();
|
||||
inputElement.value = '';
|
||||
inputElement.blur();
|
||||
}
|
||||
|
||||
function hideSearchResults() {
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
if (!resultsElement) return;
|
||||
resultsElement.classList.add('hx:hidden');
|
||||
}
|
||||
|
||||
// Handle keyboard events.
|
||||
function handleKeyDown(e) {
|
||||
const { inputElement } = getActiveSearchElement();
|
||||
if (!inputElement) return;
|
||||
|
||||
const resultsLength = getResultsLength();
|
||||
const { result: activeResult, index: activeIndex } = getActiveResult();
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
if (activeIndex > 0) setActiveResult(activeIndex - 1);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
if (activeIndex + 1 < resultsLength) setActiveResult(activeIndex + 1);
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
if (activeResult) {
|
||||
activeResult.click();
|
||||
}
|
||||
finishSearch();
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
hideSearchResults();
|
||||
// Clear the input when pressing escape
|
||||
inputElement.value = '';
|
||||
inputElement.dispatchEvent(new Event('input'));
|
||||
// Remove focus from the input
|
||||
inputElement.blur();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the search.
|
||||
function init(e) {
|
||||
e.target.removeEventListener('focus', init);
|
||||
if (!(window.pageIndex && window.sectionIndex)) {
|
||||
preloadIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloads the search index by fetching data and adding it to the FlexSearch index.
|
||||
* @returns {Promise<void>} A promise that resolves when the index is preloaded.
|
||||
*/
|
||||
async function preloadIndex() {
|
||||
const tokenize = '{{- site.Params.search.flexsearch.tokenize | default "forward" -}}';
|
||||
|
||||
// https://github.com/TryGhost/Ghost/pull/21148
|
||||
const regex = new RegExp(
|
||||
`[\u{4E00}-\u{9FFF}\u{3040}-\u{309F}\u{30A0}-\u{30FF}\u{AC00}-\u{D7A3}\u{3400}-\u{4DBF}\u{20000}-\u{2A6DF}\u{2A700}-\u{2B73F}\u{2B740}-\u{2B81F}\u{2B820}-\u{2CEAF}\u{2CEB0}-\u{2EBEF}\u{30000}-\u{3134F}\u{31350}-\u{323AF}\u{2EBF0}-\u{2EE5F}\u{F900}-\u{FAFF}\u{2F800}-\u{2FA1F}]|[0-9A-Za-zа-я\u00C0-\u017F\u0400-\u04FF\u0600-\u06FF\u0980-\u09FF\u1E00-\u1EFF\u0590-\u05FF]+`,
|
||||
'mug'
|
||||
);
|
||||
const encode = (str) => { return ('' + str).toLowerCase().match(regex) ?? []; }
|
||||
|
||||
window.pageIndex = new FlexSearch.Document({
|
||||
tokenize,
|
||||
encode,
|
||||
cache: 100,
|
||||
document: {
|
||||
id: 'id',
|
||||
store: ['title', 'crumb'],
|
||||
index: "content"
|
||||
}
|
||||
});
|
||||
|
||||
window.sectionIndex = new FlexSearch.Document({
|
||||
tokenize,
|
||||
encode,
|
||||
cache: 100,
|
||||
document: {
|
||||
id: 'id',
|
||||
store: ['title', 'content', 'url', 'display', 'crumb'],
|
||||
index: "content",
|
||||
tag: [{
|
||||
field: "pageId"
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
const resp = await fetch(searchDataURL);
|
||||
const data = await resp.json();
|
||||
let pageId = 0;
|
||||
for (const route in data) {
|
||||
let pageContent = '';
|
||||
++pageId;
|
||||
const urlParts = route.split('/').filter(x => x != "" && !x.startsWith('#'));
|
||||
|
||||
let crumb = '';
|
||||
let searchUrl = '/';
|
||||
for (let i = 0; i < urlParts.length; i++) {
|
||||
const urlPart = urlParts[i];
|
||||
searchUrl += urlPart + '/'
|
||||
|
||||
const crumbData = data[searchUrl];
|
||||
if (!crumbData) {
|
||||
console.debug('Excluded page', searchUrl, '- will not be included for search result breadcrumb for', route);
|
||||
continue;
|
||||
}
|
||||
|
||||
let title = data[searchUrl].title;
|
||||
if (title == "_index") {
|
||||
title = urlPart.split("-").map(x => x).join(" ");
|
||||
}
|
||||
crumb += title;
|
||||
|
||||
if (i < urlParts.length - 1) {
|
||||
crumb += ' > ';
|
||||
}
|
||||
}
|
||||
|
||||
for (const heading in data[route].data) {
|
||||
const [hash, text] = heading.split('#');
|
||||
const url = route.trimEnd('/') + (hash ? '#' + hash : '');
|
||||
const title = text || data[route].title;
|
||||
|
||||
const content = data[route].data[heading] || '';
|
||||
const paragraphs = content.split('\n').filter(Boolean);
|
||||
|
||||
sectionIndex.add({
|
||||
id: url,
|
||||
url,
|
||||
title,
|
||||
crumb,
|
||||
pageId: `page_${pageId}`,
|
||||
content: title,
|
||||
...(paragraphs[0] && { display: paragraphs[0] })
|
||||
});
|
||||
|
||||
for (let i = 0; i < paragraphs.length; i++) {
|
||||
sectionIndex.add({
|
||||
id: `${url}_${i}`,
|
||||
url,
|
||||
title,
|
||||
crumb,
|
||||
pageId: `page_${pageId}`,
|
||||
content: paragraphs[i]
|
||||
});
|
||||
}
|
||||
|
||||
pageContent += ` ${title} ${content}`;
|
||||
}
|
||||
|
||||
window.pageIndex.add({
|
||||
id: pageId,
|
||||
title: data[route].title,
|
||||
crumb,
|
||||
content: pageContent
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search based on the provided query and displays the results.
|
||||
* @param {Event} e - The event object.
|
||||
*/
|
||||
function search(e) {
|
||||
const query = e.target.value;
|
||||
if (!e.target.value) {
|
||||
hideSearchResults();
|
||||
return;
|
||||
}
|
||||
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
while (resultsElement.firstChild) {
|
||||
resultsElement.removeChild(resultsElement.firstChild);
|
||||
}
|
||||
resultsElement.classList.remove('hx:hidden');
|
||||
|
||||
// Configurable search limits with sensible defaults
|
||||
const maxPageResults = parseInt('{{- site.Params.search.flexsearch.maxPageResults | default 20 -}}', 10);
|
||||
const maxSectionResults = parseInt('{{- site.Params.search.flexsearch.maxSectionResults | default 10 -}}', 10);
|
||||
const pageResults = window.pageIndex.search(query, maxPageResults, { enrich: true, suggest: true })[0]?.result || [];
|
||||
|
||||
const results = [];
|
||||
const pageTitleMatches = {};
|
||||
|
||||
for (let i = 0; i < pageResults.length; i++) {
|
||||
const result = pageResults[i];
|
||||
pageTitleMatches[i] = 0;
|
||||
|
||||
const sectionResults = window.sectionIndex.search(query,
|
||||
{ enrich: true, suggest: true, tag: { 'pageId': `page_${result.id}` } })[0]?.result || [];
|
||||
let isFirstItemOfPage = true
|
||||
const occurred = {}
|
||||
|
||||
const nResults = Math.min(sectionResults.length, maxSectionResults);
|
||||
for (let j = 0; j < nResults; j++) {
|
||||
const { doc } = sectionResults[j]
|
||||
const isMatchingTitle = doc.display !== undefined
|
||||
if (isMatchingTitle) {
|
||||
pageTitleMatches[i]++
|
||||
}
|
||||
const { url, title } = doc
|
||||
const content = doc.display || doc.content
|
||||
|
||||
if (occurred[url + '@' + content]) continue
|
||||
occurred[url + '@' + content] = true
|
||||
results.push({
|
||||
_page_rk: i,
|
||||
_section_rk: j,
|
||||
route: url,
|
||||
prefix: isFirstItemOfPage ? result.doc.crumb : undefined,
|
||||
children: { title, content }
|
||||
})
|
||||
isFirstItemOfPage = false
|
||||
}
|
||||
}
|
||||
const sortedResults = results
|
||||
.sort((a, b) => {
|
||||
// Sort by number of matches in the title.
|
||||
if (a._page_rk === b._page_rk) {
|
||||
return a._section_rk - b._section_rk
|
||||
}
|
||||
if (pageTitleMatches[a._page_rk] !== pageTitleMatches[b._page_rk]) {
|
||||
return pageTitleMatches[b._page_rk] - pageTitleMatches[a._page_rk]
|
||||
}
|
||||
return a._page_rk - b._page_rk
|
||||
})
|
||||
.map(res => ({
|
||||
id: `${res._page_rk}_${res._section_rk}`,
|
||||
route: res.route,
|
||||
prefix: res.prefix,
|
||||
children: res.children
|
||||
}));
|
||||
displayResults(sortedResults, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the search results on the page.
|
||||
*
|
||||
* @param {Array} results - The array of search results.
|
||||
* @param {string} query - The search query.
|
||||
*/
|
||||
function displayResults(results, query) {
|
||||
const { resultsElement } = getActiveSearchElement();
|
||||
if (!resultsElement) return;
|
||||
|
||||
if (!results.length) {
|
||||
resultsElement.innerHTML = `<span class="hextra-search-no-result">{{ $noResultsFound | safeHTML }}</span>`;
|
||||
// Announce no results to screen readers
|
||||
const wrapper = resultsElement.closest('.hextra-search-wrapper');
|
||||
const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null;
|
||||
if (statusEl) {
|
||||
statusEl.textContent = '{{ $noResultsFound | safeHTML }}';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Append text with highlighted matches using safe text nodes.
|
||||
function appendHighlightedText(container, text, query) {
|
||||
if (!text) return;
|
||||
if (!query) {
|
||||
container.textContent = text;
|
||||
return;
|
||||
}
|
||||
|
||||
const escapedQuery = query.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
if (!escapedQuery) {
|
||||
container.textContent = text;
|
||||
return;
|
||||
}
|
||||
|
||||
const regex = new RegExp(escapedQuery, 'gi');
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
container.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
|
||||
}
|
||||
const span = document.createElement('span');
|
||||
span.className = 'hextra-search-match';
|
||||
span.textContent = match[0];
|
||||
container.appendChild(span);
|
||||
lastIndex = match.index + match[0].length;
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
container.appendChild(document.createTextNode(text.slice(lastIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(e) {
|
||||
const target = e.target.closest('a');
|
||||
if (target) {
|
||||
const active = resultsElement.querySelector('a.hextra-search-active');
|
||||
if (active) {
|
||||
active.classList.remove('hextra-search-active');
|
||||
}
|
||||
target.classList.add('hextra-search-active');
|
||||
}
|
||||
}
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i];
|
||||
if (result.prefix) {
|
||||
const prefix = document.createElement('div');
|
||||
prefix.className = 'hextra-search-prefix';
|
||||
prefix.textContent = result.prefix;
|
||||
fragment.appendChild(prefix);
|
||||
}
|
||||
const li = document.createElement('li');
|
||||
const link = document.createElement('a');
|
||||
link.dataset.index = i;
|
||||
link.href = result.route;
|
||||
if (i === 0) {
|
||||
link.classList.add('hextra-search-active');
|
||||
}
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.className = 'hextra-search-title';
|
||||
appendHighlightedText(title, result.children.title, query);
|
||||
link.appendChild(title);
|
||||
|
||||
if (result.children.content) {
|
||||
const excerpt = document.createElement('div');
|
||||
excerpt.className = 'hextra-search-excerpt';
|
||||
appendHighlightedText(excerpt, result.children.content, query);
|
||||
link.appendChild(excerpt);
|
||||
}
|
||||
|
||||
li.appendChild(link);
|
||||
li.addEventListener('mousemove', handleMouseMove);
|
||||
li.addEventListener('keydown', handleKeyDown);
|
||||
link.addEventListener('click', finishSearch);
|
||||
fragment.appendChild(li);
|
||||
}
|
||||
resultsElement.appendChild(fragment);
|
||||
resultsElement.dataset.count = results.length;
|
||||
|
||||
// Announce results count to screen readers
|
||||
const wrapper = resultsElement.closest('.hextra-search-wrapper');
|
||||
const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null;
|
||||
if (statusEl) {
|
||||
statusEl.textContent = results.length > 0
|
||||
? resultsFoundTemplate.replace('%d', results.length.toString())
|
||||
: '{{ $noResultsFound | safeHTML }}';
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
// The section must not be in the banner.js (body) file because it can create a quick flash.
|
||||
|
||||
if (localStorage.getItem('{{ site.Params.banner.key | default `banner-closed` }}')) {
|
||||
document.documentElement.style.setProperty("--hextra-banner-height", "0px");
|
||||
document.documentElement.classList.add("hextra-banner-hidden");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// The section must not be in the theme.js (body) file because it can create a quick flash (switch between light and dark).
|
||||
|
||||
function setTheme(theme) {
|
||||
document.documentElement.classList.remove("light", "dark");
|
||||
|
||||
if (theme !== "light" && theme !== "dark") {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
}
|
||||
|
||||
document.documentElement.classList.add(theme);
|
||||
document.documentElement.style.colorScheme = theme;
|
||||
}
|
||||
|
||||
setTheme("color-theme" in localStorage ? localStorage.getItem("color-theme") : '{{ site.Params.theme.default | default `system`}}')
|
||||
@@ -0,0 +1,38 @@
|
||||
{{/* FlexSearch Index Data */}}
|
||||
{{- $indexType := site.Params.search.flexsearch.index | default "content" -}}
|
||||
|
||||
{{- if not (in (slice "content" "summary" "heading" "title" ) $indexType) -}}
|
||||
{{- errorf "unknown flexsearch index type: %s" $indexType -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $pages := where .Site.Pages "Kind" "in" (slice "page" "section") -}}
|
||||
{{- $pages = where $pages "Params.excludeSearch" "!=" true -}}
|
||||
{{- $pages = where $pages "Content" "!=" "" -}}
|
||||
|
||||
{{- $output := dict -}}
|
||||
|
||||
{{- range $index, $page := $pages -}}
|
||||
{{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}}
|
||||
{{- $pageLink := $page.RelPermalink -}}
|
||||
{{- $data := partial "utils/fragments" (dict "context" $page "type" $indexType) -}}
|
||||
{{- $output = $output | merge (dict $pageLink (dict "title" $pageTitle "data" $data)) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Extract glossary data entries */}}
|
||||
{{- $glossaryEntries := dict -}}
|
||||
{{- $siteData := partial "utils/hugo-compat/site-data.html" . -}}
|
||||
{{- with (index $siteData .Site.Language.Lang "termbase") -}}
|
||||
{{- range . -}}
|
||||
{{- $entry := cond (.abbr) (printf "%s %s %s" .abbr .term .definition) (printf "%s %s" .term .definition) -}}
|
||||
{{- $glossaryEntries = $glossaryEntries | merge (dict .term $entry) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $glossary := dict
|
||||
"title" "Glossary"
|
||||
"data" $glossaryEntries
|
||||
-}}
|
||||
|
||||
{{- $output = $output | merge (dict (relLangURL "glossary") $glossary )}}
|
||||
|
||||
{{- $output | jsonify -}}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Specify the base URL
|
||||
BASE_URL=${1:-"http://localhost:1313"}
|
||||
|
||||
echo "Using base URL: $BASE_URL"
|
||||
|
||||
# Version configuration - modify these arrays to specify versions to build
|
||||
# MAIN_VERSION format: "ref:display_name:source_dir"
|
||||
# VERSIONS format: "ref:display_name:source_dir" where source_dir is either "docs" or "exampleSite"
|
||||
MAIN_VERSION="$(git tag --list "v[0-9]*" --sort=-v:refname | head -n 1):latest:docs"
|
||||
VERSIONS=(
|
||||
"main:latest:docs" # latest version always builds from main
|
||||
"v0.11.3:v0.11:docs"
|
||||
"v0.10.3:v0.10:exampleSite"
|
||||
)
|
||||
|
||||
# Parse main version
|
||||
IFS=':' read -r MAIN_REF MAIN_NAME MAIN_DIR <<< "$MAIN_VERSION"
|
||||
|
||||
# Ensure clean public directory
|
||||
rm -rf public
|
||||
mkdir -p public
|
||||
mkdir -p public/versions
|
||||
|
||||
# Checkout and build main site
|
||||
git checkout $MAIN_REF
|
||||
GIT_HASH=$(git rev-parse --short HEAD)
|
||||
echo "Building main site from $MAIN_REF (commit: $GIT_HASH)"
|
||||
hugo \
|
||||
--minify \
|
||||
--themesDir=../.. --source=$MAIN_DIR \
|
||||
--baseURL "$BASE_URL/" \
|
||||
--destination=../public
|
||||
|
||||
# Build all versions
|
||||
for VERSION in "${VERSIONS[@]}"; do
|
||||
IFS=':' read -r REF NAME DIR <<< "$VERSION"
|
||||
|
||||
git checkout $REF
|
||||
GIT_HASH=$(git rev-parse --short HEAD)
|
||||
echo "Building version $NAME from $REF (commit: $GIT_HASH)"
|
||||
|
||||
mkdir -p "public/versions/$NAME"
|
||||
hugo \
|
||||
--minify \
|
||||
--themesDir=../.. --source=$DIR \
|
||||
--baseURL "$BASE_URL/versions/$NAME/" \
|
||||
--destination="../public/versions/$NAME"
|
||||
done
|
||||
|
||||
# Return to main branch
|
||||
git checkout main
|
||||
|
||||
echo "Build completed"
|
||||
@@ -0,0 +1,312 @@
|
||||
# SVG icons
|
||||
#
|
||||
# Example usage in templates using the icon.html partial:
|
||||
#
|
||||
# {{ partial "utils/icon.html" (dict "name" "github" "attributes" "height=24") }}
|
||||
|
||||
contrast: >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M 11.996094,2 C 6.4986225,2.0192368 2.03125,6.5024993 2.03125,12 c 0,5.497501 4.4673725,9.980763 9.964844,10 H 12 12.0039 c 5.497471,-0.01924 9.964844,-4.502499 9.964844,-10 0,-5.4975007 -4.467373,-9.9807632 -9.964844,-10 H 12 Z M 12,4 c 4.417218,0.017598 7.96875,3.5822356 7.96875,8 0,4.417764 -3.551532,7.982402 -7.96875,8 z" />
|
||||
</svg>
|
||||
|
||||
github: >
|
||||
<svg fill="currentColor" viewBox="3 3 18 18">
|
||||
<path d="M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z"></path>
|
||||
</svg>
|
||||
|
||||
codeberg: >
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.955.49A12 12 0 0 0 0 12.49a12 12 0 0 0 1.832 6.373L11.838 5.928a.187.14 0 0 1 .324 0l10.006 12.935A12 12 0 0 0 24 12.49a12 12 0 0 0-12-12 12 12 0 0 0-.045 0zm.375 6.467 4.416 16.553a12 12 0 0 0 5.137-4.213z"/>
|
||||
</svg>
|
||||
|
||||
gitlab: >
|
||||
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="m23.6 9.593l-.033-.086L20.3.98a.851.851 0 0 0-.336-.405a.875.875 0 0 0-1 .054a.875.875 0 0 0-.29.44L16.47 7.818H7.537L5.333 1.07a.857.857 0 0 0-.29-.441a.875.875 0 0 0-1-.054a.859.859 0 0 0-.336.405L.433 9.502l-.032.086a6.066 6.066 0 0 0 2.012 7.01l.01.009l.03.021l4.977 3.727l2.462 1.863l1.5 1.132a1.009 1.009 0 0 0 1.22 0l1.499-1.132l2.461-1.863l5.006-3.75l.013-.01a6.068 6.068 0 0 0 2.01-7.002"/>
|
||||
</svg>
|
||||
|
||||
bitbucket: >
|
||||
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M.778 1.213a.768.768 0 0 0-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 0 0 .77-.646l3.27-20.03a.768.768 0 0 0-.768-.891zM14.52 15.53H9.522L8.17 8.466h7.561z" />
|
||||
</svg>
|
||||
|
||||
hextra: <svg viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="m 105.50024,22.224647 c -9.59169,-5.537563 -21.40871,-5.537563 -31.000093,0 L 39.054693,42.689119 C 29.463353,48.226675 23.55484,58.460531 23.55484,69.535642 v 40.928918 c 0,11.07542 5.908513,21.3092 15.499853,26.84652 l 35.445453,20.46446 c 9.591313,5.53732 21.408404,5.53732 31.000094,0 l 35.44507,-20.46446 c 9.59131,-5.53732 15.49985,-15.7711 15.49985,-26.84652 V 69.535642 c 0,-11.075111 -5.90854,-21.308967 -15.49985,-26.846523 z M 34.112797,85.737639 c -1.384445,2.397827 -1.384445,5.352099 0,7.749927 l 24.781554,42.922974 c 1.38437,2.39783 3.942853,3.87496 6.711592,3.87496 h 49.563107 c 2.76905,0 5.3273,-1.47713 6.71144,-3.87496 l 24.78194,-42.922974 c 1.38414,-2.397828 1.38414,-5.3521 0,-7.749927 L 121.88049,42.814746 c -1.38414,-2.397828 -3.94239,-3.874964 -6.71144,-3.874964 H 65.605944 c -2.768739,0 -5.327223,1.477059 -6.711592,3.874964 z" style="stroke-width:0.774993" /></svg>
|
||||
|
||||
hugo: <svg viewBox="0 0 370 391" xmlns="http://www.w3.org/2000/svg"><g clip-rule="evenodd" fill-rule="evenodd"><path d="m207.5 22.4 114.4 66.6c13.5 7.9 21.9 22.4 21.9 38v136.4c0 17.3-9.3 33.3-24.5 41.8l-113.5 63.9a49.06 49.06 0 0 1 -48.5-.2l-104.5-60.1c-16.4-9.5-26.6-27-26.6-45.9v-129.5c0-19.1 9.9-36.8 26.1-46.8l102.8-63.5c16-9.9 36.2-10.1 52.4-.7z" fill="#ff4088" stroke="#c9177e" stroke-width="27" /><path d="m105.6 298.2v-207.2h43.4v75.5h71.9v-75.5h43.5v207.2h-43.5v-90.6h-71.9v90.6z" fill="#fff" /></g></svg>
|
||||
hugo-full: >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" stroke-width="27" aria-label="Logo" viewBox="0 0 1493 391">
|
||||
<path fill="#ebb951" stroke="#fcd804" d="M1345.211 24.704l112.262 64.305a43 43 0 0 1 21.627 37.312v142.237a40 40 0 0 1-20.702 35.037l-120.886 66.584a42 42 0 0 1-41.216-.389l-106.242-61.155a57 57 0 0 1-28.564-49.4V138.71a64 64 0 0 1 31.172-54.939l98.01-58.564a54 54 0 0 1 54.54-.503z"/>
|
||||
<path fill="#33ba91" stroke="#00a88a" d="M958.07 22.82l117.31 66.78a41 41 0 0 1 20.72 35.64v139.5a45 45 0 0 1-23.1 39.32L955.68 369.4a44 44 0 0 1-43.54-.41l-105.82-61.6a56 56 0 0 1-27.83-48.4V140.07a68 68 0 0 1 33.23-58.44l98.06-58.35a48 48 0 0 1 48.3-.46z"/>
|
||||
<path fill="#0594cb" stroke="#0083c0" d="M575.26 20.97l117.23 68.9a40 40 0 0 1 19.73 34.27l.73 138.67a48 48 0 0 1-24.64 42.2l-115.13 64.11a45 45 0 0 1-44.53-.42l-105.83-61.6a55 55 0 0 1-27.33-47.53V136.52a63 63 0 0 1 29.87-53.59l99.3-61.4a49 49 0 0 1 50.6-.56z"/>
|
||||
<path fill="#ff4088" stroke="#c9177e" d="M195.81 24.13l114.41 66.54a44 44 0 0 1 21.88 38.04v136.43a48 48 0 0 1-24.45 41.82L194.1 370.9a49 49 0 0 1-48.48-.23L41.05 310.48a53 53 0 0 1-26.56-45.93V135.08a55 55 0 0 1 26.1-46.8l102.8-63.46a51 51 0 0 1 52.42-.69z"/>
|
||||
<path fill="#fff" d="M1320.72 89.15c58.79 0 106.52 47.73 106.52 106.51 0 58.8-47.73 106.52-106.52 106.52-58.78 0-106.52-47.73-106.52-106.52 0-58.78 47.74-106.51 106.52-106.51zm0 39.57c36.95 0 66.94 30 66.94 66.94a66.97 66.97 0 0 1-66.94 66.94c-36.95 0-66.94-29.99-66.94-66.94a66.97 66.97 0 0 1 66.93-66.94h.01zm-283.8 65.31c0 47.18-8.94 60.93-26.81 80.58-17.87 19.65-41.57 27.57-71.1 27.57-27 0-48.75-9.58-67.61-26.23-20.88-18.45-36.08-47.04-36.08-78.95 0-31.37 11.72-58.48 32.49-78.67 18.22-17.67 45.34-29.18 73.3-29.18 33.77 0 68.83 15.98 90.44 47.53l-31.73 26.82c-13.45-25.03-32.94-33.46-60.82-34.26-30.83-.88-64.77 28.53-62.25 67.75 1.4 21.94 11.65 59.65 60.96 66.57 25.9 3.63 55.36-24.02 55.36-39.04H944.4v-37.5h92.5V194l.02.03zm-562.6-94.65h42.29v112.17c0 17.8.49 29.33 1.47 34.61 1.69 8.48 4.81 14.37 11.17 19.5 6.37 5.13 13.8 6.59 24.84 6.59 11.2 0 14.96-1.74 20.66-6.6 5.69-4.85 9.12-9.46 10.28-16.53 1.15-7.07 3.07-18.8 3.07-35.18V99.38h42.28v108.78c0 24.86-1.07 42.43-3.21 52.69-2.14 10.27-6.08 18.93-11.82 26-5.74 7.06-13.42 12.69-23.03 16.88-9.62 4.19-22.16 6.28-37.65 6.28-18.7 0-32.87-2.28-42.52-6.85-9.66-4.57-17.3-10.5-22.9-17.8-5.61-7.3-9.3-14.95-11.08-22.96-2.58-11.86-3.88-29.38-3.88-52.55V99.38h.03zM93.91 299.92V92.7h43.35v75.48h71.92V92.7h43.48v207.22h-43.48v-90.61h-71.92v90.61z"/>
|
||||
</svg>
|
||||
|
||||
jupyter: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799a1.784 1.799 0 0 1 1.784-1.799a1.784 1.799 0 0 1 1.783 1.799M20.582 1.427a1.415 1.427 0 0 1-1.415 1.428a1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427M4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056m7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.36 9.36 0 0 0-3.393-4.547a9.24 9.24 0 0 0-5.377-1.728A9.24 9.24 0 0 0 6.95 3.73a9.36 9.36 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424m.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.36 9.36 0 0 0 3.394 4.547A9.24 9.24 0 0 0 12.33 21a9.24 9.24 0 0 0 5.377-1.729a9.36 9.36 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425Z" /></svg>
|
||||
|
||||
warning: <svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"></path></svg>
|
||||
one: <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="-1 0 19 19"><path d="M16.417 9.6A7.917 7.917 0 1 1 8.5 1.683 7.917 7.917 0 0 1 16.417 9.6zM9.666 6.508H8.248L6.09 8.09l.806 1.103 1.222-.945v4.816h1.547z"></path></svg>
|
||||
cards: <svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 0 0 4.5 9v.878m13.5-3A2.25 2.25 0 0 1 19.5 9v.878m0 0a2.246 2.246 0 0 0-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0 1 21 12v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6c0-.98.626-1.813 1.5-2.122"></path></svg>
|
||||
copy: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
|
||||
hamburger-menu: <svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><g><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8H20"></path></g><g><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16H20"></path></g></svg>
|
||||
markdown: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.56 18H3.44C2.65 18 2 17.37 2 16.59V7.41C2 6.63 2.65 6 3.44 6h17.12c.79 0 1.44.63 1.44 1.41v9.18c0 .78-.65 1.41-1.44 1.41M6.81 15.19v-3.66l1.92 2.35l1.92-2.35v3.66h1.93V8.81h-1.93l-1.92 2.35l-1.92-2.35H4.89v6.38h1.92M19.69 12h-1.92V8.81h-1.92V12h-1.93l2.89 3.28L19.69 12Z"/></svg>
|
||||
folder-tree: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="m 114.47781,81.538059 c 0,-13.785609 -11.07754,-24.923135 -24.78889,-24.923135 -13.711352,0 -24.788885,11.137526 -24.788885,24.923135 V 355.69254 c 0,27.49333 22.232532,49.84627 49.577775,49.84627 H 263.21112 V 355.69254 H 114.47781 V 181.2306 H 263.21112 V 131.38433 H 114.47781 Z M 288,206.15373 c 0,13.78561 11.07753,24.92314 24.78888,24.92314 h 173.5222 c 13.71135,0 24.78888,-11.13753 24.78888,-24.92314 v -99.69254 c 0,-13.785605 -11.07753,-24.923131 -24.78888,-24.923131 h -76.45822 c -6.58454,0 -12.85923,-2.648083 -17.50715,-7.321171 L 382.04283,63.936095 c -4.64791,-4.673088 -10.9226,-7.321171 -17.50715,-7.321171 h -51.7468 C 299.07753,56.614924 288,67.75245 288,81.538059 Z m 0,224.30821 c 0,13.78561 11.07753,24.92314 24.78888,24.92314 h 173.5222 c 13.71135,0 24.78888,-11.13753 24.78888,-24.92314 V 330.7694 c 0,-13.78561 -11.07753,-24.92313 -24.78888,-24.92313 h -76.45822 c -6.58454,0 -12.85923,-2.64808 -17.50715,-7.32117 l -10.30288,-10.35868 c -4.64791,-4.67309 -10.9226,-7.32117 -17.50715,-7.32117 h -51.7468 C 299.07753,280.84525 288,291.98278 288,305.76838 Z" style="stroke-width:0.776747" /></svg>
|
||||
card: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M14 11V4H1v7h13Zm1-7v7a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1ZM2 5.25A.25.25 0 0 1 2.25 5h3.5a.25.25 0 0 1 .25.25v4.5a.25.25 0 0 1-.25.25h-3.5A.25.25 0 0 1 2 9.75v-4.5ZM7.5 7a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3ZM7 9.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5ZM7.5 5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4Z" clip-rule="evenodd"/></svg>
|
||||
|
||||
arrow-up-right: >-
|
||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
arrow-up-left: >-
|
||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m7.7574 14.828v-7.0711m0 0 7.0711 1e-7m-7.0711-1e-7 8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
||||
# Icons from heroicons v1 outlined https://github.com/tailwindlabs/heroicons/tree/v1
|
||||
academic-cap: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path d="M12 14l9-5-9-5-9 5 9 5z"/><path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"/><path stroke-linecap="round" stroke-linejoin="round" d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"/></svg>
|
||||
adjustments: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/></svg>
|
||||
annotation: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/></svg>
|
||||
archive: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"/></svg>
|
||||
arrow-circle-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 13l-3 3m0 0l-3-3m3 3V8m0 13a9 9 0 110-18 9 9 0 010 18z"/></svg>
|
||||
arrow-circle-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 15l-3-3m0 0l3-3m-3 3h8M3 12a9 9 0 1118 0 9 9 0 01-18 0z"/></svg>
|
||||
arrow-circle-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
arrow-circle-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 11l3-3m0 0l3 3m-3-3v8m0-13a9 9 0 110 18 9 9 0 010-18z"/></svg>
|
||||
arrow-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 14l-7 7m0 0l-7-7m7 7V3"/></svg>
|
||||
arrow-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
|
||||
arrow-narrow-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 17l-4 4m0 0l-4-4m4 4V3"/></svg>
|
||||
arrow-narrow-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 16l-4-4m0 0l4-4m-4 4h18"/></svg>
|
||||
arrow-narrow-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
|
||||
arrow-narrow-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7l4-4m0 0l4 4m-4-4v18"/></svg>
|
||||
arrow-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
|
||||
arrow-sm-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 13l-5 5m0 0l-5-5m5 5V6"/></svg>
|
||||
arrow-sm-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 17l-5-5m0 0l5-5m-5 5h12"/></svg>
|
||||
arrow-sm-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6"/></svg>
|
||||
arrow-sm-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 11l5-5m0 0l5 5m-5-5v12"/></svg>
|
||||
arrow-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 10l7-7m0 0l7 7m-7-7v18"/></svg>
|
||||
arrows-expand: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"/></svg>
|
||||
at-symbol: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"/></svg>
|
||||
backspace: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M3 12l6.414 6.414a2 2 0 001.414.586H19a2 2 0 002-2V7a2 2 0 00-2-2h-8.172a2 2 0 00-1.414.586L3 12z"/></svg>
|
||||
badge-check: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"/></svg>
|
||||
ban: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/></svg>
|
||||
beaker: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"/></svg>
|
||||
bell: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
|
||||
book-open: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/></svg>
|
||||
bookmark: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/></svg>
|
||||
bookmark-alt: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 4v12l-4-2-4 2V4M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
briefcase: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
|
||||
cake: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 15.546c-.523 0-1.046.151-1.5.454a2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.701 2.701 0 00-1.5-.454M9 6v2m3-2v2m3-2v2M9 3h.01M12 3h.01M15 3h.01M21 21v-7a2 2 0 00-2-2H5a2 2 0 00-2 2v7h18zm-3-9v-2a2 2 0 00-2-2H8a2 2 0 00-2 2v2h12z"/></svg>
|
||||
calculator: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>
|
||||
calendar: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
camera: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
||||
cash: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z"/></svg>
|
||||
chart-bar: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg>
|
||||
chart-pie: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z"/><path stroke-linecap="round" stroke-linejoin="round" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z"/></svg>
|
||||
chart-square-bar: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
chat: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
|
||||
chat-alt: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/></svg>
|
||||
chat-alt-2: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"/></svg>
|
||||
check: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
check-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
chevron-double-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13l-7 7-7-7m14-8l-7 7-7-7"/></svg>
|
||||
chevron-double-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 19l-7-7 7-7m8 14l-7-7 7-7"/></svg>
|
||||
chevron-double-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 5l7 7-7 7M5 5l7 7-7 7"/></svg>
|
||||
chevron-double-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 11l7-7 7 7M5 19l7-7 7 7"/></svg>
|
||||
chevron-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg>
|
||||
chevron-left: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/></svg>
|
||||
chevron-right: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/></svg>
|
||||
chevron-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg>
|
||||
chip: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/></svg>
|
||||
clipboard: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
|
||||
clipboard-check: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/></svg>
|
||||
clipboard-copy: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"/></svg>
|
||||
clipboard-list: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/></svg>
|
||||
clock: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
cloud: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/></svg>
|
||||
cloud-download: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"/></svg>
|
||||
cloud-upload: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/></svg>
|
||||
code: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>
|
||||
cog: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
||||
collection: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg>
|
||||
color-swatch: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"/></svg>
|
||||
credit-card: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>
|
||||
cube: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/></svg>
|
||||
cube-transparent: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14 10l-2 1m0 0l-2-1m2 1v2.5M20 7l-2 1m2-1l-2-1m2 1v2.5M14 4l-2-1-2 1M4 7l2-1M4 7l2 1M4 7v2.5M12 21l-2-1m2 1l2-1m-2 1v-2.5M6 18l-2-1v-2.5M18 18l2-1v-2.5"/></svg>
|
||||
currency-bangladeshi: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 11V9a2 2 0 00-2-2m2 4v4a2 2 0 104 0v-1m-4-3H9m2 0h4m6 1a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
currency-dollar: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
currency-euro: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14.121 15.536c-1.171 1.952-3.07 1.952-4.242 0-1.172-1.953-1.172-5.119 0-7.072 1.171-1.952 3.07-1.952 4.242 0M8 10.5h4m-4 3h4m9-1.5a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
currency-pound: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9a2 2 0 10-4 0v5a2 2 0 01-2 2h6m-6-4h4m8 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
currency-rupee: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 8h6m-5 0a3 3 0 110 6H9l3 3m-3-6h6m6 1a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
currency-yen: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 8l3 5m0 0l3-5m-3 5v4m-3-5h6m-6 3h6m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
cursor-click: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"/></svg>
|
||||
database: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/></svg>
|
||||
desktop-computer: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
|
||||
device-mobile: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>
|
||||
device-tablet: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 18h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>
|
||||
document: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>
|
||||
document-add: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
document-download: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
document-duplicate: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"/></svg>
|
||||
document-remove: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
document-report: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
document-search: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 21h7a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v11m0 5l4.879-4.879m0 0a3 3 0 104.243-4.242 3 3 0 00-4.243 4.242z"/></svg>
|
||||
document-text: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
|
||||
dots-circle-horizontal: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
dots-horizontal: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"/></svg>
|
||||
dots-vertical: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"/></svg>
|
||||
download: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
|
||||
duplicate: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
|
||||
emoji-happy: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
emoji-sad: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
exclamation: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
|
||||
exclamation-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
external-link: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
|
||||
eye: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
|
||||
eye-off: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"/></svg>
|
||||
fast-forward: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11.933 12.8a1 1 0 000-1.6L6.6 7.2A1 1 0 005 8v8a1 1 0 001.6.8l5.333-4zM19.933 12.8a1 1 0 000-1.6l-5.333-4A1 1 0 0013 8v8a1 1 0 001.6.8l5.333-4z"/></svg>
|
||||
film: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z"/></svg>
|
||||
filter: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"/></svg>
|
||||
finger-print: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4"/></svg>
|
||||
fire: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z"/><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 16.121A3 3 0 1012.015 11L11 14H9c0 .768.293 1.536.879 2.121z"/></svg>
|
||||
flag: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9"/></svg>
|
||||
folder: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/></svg>
|
||||
folder-add: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/></svg>
|
||||
folder-download: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 10v6m0 0l-3-3m3 3l3-3M3 17V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/></svg>
|
||||
folder-open: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 19a2 2 0 01-2-2V7a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1M5 19h14a2 2 0 002-2v-5a2 2 0 00-2-2H9a2 2 0 00-2 2v5a2 2 0 01-2 2z"/></svg>
|
||||
folder-remove: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6M3 17V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/></svg>
|
||||
gift: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7"/></svg>
|
||||
globe: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
globe-alt: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/></svg>
|
||||
hand: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 11.5V14m0-2.5v-6a1.5 1.5 0 113 0m-3 6a1.5 1.5 0 00-3 0v2a7.5 7.5 0 0015 0v-5a1.5 1.5 0 00-3 0m-6-3V11m0-5.5v-1a1.5 1.5 0 013 0v1m0 0V11m0-5.5a1.5 1.5 0 013 0v3m0 0V11"/></svg>
|
||||
hashtag: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"/></svg>
|
||||
heart: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/></svg>
|
||||
home: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/></svg>
|
||||
identification: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"/></svg>
|
||||
inbox: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/></svg>
|
||||
inbox-in: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293h3.172a1 1 0 00.707-.293l2.414-2.414a1 1 0 01.707-.293H20"/></svg>
|
||||
information-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
key: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
|
||||
library: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 14v3m4-3v3m4-3v3M3 21h18M3 10h18M3 7l9-4 9 4M4 10h16v11H4V10z"/></svg>
|
||||
light-bulb: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/></svg>
|
||||
lightning-bolt: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
|
||||
link: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>
|
||||
location-marker: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
|
||||
lock-closed: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
|
||||
lock-open: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"/></svg>
|
||||
login: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"/></svg>
|
||||
logout: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
|
||||
mail: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
|
||||
mail-open: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 19v-8.93a2 2 0 01.89-1.664l7-4.666a2 2 0 012.22 0l7 4.666A2 2 0 0121 10.07V19M3 19a2 2 0 002 2h14a2 2 0 002-2M3 19l6.75-4.5M21 19l-6.75-4.5M3 10l6.75 4.5M21 10l-6.75 4.5m0 0l-1.14.76a2 2 0 01-2.22 0l-1.14-.76"/></svg>
|
||||
map: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
|
||||
menu: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
menu-alt-1: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h8m-8 6h16"/></svg>
|
||||
menu-alt-2: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h7"/></svg>
|
||||
menu-alt-3: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16m-7 6h7"/></svg>
|
||||
menu-alt-4: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/></svg>
|
||||
microphone: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"/></svg>
|
||||
minus: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20 12H4"/></svg>
|
||||
minus-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
minus-sm: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M18 12H6"/></svg>
|
||||
moon: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
||||
music-note: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"/></svg>
|
||||
newspaper: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/></svg>
|
||||
office-building: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/></svg>
|
||||
paper-airplane: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/></svg>
|
||||
paper-clip: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"/></svg>
|
||||
pause: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
pencil: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
|
||||
pencil-alt: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||
phone: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/></svg>
|
||||
phone-incoming: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 3l-6 6m0 0V4m0 5h5M5 3a2 2 0 00-2 2v1c0 8.284 6.716 15 15 15h1a2 2 0 002-2v-3.28a1 1 0 00-.684-.948l-4.493-1.498a1 1 0 00-1.21.502l-1.13 2.257a11.042 11.042 0 01-5.516-5.517l2.257-1.128a1 1 0 00.502-1.21L9.228 3.683A1 1 0 008.279 3H5z"/></svg>
|
||||
phone-missed-call: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 8l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M5 3a2 2 0 00-2 2v1c0 8.284 6.716 15 15 15h1a2 2 0 002-2v-3.28a1 1 0 00-.684-.948l-4.493-1.498a1 1 0 00-1.21.502l-1.13 2.257a11.042 11.042 0 01-5.516-5.517l2.257-1.128a1 1 0 00.502-1.21L9.228 3.683A1 1 0 008.279 3H5z"/></svg>
|
||||
phone-outgoing: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 3h5m0 0v5m0-5l-6 6M5 3a2 2 0 00-2 2v1c0 8.284 6.716 15 15 15h1a2 2 0 002-2v-3.28a1 1 0 00-.684-.948l-4.493-1.498a1 1 0 00-1.21.502l-1.13 2.257a11.042 11.042 0 01-5.516-5.517l2.257-1.128a1 1 0 00.502-1.21L9.228 3.683A1 1 0 008.279 3H5z"/></svg>
|
||||
photograph: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
play: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
plus: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/></svg>
|
||||
plus-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
plus-sm: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/></svg>
|
||||
presentation-chart-bar: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 13v-1m4 1v-3m4 3V8M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z"/></svg>
|
||||
presentation-chart-line: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z"/></svg>
|
||||
printer: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/></svg>
|
||||
puzzle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z"/></svg>
|
||||
qrcode: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"/></svg>
|
||||
question-mark-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
receipt-refund: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 15v-1a4 4 0 00-4-4H8m0 0l3 3m-3-3l3-3m9 14V5a2 2 0 00-2-2H6a2 2 0 00-2 2v16l4-2 4 2 4-2 4 2z"/></svg>
|
||||
receipt-tax: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2zM10 8.5a.5.5 0 11-1 0 .5.5 0 011 0zm5 5a.5.5 0 11-1 0 .5.5 0 011 0z"/></svg>
|
||||
refresh: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
|
||||
reply: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/></svg>
|
||||
rewind: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0019 16V8a1 1 0 00-1.6-.8l-5.333 4zM4.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0011 16V8a1 1 0 00-1.6-.8l-5.334 4z"/></svg>
|
||||
rss: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z"/></svg>
|
||||
save: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
|
||||
save-as: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 16v2a2 2 0 01-2 2H5a2 2 0 01-2-2v-7a2 2 0 012-2h2m3-4H9a2 2 0 00-2 2v7a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-1m-1 4l-3 3m0 0l-3-3m3 3V3"/></svg>
|
||||
scale: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3"/></svg>
|
||||
scissors: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z"/></svg>
|
||||
search: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
|
||||
search-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 16l2.879-2.879m0 0a3 3 0 104.243-4.242 3 3 0 00-4.243 4.242zM21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
selector: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 9l4-4 4 4m0 6l-4 4-4-4"/></svg>
|
||||
server: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/></svg>
|
||||
share: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"/></svg>
|
||||
shield-check: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>
|
||||
shield-exclamation: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M20.618 5.984A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016zM12 9v2m0 4h.01"/></svg>
|
||||
shopping-bag: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"/></svg>
|
||||
shopping-cart: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/></svg>
|
||||
sort-ascending: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"/></svg>
|
||||
sort-descending: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4"/></svg>
|
||||
sparkles: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"/></svg>
|
||||
speakerphone: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"/></svg>
|
||||
star: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/></svg>
|
||||
status-offline: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414"/></svg>
|
||||
status-online: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5.636 18.364a9 9 0 010-12.728m12.728 0a9 9 0 010 12.728m-9.9-2.829a5 5 0 010-7.07m7.072 0a5 5 0 010 7.07M13 12a1 1 0 11-2 0 1 1 0 012 0z"/></svg>
|
||||
stop: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"/></svg>
|
||||
sun: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
support: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
switch-horizontal: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>
|
||||
switch-vertical: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/></svg>
|
||||
table: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
|
||||
tag: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/></svg>
|
||||
template: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/></svg>
|
||||
terminal: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
||||
thumb-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.736 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.096c.5 0 .905-.405.905-.904 0-.715.211-1.413.608-2.008L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5"/></svg>
|
||||
thumb-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5"/></svg>
|
||||
ticket: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 5v2m0 4v2m0 4v2M5 5a2 2 0 00-2 2v3a2 2 0 110 4v3a2 2 0 002 2h14a2 2 0 002-2v-3a2 2 0 110-4V7a2 2 0 00-2-2H5z"/></svg>
|
||||
translate: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"/></svg>
|
||||
trash: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
|
||||
trending-down: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"/></svg>
|
||||
trending-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"/></svg>
|
||||
truck: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0"/></svg>
|
||||
upload: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></svg>
|
||||
user: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
|
||||
user-add: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"/></svg>
|
||||
user-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
user-group: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/></svg>
|
||||
user-remove: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7a4 4 0 11-8 0 4 4 0 018 0zM9 14a6 6 0 00-6 6v1h12v-1a6 6 0 00-6-6zM21 12h-6"/></svg>
|
||||
users: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
||||
variable: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4.871 4A17.926 17.926 0 003 12c0 2.874.673 5.59 1.871 8m14.13 0a17.926 17.926 0 001.87-8c0-2.874-.673-5.59-1.87-8M9 9h1.246a1 1 0 01.961.725l1.586 5.55a1 1 0 00.961.725H15m1-7h-.08a2 2 0 00-1.519.698L9.6 15.302A2 2 0 018.08 16H8"/></svg>
|
||||
video-camera: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
|
||||
view-boards: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"/></svg>
|
||||
view-grid: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/></svg>
|
||||
view-grid-add: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z"/></svg>
|
||||
view-list: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 10h16M4 14h16M4 18h16"/></svg>
|
||||
volume-off: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" clip-rule="evenodd"/><path stroke-linecap="round" stroke-linejoin="round" d="M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2"/></svg>
|
||||
volume-up: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"/></svg>
|
||||
wifi: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"/></svg>
|
||||
x: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
x-circle: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
zoom-in: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7"/></svg>
|
||||
zoom-out: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7"/></svg>
|
||||
|
||||
# Socials
|
||||
instagram: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M12.91 12.909c.326-.327.582-.72.749-1.151c.16-.414.27-.886.302-1.578c.032-.693.04-.915.04-2.68c0-1.765-.008-1.987-.04-2.68c-.032-.692-.142-1.164-.302-1.578a3.185 3.185 0 0 0-.75-1.151a3.187 3.187 0 0 0-1.151-.75c-.414-.16-.886-.27-1.578-.302C9.487 1.007 9.265 1 7.5 1c-1.765 0-1.987.007-2.68.04c-.692.03-1.164.14-1.578.301a3.2 3.2 0 0 0-1.151.75a3.2 3.2 0 0 0-.75 1.151c-.16.414-.27.886-.302 1.578C1.007 5.513 1 5.735 1 7.5c0 1.765.007 1.987.04 2.68c.03.692.14 1.164.301 1.578c.164.434.42.826.75 1.151c.325.33.718.586 1.151.75c.414.16.886.27 1.578.302c.693.031.915.039 2.68.039c1.765 0 1.987-.008 2.68-.04c.692-.03 1.164-.14 1.578-.301a3.323 3.323 0 0 0 1.151-.75ZM2 6.735v1.53c-.002.821-.002 1.034.02 1.5c.026.586.058 1.016.156 1.34c.094.312.199.63.543 1.012c.344.383.675.556 1.097.684c.423.127.954.154 1.415.175c.522.024.73.024 1.826.024H8.24c.842.001 1.054.002 1.526-.02c.585-.027 1.015-.059 1.34-.156c.311-.094.629-.2 1.011-.543c.383-.344.556-.676.684-1.098c.127-.422.155-.953.176-1.414C13 9.247 13 9.04 13 7.947v-.89c0-1.096 0-1.303-.023-1.826c-.021-.461-.049-.992-.176-1.414c-.127-.423-.3-.754-.684-1.098c-.383-.344-.7-.449-1.011-.543c-.325-.097-.755-.13-1.34-.156A27.29 27.29 0 0 0 8.24 2H7.057c-1.096 0-1.304 0-1.826.023c-.461.021-.992.049-1.415.176c-.422.128-.753.301-1.097.684c-.344.383-.45.7-.543 1.012c-.098.324-.13.754-.156 1.34c-.022.466-.022.679-.02 1.5ZM7.5 5.25a2.25 2.25 0 1 0 0 4.5a2.25 2.25 0 0 0 0-4.5ZM4.25 7.5a3.25 3.25 0 1 1 6.5 0a3.25 3.25 0 0 1-6.5 0Zm6.72-2.72a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5Z" clip-rule="evenodd"/></svg>
|
||||
facebook: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M22 12c0-5.52-4.48-10-10-10S2 6.48 2 12c0 4.84 3.44 8.87 8 9.8V15H8v-3h2V9.5C10 7.57 11.57 6 13.5 6H16v3h-2c-.55 0-1 .45-1 1v2h3v3h-3v6.95c5.05-.5 9-4.76 9-9.95z"/></svg>
|
||||
discord: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"></path></svg>
|
||||
twitter: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"></path></svg>
|
||||
mastodon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"></path></svg>
|
||||
youtube: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104l.022.26l.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105l-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006l-.087-.004l-.171-.007l-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103l.003-.052l.008-.104l.022-.26l.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007l.172-.006l.086-.003l.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408L6.4 5.209z"/></svg>
|
||||
x-twitter: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
|
||||
linkedin: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||
slack: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52a2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52zm0 1.271a2.528 2.528 0 0 1 2.521 2.521a2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521zm10.122 2.521a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522zm-1.268 0a2.528 2.528 0 0 1-2.523 2.521a2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522zm-2.523 10.122a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522zm0-1.268a2.527 2.527 0 0 1-2.52-2.523a2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523z" /></svg>
|
||||
bluesky: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 530"><path fill="currentColor" d="M136 44c66 50 138 151 164 205 26-54 98-155 164-205 48-36 126-64 126 25 0 18-10 149-16 170-21 74-96 93-163 81 117 20 147 86 82 153-122 125-176-32-189-72-3-8-4-11-4-8 0-3-1 0-4 8-13 40-67 197-189 72-65-67-35-133 82-153-67 12-142-7-163-81-6-21-16-152-16-170 0-89 78-61 126-25z"/></svg>
|
||||
telegram: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111.033 8 0 119.033 0 256s111.033 248 248 248 248-111.033 248-248S384.967 8 248 8m114.952 168.66c-3.732 39.215-19.881 134.378-28.1 178.3-3.476 18.584-10.322 24.816-16.948 25.425-14.4 1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25 5.342-39.5 3.652-3.793 67.107-61.51 68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608 69.142-14.845 10.194-26.894 9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7 18.45-13.7 108.446-47.248 144.628-62.3c68.872-28.647 83.183-33.623 92.511-33.789 2.052-.034 6.639.474 9.61 2.885a10.45 10.45 0 0 1 3.53 6.716 43.8 43.8 0 0 1 .417 9.769"/></svg>
|
||||
|
||||
# ai
|
||||
claude: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 257"><path fill="currentColor" d="m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z"/></svg>
|
||||
chatgpt: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 260"><path fill="currentColor" d="M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z"/></svg>
|
||||
gemini: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192"><path fill="currentColor" d="M164.93 86.68c-13.56-5.84-25.42-13.84-35.6-24.01-10.17-10.17-18.18-22.04-24.01-35.6-2.23-5.19-4.04-10.54-5.42-16.02C99.45 9.26 97.85 8 96 8s-3.45 1.26-3.9 3.05c-1.38 5.48-3.18 10.81-5.42 16.02-5.84 13.56-13.84 25.43-24.01 35.6-10.17 10.16-22.04 18.17-35.6 24.01-5.19 2.23-10.54 4.04-16.02 5.42C9.26 92.55 8 94.15 8 96s1.26 3.45 3.05 3.9c5.48 1.38 10.81 3.18 16.02 5.42 13.56 5.84 25.42 13.84 35.6 24.01 10.17 10.17 18.18 22.04 24.01 35.6 2.24 5.2 4.04 10.54 5.42 16.02A4.03 4.03 0 0 0 96 184c1.85 0 3.45-1.26 3.9-3.05 1.38-5.48 3.18-10.81 5.42-16.02 5.84-13.56 13.84-25.42 24.01-35.6 10.17-10.17 22.04-18.18 35.6-24.01 5.2-2.24 10.54-4.04 16.02-5.42A4.03 4.03 0 0 0 184 96c0-1.85-1.26-3.45-3.05-3.9-5.48-1.38-10.81-3.18-16.02-5.42"/></svg>
|
||||
@@ -0,0 +1,35 @@
|
||||
# Theme development config for documentation site
|
||||
# https://gohugo.io/configuration/build/#cache-busters
|
||||
[build]
|
||||
[build.buildStats]
|
||||
enable = true
|
||||
disableIDs = true
|
||||
[[build.cachebusters]]
|
||||
source = 'assets/notwatching/hugo_stats\.json'
|
||||
target = 'styles\.css'
|
||||
[[build.cachebusters]]
|
||||
source = '(postcss|tailwind)\.config\.mjs'
|
||||
target = 'css'
|
||||
[[build.cachebusters]]
|
||||
source = 'assets/.*\.(js|ts|jsx|tsx)'
|
||||
target = 'js'
|
||||
[[build.cachebusters]]
|
||||
source = 'assets/.*\.(.*)$'
|
||||
target = '$1'
|
||||
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source = "assets"
|
||||
target = "assets"
|
||||
[[module.mounts]]
|
||||
source = "hugo_stats.json"
|
||||
target = "assets/notwatching/hugo_stats.json"
|
||||
disableWatch = true
|
||||
|
||||
# Theme dev mode runs PostCSS from docs/, while Tailwind v4 loads native
|
||||
# dependencies from the theme root. Disable Hugo's Node permission sandbox
|
||||
# for this dev-only pipeline so the CSS is compiled instead of served raw.
|
||||
[security]
|
||||
[security.node]
|
||||
[security.node.permissions]
|
||||
disable = true
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: تم هگزترا
|
||||
layout: hextra-home
|
||||
---
|
||||
|
||||
{{< hextra/hero-badge >}}
|
||||
<div class="hx:w-2 hx:h-2 hx:rounded-full hx:bg-primary-400"></div>
|
||||
<span>آزاد، متنباز</span>
|
||||
{{< icon name="arrow-circle-left" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
|
||||
<div class="hx:mt-6 hx:mb-6">
|
||||
{{< hextra/hero-headline >}}
|
||||
ساخت وبسایتهای مدرن <br class="hx:sm:block hx:hidden" />با مارکداون و هیوگو
|
||||
{{< /hextra/hero-headline >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-12">
|
||||
{{< hextra/hero-subtitle >}}
|
||||
تم هیوگو سریع و دارای امکانات کامل <br class="hx:sm:block hx:hidden" />برای ایجاد وبسایتهای استاتیک زیبا
|
||||
{{< /hextra/hero-subtitle >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-6">
|
||||
{{< hextra/hero-button text="شروع کنید" link="docs" >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mt-6"></div>
|
||||
|
||||
{{< hextra/feature-grid >}}
|
||||
{{< hextra/feature-card
|
||||
title="سریع و با امکانات کامل"
|
||||
subtitle="ساده و آسان برای استفاده، در عین حال قدرتمند و غنی از ویژگیها متنوع."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-doc.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[24px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="مارکداون تنها چیزی است که شما نیاز دارید"
|
||||
subtitle="فقط با مارکداون بنویسید. تکمیل و کامل با کامپوننتهای کد کوتاه."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-lg:min-h-[340px]"
|
||||
image="/images/hextra-markdown.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(142,53,74,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="جستجوی کامل متن"
|
||||
subtitle="جستجوی متن کامل داخلی با FlexSearch، بدون نیاز به نصب موارد اضافی."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-search.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[110%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(221,210,59,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="سبک مانند یک پر"
|
||||
subtitle="برای استفاده از هگزترا به هیچ وابستگی یا Node.js نیاز نیست. با پشتیبانی از هیوگو، یکی از سریعترین تولیدکنندگان سایت استاتیک، سایت شما را تنها در چند ثانیه با یک باینری میسازد."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title=" واکنشگرا با حالت تیره"
|
||||
subtitle="در اندازههای مختلف صفحه نمایش عالی به نظر میرسد. پشتیبانی از حالت تیره داخلی، با تغییر خودکار براساس اولویت سیستم کاربر."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="ساخت و میزبانی رایگان"
|
||||
subtitle="با گیتهاب Actions بسازید و به صورت رایگان در گیتهاب Pages میزبانی کنید. یا میتوانید آن را در هر سرویس میزبانی استاتیک میزبانی کنید."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="چند زبانه آسان"
|
||||
subtitle="فقط با افزودن پسوند محلی به پرونده مارکداون صفحات وبسایت چند زبانه ایجاد کنید. افزودن پشتیبانی i18n به سایت شما بصری است."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="و خیلی بیشتر..."
|
||||
icon="sparkles"
|
||||
subtitle="برجستهکردن سینتکس / فهرست مطالب / سئو / RSS / LaTeX / Mermaid / سفارشیسازی / و موارد دیگر…"
|
||||
>}}
|
||||
{{< /hextra/feature-grid >}}
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Hextra テーマ
|
||||
layout: hextra-home
|
||||
---
|
||||
|
||||
{{< hextra/hero-badge >}}
|
||||
<div class="hx:w-2 hx:h-2 hx:rounded-full hx:bg-primary-400"></div>
|
||||
<span>無料、オープンソース</span>
|
||||
{{< icon name="arrow-circle-right" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
|
||||
<div class="hx:mt-6 hx:mb-6">
|
||||
{{< hextra/hero-headline >}}
|
||||
MarkdownとHugoで <br class="hx:sm:block hx:hidden" />モダンなウェブサイトを構築
|
||||
{{< /hextra/hero-headline >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-12">
|
||||
{{< hextra/hero-subtitle >}}
|
||||
美しい静的ウェブサイトを作るための <br class="hx:sm:block hx:hidden" />高速でバッテリー同梱型のHugoテーマ
|
||||
{{< /hextra/hero-subtitle >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-6">
|
||||
{{< hextra/hero-button text="始める" link="docs" >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mt-6"></div>
|
||||
|
||||
{{< hextra/feature-grid >}}
|
||||
{{< hextra/feature-card
|
||||
title="高速かつ多機能"
|
||||
subtitle="シンプルで使いやすく、それでいて強力で豊富な機能を備えています。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-doc.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[24px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="MarkdownだけでOK"
|
||||
subtitle="Markdownだけで作成可能。ショートコードコンポーネントで充実させることもできます。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-lg:min-h-[340px]"
|
||||
image="/images/hextra-markdown.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(142,53,74,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="全文検索"
|
||||
subtitle="FlexSearchによる全文検索が内蔵されており、追加の設定は不要です。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-search.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[110%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(221,210,59,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="羽のように軽量"
|
||||
subtitle="Hextraを使用するために依存関係やNode.jsは必要ありません。Hugoによって動力を得ており、単一のバイナリで数秒でサイトを構築できます。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="レスポンシブ対応とダークモード"
|
||||
subtitle="さまざまな画面サイズで美しく見えます。内蔵のダークモードサポートにより、ユーザーのシステム設定に基づいて自動切り替えが可能です。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="無料で構築とホスティング"
|
||||
subtitle="GitHub Actionsを使って構築し、GitHub Pagesで無料でホスティングできます。また、他の静的ホスティングサービスでもホスティング可能です。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="簡単な多言語対応"
|
||||
subtitle="Markdownファイルにロケールサフィックスを追加するだけで多言語ページを作成できます。i18nサポートの追加も直感的です。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="さらに多くの機能"
|
||||
icon="sparkles"
|
||||
subtitle="構文ハイライト / 目次 / SEO / RSS / LaTeX / Mermaid / カスタマイズ可能 / など多数..."
|
||||
>}}
|
||||
{{< /hextra/feature-grid >}}
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Hextra Theme
|
||||
layout: hextra-home
|
||||
---
|
||||
|
||||
{{< hextra/hero-badge >}}
|
||||
<div class="hx:w-2 hx:h-2 hx:rounded-full hx:bg-primary-400"></div>
|
||||
<span>Free, open source</span>
|
||||
{{< icon name="arrow-circle-right" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
|
||||
<div class="hx:mt-6 hx:mb-6">
|
||||
{{< hextra/hero-headline >}}
|
||||
Build modern websites <br class="hx:sm:block hx:hidden" />with Markdown and Hugo
|
||||
{{< /hextra/hero-headline >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-12">
|
||||
{{< hextra/hero-subtitle >}}
|
||||
Fast, batteries-included Hugo theme <br class="hx:sm:block hx:hidden" />for creating beautiful static websites
|
||||
{{< /hextra/hero-subtitle >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-6">
|
||||
{{< hextra/hero-button text="Get Started" link="docs" >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mt-6"></div>
|
||||
|
||||
{{< hextra/feature-grid >}}
|
||||
{{< hextra/feature-card
|
||||
title="Fast and Full-featured"
|
||||
subtitle="Simple and easy to use, yet powerful and feature-rich."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="images/hextra-doc.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[24px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Markdown is All You Need"
|
||||
subtitle="Compose with just Markdown. Enrich with Shortcode components."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-lg:min-h-[340px]"
|
||||
image="images/hextra-markdown.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(142,53,74,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Full Text Search"
|
||||
subtitle="Built-in full text search with FlexSearch, no extra setup required."
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="images/hextra-search.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[110%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(221,210,59,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Lightweight as a Feather"
|
||||
subtitle="No dependency or Node.js is needed to use Hextra. Powered by Hugo, one of *the fastest* static site generators, building your site in just seconds with a single binary."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Responsive with Dark Mode Included"
|
||||
subtitle="Looks great on different screen sizes. Built-in dark mode support, with auto-switching based on user's system preference."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Build and Host for Free"
|
||||
subtitle="Build with GitHub Actions, and host for free on GitHub Pages. Alternatively it can be hosted on any static hosting service."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Multi-Language Made Easy"
|
||||
subtitle="Create multi-language pages by just adding locales suffix to the Markdown file. Adding i18n support to your site is intuitive."
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="And Much More..."
|
||||
icon="sparkles"
|
||||
subtitle="Syntax highlighting / Table of contents / SEO / RSS / LaTeX / Mermaid / Customizable / and more..."
|
||||
>}}
|
||||
{{< /hextra/feature-grid >}}
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Hextra 主题
|
||||
layout: hextra-home
|
||||
---
|
||||
|
||||
{{< hextra/hero-badge >}}
|
||||
<div class="hx:w-2 hx:h-2 hx:rounded-full hx:bg-primary-400"></div>
|
||||
<span>免费 开源</span>
|
||||
{{< icon name="arrow-circle-right" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
|
||||
<div class="hx:mt-6 hx:mb-6">
|
||||
{{< hextra/hero-headline >}}
|
||||
创建现代化网站 <br class="hx:sm:block hx:hidden" />由 Markdown 和 Hugo 驱动
|
||||
{{< /hextra/hero-headline >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-12">
|
||||
{{< hextra/hero-subtitle >}}
|
||||
极速且全能的 Hugo 主题框架 <br class="hx:sm:block hx:hidden" />为构建现代化的静态网站而生
|
||||
{{< /hextra/hero-subtitle >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mb-6">
|
||||
{{< hextra/hero-button text="现在开始" link="docs" >}}
|
||||
</div>
|
||||
|
||||
<div class="hx:mt-6"></div>
|
||||
|
||||
{{< hextra/feature-grid >}}
|
||||
{{< hextra/feature-card
|
||||
title="快速且功能全面"
|
||||
subtitle="简单易用,功能强大丰富。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-doc.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[24px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="Markdown 写作"
|
||||
subtitle="只需使用 Markdown 进行编辑。多样的 Shortcode 组件开箱即用。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-lg:min-h-[340px]"
|
||||
image="/images/hextra-markdown.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(142,53,74,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="全文搜索"
|
||||
subtitle="内置 FlexSearch 全文搜索,无需额外设置。"
|
||||
class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
|
||||
image="/images/hextra-search.webp"
|
||||
imageClass="hx:top-[40%] hx:left-[36px] hx:w-[110%] hx:sm:w-[110%] hx:dark:opacity-80"
|
||||
style="background: radial-gradient(ellipse at 50% 80%,rgba(221,210,59,0.15),hsla(0,0%,100%,0));"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="轻如羽毛"
|
||||
subtitle="使用 Hextra 无需依赖 Node.js。由 Hugo 提供支持,Hugo 是最快的静态网站生成器之一,只需一个二进制文件即可在数秒内创建网站。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="响应式布局,暗黑模式"
|
||||
subtitle="适应不同的屏幕尺寸。内置暗黑模式支持,并根据用户的系统偏好自动切换。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="免费构建和托管"
|
||||
subtitle="使用 GitHub Actions 进行构建,并在 GitHub Pages 上免费托管。也可以托管在任何静态托管服务上。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="多语言轻松实现"
|
||||
subtitle="仅需通过在 Markdown 文件后添加语言代码即可创建多语言页面。向您的站点添加 i18n 支持直观易行。"
|
||||
>}}
|
||||
{{< hextra/feature-card
|
||||
title="还有更多..."
|
||||
icon="sparkles"
|
||||
subtitle="代码高亮 / 目录 / SEO / RSS / LaTeX 公式 / Mermaid 图标 / 自定义 / 等等..."
|
||||
>}}
|
||||
{{< /hextra/feature-grid >}}
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: درباره ما
|
||||
toc: false
|
||||
---
|
||||
|
||||
هگزترا به گونهای طراحی شده است که یک موضوع ساده، سریع و انعطاف پذیر برای ساخت وبسایتهای استاتیک مدرن باشد. این به ویژه برای وبسایتهای مستندسازی مناسب است اما میتواند برای انواع مختلف سایتها مانند وبلاگها، نمونهکار و موارد دیگر نیز استفاده شود.
|
||||
|
||||
Hugo مانند Jekyll، یک ایجادکننده سایت استاتیک است. چیزی که Hugo را متمایز میکند این است که یک باینری واحد است و نصب و اجرای آن بر روی پلتفرمهای مختلف را آسان میکند. همچنین بسیار سریع و قابل اعتماد است و میتواند یک سایت را با هزاران صفحه در میلیثانیه ارائه دهد.
|
||||
|
||||
هگزترا با ذهنیتی ساخته شده است که بر داشتن حداقل ردپا متمرکز شده است. برای شروع، هیچ وابستگی اضافی مانند بستههای Node.js لازم نیست. تنها چیزی که نیاز دارید یک پرونده پیکربندی YAML به همراه محتوای مارکداون شما است. بنابراین، شما میتوانید به جای تنظیم ابزار، روی نوشتن محتوای با کیفیت تمرکز کنید.
|
||||
|
||||
## اعتبار
|
||||
|
||||
ترجمه فارسی مستندات توسط [گودرز جعفری](https://goudarzjafari.com/) انجام شده است.
|
||||
|
||||
هگزترا بدون ابزار و الهامات زیر ساخته نمیشود:
|
||||
|
||||
- [هیوگو](https://gohugo.io/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Heroicons](https://heroicons.com/)
|
||||
- [Nextra](https://nextra.vercel.app/)
|
||||
- [Next.js](https://nextjs.org/)
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: このサイトについて
|
||||
toc: false
|
||||
---
|
||||
|
||||
Hextra は、モダンな静的サイトを構築するためのシンプルで高速かつ柔軟なテーマとして設計されています。特にドキュメントサイトの構築に適していますが、ブログやポートフォリオなど様々な種類のサイトにも利用可能です。
|
||||
|
||||
Hugo は Jekyll と同様に静的サイトジェネレータですが、単一のバイナリで構成されている点が特徴です。これにより様々なプラットフォームでのインストールと実行が容易で、極めて高速かつ信頼性が高く、数千ページあるサイトでもミリ秒単位でレンダリング可能です。
|
||||
|
||||
Hextra は最小限のフットプリントに焦点を当てた思想で構築されています。開始するにあたり、Node.js パッケージなどの追加依存関係は不要で、必要なのは単一の YAML 設定ファイルと Markdown コンテンツのみです。これにより、ツールのセットアップではなく質の高いコンテンツの作成に集中できます。
|
||||
|
||||
## クレジット
|
||||
|
||||
Hextra は以下のツールとインスピレーションなしには成り立ちません:
|
||||
|
||||
- [Hugo](https://gohugo.io/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Heroicons](https://heroicons.com/)
|
||||
- [Nextra](https://nextra.vercel.app/)
|
||||
- [Next.js](https://nextjs.org/)
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: About
|
||||
toc: false
|
||||
---
|
||||
|
||||
Hextra is designed to be a simple, fast, and flexible theme for building modern static websites. It is especially well-suited for documentation websites but can also be used for various types of sites, such as blogs, portfolios, and more.
|
||||
|
||||
Hugo, like Jekyll, is a static site generator. What sets Hugo apart is that it is a single binary, making it easy to install and run on various platforms. It is also extremely fast and reliable, capable of rendering a site with thousands of pages in milliseconds.
|
||||
|
||||
Hextra is built with a mindset focused on having a minimal footprint. To get started, no extra dependencies like Node.js packages are required; all you need is a single YAML configuration file, along with your Markdown content. Thus, we can focus on writing quality content instead of setting up tooling.
|
||||
|
||||
## Credits
|
||||
|
||||
Hextra cannot be built without the following tools and inspirations:
|
||||
|
||||
- [Hugo](https://gohugo.io/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Heroicons](https://heroicons.com/)
|
||||
- [Nextra](https://nextra.vercel.app/)
|
||||
- [Next.js](https://nextjs.org/)
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: 关于
|
||||
toc: false
|
||||
---
|
||||
|
||||
Hextra 是一款专为构建现代化静态网站而设计的简洁、快速且灵活的主题。它尤其适合搭建文档类网站,同时也能轻松驾驭博客、作品集等多种站点类型。
|
||||
|
||||
与 Jekyll 类似,Hugo 同样是一款静态网站生成器。其独特之处在于采用单一二进制文件,可在多平台轻松安装运行。Hugo 以极致的速度与稳定性著称,能在毫秒间渲染包含数千页面的网站。
|
||||
|
||||
Hextra 秉持极简理念开发。您无需安装 Node.js 等额外依赖,仅需一个 YAML 配置文件搭配 Markdown 内容即可快速开始。这让我们能专注于创作优质内容,而非配置工具链。
|
||||
|
||||
## 致谢
|
||||
|
||||
Hextra 的诞生离不开以下工具与灵感的启发:
|
||||
|
||||
- [Hugo](https://gohugo.io/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Heroicons](https://heroicons.com/)
|
||||
- [Nextra](https://nextra.vercel.app/)
|
||||
- [Next.js](https://nextjs.org/)
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: آرشیو
|
||||
layout: archives
|
||||
toc: false
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: アーカイブ
|
||||
layout: archives
|
||||
toc: false
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Archives
|
||||
layout: archives
|
||||
toc: false
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 归档
|
||||
layout: archives
|
||||
toc: false
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "وبلاگ"
|
||||
---
|
||||
|
||||
<div style="text-align: center; margin-top: 1em;">
|
||||
{{< hextra/hero-badge link="index.xml" >}}
|
||||
<span>فید RSS</span>
|
||||
{{< icon name="rss" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "ブログ"
|
||||
---
|
||||
|
||||
<div style="text-align: center; margin-top: 1em;">
|
||||
{{< hextra/hero-badge link="index.xml" >}}
|
||||
<span>RSS フィード</span>
|
||||
{{< icon name="rss" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "Blog"
|
||||
---
|
||||
|
||||
<div style="text-align: center; margin-top: 1em;">
|
||||
{{< hextra/hero-badge link="index.xml" >}}
|
||||
<span>RSS Feed</span>
|
||||
{{< icon name="rss" attributes="height=14" >}}
|
||||
{{< /hextra/hero-badge >}}
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user